[SOLUTION] One-Liners (#113)

P

Phrogz

Here are my solutions to Quiz #113. For some of them I just couldn't
help but to provide a couple variations.

Because I'm a terrible golfer, most strive for elegance (in some form)
over terseness.


# 1 - Commafy Numerics
i,f=quiz.to_s.split('.'); i.gsub(/(\d)(?=\d{3}+$)/,'\\1,') + (f ?
('.'+f) : '')



# 2 - Flatten_Once
a=[]; quiz.each{ |x| Array===x ? a.concat(x) : a<<x }; a



# 3 - Shuffle Array
quiz.sort_by{ rand }



# 4 - Resolve class (and other constants) from string
quiz.split( '::' ).inject( Module ){ |r,o| r.const_get(o) }
#...or, by cheating
eval(quiz)



#5 - Paragraph Wrapping - extra work to not put a new line on the last
line
quiz.gsub( /^(.{1,40})($|[ \t]+)/ ){ $2.empty? ? $1 : "#{$1}\n" }



#6 - Anagrams - assuming that the original word shouldn't be in the
output...
a=[]; r=quiz.shift.split('').sort; quiz.each{|w|a<<w if
w.split('').sort==r}; a
#...or, if the original word should be included
a=[]; r=quiz[0].split('').sort; quiz.each{ |w| a<<w if
w.split('').sort==r }; a



#7 - String to Binary String, the geeky way
o=''; quiz.each_byte{|b| o << ( b==32 ? "\n" : ('%b' % b) ) }; o
#...or slightly more 'rubyish'...
quiz.split(' ').map{|s| o=''; s.each_byte{|b| o << b.to_s(2) };
o }.join("\n")
#...but what's more rubyish than nested #maps and pulling bytes from
strings? ;)
quiz.split(' ').map{|s| s.scan(/./).map{|c| '%b' %
c[0] }.join }.join("\n")

# By the way, I have to say that if the Think Geek t-shirts are really
in the
# form provided, they are terrible #representations of geekiness. What
geek
# would strip the leading zeros from the bits in a byte? I'd replace
"%b" with
# "%08b" above for a better representation (and use it instead of
to_s(2)).



#8 - Random line from file - if you run out of memory, go buy more
RAM ;)
all=quiz.readlines; all[ rand(all.length) ]



#9 - Wondrous number path
a=[n=quiz]; while n>1; a << ( n%2==0 ? n/=2 : n=(n*3)+1 ); end; a



#10 - Array to Nested Hash, direct indexing...
a=quiz; h={a[-2]=>a[-1]}; (a.size-3).downto(0){ |i| h={a=>h} }; h
#...or a slightly different way...
a=quiz; y,z=a[-2..-1]; h={y=>z}; a[0..-3].reverse.each{ |o|
h={o=>h} }; h
#...or poppin' values for a tighter format...
a=quiz; z,y=a.pop,a.pop; h={y=>z}; a.reverse.each{ |o| h={o=>h} }; h
#...and one last, just because I love Hash.[]
a=quiz.reverse; h=Hash[a.shift,a.shift].invert; a.each{ |o|
h={o=>h} }; h
 
K

Ken Bloom

Here are my solutions to Quiz #113. For some of them I just couldn't
help but to provide a couple variations.

Because I'm a terrible golfer, most strive for elegance (in some form)
over terseness.


# 1 - Commafy Numerics
i,f=quiz.to_s.split('.'); i.gsub(/(\d)(?=\d{3}+$)/,'\\1,') + (f ?
('.'+f) : '')

Good answer. you helped me golf mine down a little bit by getting rid of
array indexing:

i,f=i.to_s.split('.');"#{i.reverse.scan(/.{1,3}/).join(',').reverse}.#{f}"
# 3 - Shuffle Array
quiz.sort_by{ rand }

I feel stupid for taking such a longer answer. And I've used sort_by{rand}
too to do this, if not in Ruby then in SQL, so I feel stupid for not
thinking of it in this context.]

mine, as I posted elsewhere was:
i.inject([]){|cur,val| cur.insert(rand(cur.length+1),val)}

#5 - Paragraph Wrapping - extra work to not put a new line on the last
line
quiz.gsub( /^(.{1,40})($|[ \t]+)/ ){ $2.empty? ? $1 : "#{$1}\n" }

I like this one a lot. My only answer was way too long, and far more
complicated.
 
K

Ken Bloom

Good answer. you helped me golf mine down a little bit by getting rid of
array indexing:

i,f=i.to_s.split('.');"#{i.reverse.scan(/.{1,3}/).join(',').reverse}.#{f}"

Another answer, based on yours, this one is exactly one expression (no
semicolons):
quiz.to_s.gsub(/(\d)(?=\d{3}+#{quiz.to_s=~/\./?/\./:/$/})/,'\\1,')
 
J

James Edward Gray II

Here are my solutions to Quiz #113.

When I built the quiz, I used the following solutions to reality-
check myself. (Making sure I could find a viable answer.)

#
# Given a Numeric, provide a String representation with commas
inserted between
# each set of three digits in front of the decimal. For example,
1999995.99
# should become "1,999,995.99".
#
quiz.to_s.reverse.gsub(/(\d\d\d)(?=\d)(?!\d*\.)/,"\\1,").reverse

#
# Given a nested Array of Arrays, perform a flatten()-like operation
that
# removes only the top level of nesting. For example, [1, [2, [3]]]
would
# become [1, 2, [3]].
#
quiz.inject(Array.new) { |arr, a| arr.push(*a) }

# Shuffle the contents of a provided Array.
quiz.sort_by { rand }

#
# Given a Ruby class name in String form (like
# "GhostWheel::Expression::LookAhead"), fetch the actual class object.
#
quiz.split("::").inject(Object) { |par, const| par.const_get(const) }

#
# Insert newlines into a paragraph of prose (provided in a String) so
lines will
# wrap at 40 characters.
#
quiz.gsub!(/(.{1,40}|\S{41,})(?: +|$\n?)/, "\\1\n")

#
# Given an Array of String words, build an Array of only those words
that are
# anagrams of the first word in the Array.
#
quiz.select { |w| w.split("").sort == quiz.first.split("").sort }

#
# Convert a ThinkGeek t-shirt slogan (in String form) into a binary
# representation (still a String). For example, the popular shirt
"you are
# dumb" is actually printed as:
#
# 111100111011111110101
# 110000111100101100101
# 1100100111010111011011100010
#
quiz.split("").map { |c| c == " " ? "\n" : c[0].to_s(2) }.join

# Provided with an open File object, select a random line of content.
quiz.inject { |choice, line| rand < 1/quiz.lineno.to_f ? line : choice }

#
# Given a wondrous number Integer, produce the sequence (in an
Array). A
# wondrous number is a number that eventually reaches one, if you
apply the
# following rules to build a sequence from it. If the current number
in the
# sequence is even, the next number is that number divided by two.
When the
# current number is odd, multiply that number by three and add one to
get the
# next number in the sequence. Therefore, if we start with the
wondrous number
# 15, the sequence is [15, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20,
10, 5, 16,
# 8, 4, 2, 1].
#
Hash.new { |h, n| n == 1 ? [1] : [n] + h[n % 2 == 0 ? n/2 : n*3+1] }
[quiz]

#
# Convert an Array of objects to nested Hashes such that %w[one two
three four
# five] becomes {"one" => {"two" => {"three" => {"four" => "five"}}}}.
#
quiz.reverse.inject { |res, wrap| {wrap => res} }

James Edward Gray II
 
P

Phrogz

quiz.to_s.reverse.gsub(/(\d\d\d)(?=\d)(?!\d*\.)/,"\\1,").reverse
quiz.inject(Array.new) { |arr, a| arr.push(*a) }
quiz.sort_by { rand }
quiz.split("::").inject(Object) { |par, const| par.const_get(const) }
quiz.gsub!(/(.{1,40}|\S{41,})(?: +|$\n?)/, "\\1\n")
quiz.select { |w| w.split("").sort == quiz.first.split("").sort }
quiz.split("").map { |c| c == " " ? "\n" : c[0].to_s(2) }.join
quiz.inject { |choice, line| rand < 1/quiz.lineno.to_f ? line : choice }
Hash.new{|h, n| n==1 ? [1] : [n] + h[n%2 == 0 ? n/2 : n*3+1] }[quiz]
quiz.reverse.inject { |res, wrap| {wrap => res} }

What I particularly admire about your solutions is that they are all
single- or chained-statements. None of the "a=[];...;a" nonsense that
I mucked about with. I wish I had been sure that it was possible to do
this for all solutions, so that I would have looked harder.

I can't tell if I think the reverse/regexp/reverse technique you (and
many others) used for the first problem is more or less elegant than a
single regexp on the integer portion. I suspect that mine is faster at
runtime...but speed is rarely an appropriate measure of elegance.

Kudos on the memoizing wondrous number, btw. :)
 
J

James Edward Gray II

What I particularly admire about your solutions is that they are all
single- or chained-statements.

Thank you.
I can't tell if I think the reverse/regexp/reverse technique you (and
many others) used for the first problem is more or less elegant than a
single regexp on the integer portion. I suspect that mine is faster at
runtime...but speed is rarely an appropriate measure of elegance.

I'm pretty sure I learned that reverse(), gsub(), and reverse() trick
from Perl's FAQ years ago. I just checked now though and the answer
is not what I recall, so maybe I am misremembering that.
Kudos on the memoizing wondrous number, btw. :)

It's not actually. I never assign the Hash value. ;)

James Edward Gray II
 
P

Phrogz

Hash.new{|h, n| n==1 ? [1] : [n] + h[n%2 == 0 ? n/2 : n*3+1] }[quiz]
Kudos on the memoizing wondrous number, btw. :)

It's not actually. I never assign the Hash value. ;)

Interesting point. (And, of course, it wouldn't be useful if it
memoized the result for a single call to the function.) I think it's
interesting because this pattern really allows you to (ab)use the
block form of Hash as a lambda that passes a reference to itself as
one of its arguments. Very convenient for one-liners.
 
J

James Edward Gray II

521/21 > cat bench.rb
require 'benchmark'

array = (1..100000).map { rand * (ARGV.first||1_000_000).to_f }

def phrogz quiz
i,f=quiz.to_s.split('.'); i.gsub(/(\d)(?=\d{3}+$)/,'\\1,') + (f ?
('.'+f) : '')
end
def james quiz
quiz.to_s.reverse.gsub(/(\d\d\d)(?=\d)(?!\d*\.)/,"\\1,").reverse
end


Benchmark.bmbm do |x|
x.report("Phrogz") {array.map{ |e| phrogz(e) }}
x.report("James") {array.map{ |e| james(e) }}
end

How does this code work? You pass arguments to methods we don't
see. The ones we do see don't even accept arguments.

The results look right though:

#!/usr/bin/env ruby -w

require "benchmark"

def phrogz(num)
i,f=num.to_s.split('.'); i.gsub(/(\d)(?=\d{3}+$)/,'\\1,') + (f ?
('.'+f) : '')
end

def james(num)
num.to_s.reverse.gsub(/(\d\d\d)(?=\d)(?!\d*\.)/,"\\1,").reverse
end

TESTS = Array.new(100_000) { rand(1_000_000) + 1.to_f / (rand(1_000)
+ 1) }
Benchmark.bmbm do |results|
results.report("Phrogz:") { TESTS.each { |n| phrogz(n) } }
results.report("James:") { TESTS.each { |n| james(n) } }
end
# >> Rehearsal -------------------------------------------
# >> Phrogz: 1.690000 0.010000 1.700000 ( 1.700985)
# >> James: 1.550000 0.010000 1.560000 ( 1.557882)
# >> ---------------------------------- total: 3.260000sec
# >>
# >> user system total real
# >> Phrogz: 1.690000 0.010000 1.700000 ( 1.703213)
# >> James: 1.520000 0.000000 1.520000 ( 1.528621)

__END__

James Edward Gray II
 
J

James Edward Gray II

Hash.new{|h, n| n==1 ? [1] : [n] + h[n%2 == 0 ? n/2 : n*3+1] }
[quiz]
Kudos on the memoizing wondrous number, btw. :)
It's not actually. I never assign the Hash value. ;)

So it's equivalent to:
(h=lambda {|n| n==1 ? [1] : [n] + h[n%2 == 0 ? n/2 : n*3+1] })[23]

Exactly.

James Edward Gray II


That really puts the spotlight on it, this will go away in 2.0
though if
I understand correctly :) and one would need to write:

(h=lambda {|n,lmb| n==1 ? [1] : [n] + h[n%2 == 0 ? n/2 : n*3
+1,lmb] })[23,h]


is that right?

Hmm, I wasn't aware of this. What makes you say that?

James Edward Gray II
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top