[QUIZ] One-Liners (#113)

Elegantly small!

···

On Feb 11, 4:40 pm, Alex Young <a...@blackkettle.org> wrote:

# One-level flatten().
   r=;quiz.each{|a|r+=[*a]};r

I don't think my solutions contribute much that is new, but you can
see them below and on pastie: http://pastie.caboo.se/39741.

I thought it was interesting that many previous solutions to the
commify problem posted on lists and on RubyForge fail for numbers like
0.23423423.

Looking at others' solutions, I really like Robert Dober's
sort_by{rand} solution to the array shuffle.

cheers,
Krishna

···

----------------------------------------------------------------
require 'test/unit'
# test setup mostly borrowed from Jamie Macey

class OneLiner
  class << self

    # this was the hardest one for me. this answer is not
    # entirely my own, as it was inspired by
    # http://rubyforge.org/snippet/detail.php?type=snippet&id=8
    # (which does not work for numbers like 0.234234234234)
    def commaize(quiz)
      quiz.to_s.sub(/^(-*)(\d+)/){|m| $1 + $2.gsub(/(\d)(?=\d{3}+$)/, '\1,')}
    end

    def flatten_once(quiz)
      quiz.inject([]){|n, e| e.is_a?(Array) ? n + e : n << e }
    end

    def shuffle(quiz)
      a = quiz.dup; Array.new(a.size).map{|i| a.delete_at(rand(a.size)) }
    end

    def get_class(quiz)
      require quiz.downcase.split("::")[0..-2].join("/"); eval quiz
    end

    def wrap_text(quiz)
      quiz.gsub(/(.{1,40}(\s|$))/, '\1' + "\n").chop
    end

    def find_anagrams(quiz)
      quiz.select{|w| w.scan(/./).sort == quiz[0].scan(/./).sort}
    end

    def binarize(quiz)
      s = ""; quiz.each_byte {|c| c == 32 ? s << "\n" : s << "%b" % c}; s
    end

    # using #readlines would be easiest, but unlike that, this solution
    # should work fine on files that are too big to hold in memory.
    # unfortunately, it is more than 80 chars when using a variable
    # named 'quiz'
    def random_line(quiz)
      i = rand(quiz.each{|l|}.lineno); quiz.rewind; quiz.each{|l|
return l if quiz.lineno == i+1}
    end

    # i know. it's 6 lines, not one. and more than 80 chars :frowning:
    def wondrous_sequence(quiz)
      a = [n = quiz]; while n != 1; n = (n % 2 > 0 ? n * 3 + 1 : n /
2); a << n; end; a
    end

    # i guess it is cheating to use recursion (two lines)
    # but it worked too nicely to resist here.
    def nested_hash(quiz)
      quiz.size > 1 ? {quiz[0] => nested_hash(quiz[1..-1])} : quiz[0]
    end
  end
end

require 'tempfile'
class TestOneLiner < Test::Unit::TestCase
# 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".
def test_commaize
   assert_equal "995", OneLiner.commaize(995)
   assert_equal "1,995", OneLiner.commaize(1995)
   assert_equal "12,995", OneLiner.commaize(12995)
   assert_equal "123,995", OneLiner.commaize(123995)
   assert_equal "1,234,995", OneLiner.commaize(1234995)
   assert_equal "1,234,567,890,995", OneLiner.commaize(1234567890995)
   assert_equal "99,995.992349834", OneLiner.commaize(99995.992349834)
   assert_equal "0.992349834", OneLiner.commaize(0.992349834)
   assert_equal "-0.992349834", OneLiner.commaize(-0.992349834)
   assert_equal "999,995.99", OneLiner.commaize(999995.99)
   assert_equal "-1,999,995.99", OneLiner.commaize(-1999995.99)
end

# 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]].
def test_flatten_once
   ary = [1, [2, [3, 4]]]
   flatter_ary = [1, 2, [3, 4]]
   assert_equal flatter_ary, OneLiner.flatten_once(ary)
end

# Shuffle the contents of a provided Array.
def test_shuffle
   ary = [3,1,4,1,5,9]
   shuffled_ary = OneLiner.shuffle(ary)
   assert_not_equal ary, shuffled_ary
   assert_equal ary.sort, shuffled_ary.sort
end

# Given a Ruby class name in String form (like
# "GhostWheel::Expression::LookAhead"), fetch the actual class object.
def test_get_class
   assert_equal Test::Unit::TestCase,
                OneLiner.get_class("Test::Unit::TestCase")
end

# Insert newlines into a paragraph of prose (provided in a String) so
# lines will wrap at 40 characters.
def test_wrap_text
   wrapped = "Insert newlines into a paragraph of " + "\n" +
             "prose (provided in a String) so lines " + "\n" +
             "will wrap at 40 characters."
   paragraph = "Insert newlines into a paragraph of " +
               "prose (provided in a String) so lines " +
               "will wrap at 40 characters."
   assert_equal wrapped, OneLiner.wrap_text(paragraph)
end

# Given an Array of String words, build an Array of only those words that
# are anagrams of the first word in the Array.
def test_find_anagrams
   anagrams = %w(tac cat act)
   assert_equal anagrams, OneLiner.find_anagrams(%w(tac bat cat rat act))
end

# 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
def test_binarize
   output = "111100111011111110101" + "\n" +
            "110000111100101100101" + "\n" +
            "1100100111010111011011100010"
   assert_equal output, OneLiner.binarize("you are dumb")
end

# Provided with an open File object, select a random line of content.
#
# NOTE: This test assumes you're using File#read to get the string data
# from the file - if doing otherwise, update the test?
def test_random_line
   f = Tempfile.new("foo")
   f.print("development:
adapter: mysql
database: redvase_development
host: localhost
username: root
password:")
   f.flush
   f.rewind
   lines = f.readlines
   line = OneLiner.random_line(f)
   assert_equal true, lines.include?(line)

end

# 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].
def test_wondrous_sequence
   seq = [23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
   assert_equal seq, OneLiner.wondrous_sequence(23)
end

# Convert an Array of objects to nested Hashes such that %w[one two three
# four five] becomes {"one" => {"two" => {"three" => {"four" => "five"}}}}.
def test_nested_hash
   hash = {:o => {:t => {:t => {:f => :f}}}}
   assert_equal hash, OneLiner.nested_hash([:o, :t, :t, :f, :f])
end
end

You read (Past Tense) me perfectly :wink:
Thx

Robert

···

On 2/9/07, James Edward Gray II <james@grayproductions.net> wrote:

On Feb 9, 2007, at 1:40 PM, Robert Dober wrote:

> On 2/9/07, Ruby Quiz <james@grayproductions.net> wrote:
>>
>> The three rules of Ruby Quiz:
>>
>> <snip>
>>
> Sounds like fun,
> Now the stupid question of the day: will a comment before the line
> be a
> problem for your tools, I guess they are counting :wink:

I don't have any tools. Someone should build a test suite and post
it though, if they feel motivated to do so.

For my solutions though, I put the questions as comments before each
solution.

--
We have not succeeded in answering all of our questions.
In fact, in some ways, we are more confused than ever.
But we feel we are confused on a higher level and about more important
things.
-Anonymous

Okay. The only one I'm taking significant liberties with is the
random-line-from-a-file - the test is expecting that the contents of
the file be loaded using File#read, and uses OpenStruct to provide a
test stub with that functionality. Other than that, these tests are a
pretty direct translation from the initial description. Method stubs
for the implementation are also provided.

require 'test/unit'
require 'ostruct'

class OneLiner
  class << self
    def commaize(number)
    end

    def flatten_once(ary)
    end

    def shuffle(ary)
    end

    def get_class(name)
    end

    def wrap_text(paragraph)
    end

    def find_anagrams(words)
    end

    def binarize(slogan)
    end

    def random_line(file)
    end

    def wondrous_sequence(n)
    end

    def nested_hash(ary)
    end
  end
end

class TestOneLiner < Test::Unit::TestCase
  # 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".
  def test_commaize
    assert_equal "1,999,995.99", OneLiner.commaize(1999995.99)
  end

  # 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]].
  def test_flatten_once
    ary = [1, [2, [3, 4]]]
    flatter_ary = [1, 2, [3, 4]]
    assert_equal flatter_ary, OneLiner.flatten_once(ary)
  end

  # Shuffle the contents of a provided Array.
  def test_shuffle
    ary = [3,1,4,1,5,9]
    shuffled_ary = OneLiner.shuffle(ary)
    assert_not_equal ary, shuffled_ary
    assert_equal ary.sort, shuffled_ary.sort
  end

  # Given a Ruby class name in String form (like
  # "GhostWheel::Expression::LookAhead"), fetch the actual class object.
  def test_get_class
    assert_equal Test::Unit::TestCase,
                 OneLiner.get_class("Test::Unit::TestCase")
  end

  # Insert newlines into a paragraph of prose (provided in a String) so
  # lines will wrap at 40 characters.
  def test_wrap_text
    wrapped = "Insert newlines into a paragraph of " + "\n" +
              "prose (provided in a String) so lines " + "\n" +
              "will wrap at 40 characters."
    paragraph = "Insert newlines into a paragraph of " +
                "prose (provided in a String) so lines " +
                "will wrap at 40 characters."
    assert_equal wrapped, OneLiner.wrap_text(paragraph)
  end

  # Given an Array of String words, build an Array of only those words that
  # are anagrams of the first word in the Array.
  def test_find_anagrams
    anagrams = %w(cat act)
    assert_equal anagrams, OneLiner.find_anagrams(%w(tac bat cat rat act))
  end

  # 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
  def test_binarize
    output = "111100111011111110101" + "\n" +
             "110000111100101100101" + "\n" +
             "1100100111010111011011100010"
    assert_equal output, OneLiner.binarize("you are dumb")
  end

  # Provided with an open File object, select a random line of content.

···

On 2/9/07, James Edward Gray II <james@grayproductions.net> wrote:

Someone should build a test suite and post
it though, if they feel motivated to do so.

  #
  # NOTE: This test assumes you're using File#read to get the string data
  # from the file - if doing otherwise, update the test?
  def test_random_line
    file = OpenStruct.new(:read => "development:
  adapter: mysql
  database: redvase_development
  host: localhost
  username: root
  password:")
    lines = file.read.split("\n")
    line = OneLiner.random_line(file)
    assert_equal true, lines.include?(line)
  end

  # 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].
  def test_wondrous_sequence
    seq = [23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
    assert_equal seq, OneLiner.wondrous_sequence(23)
  end

  # Convert an Array of objects to nested Hashes such that %w[one two three
  # four five] becomes {"one" => {"two" => {"three" => {"four" => "five"}}}}.
  def test_nested_hash
    hash = {:o => {:t => {:t => {:f => :f}}}}
    assert_equal hash, OneLiner.nested_hash([:o, :t, :t, :f, :f])
  end
end

Technically those are constants, not classes. For example, OpenSSL is a module. That neatly foils a solution someone sent me off list today. :wink:

James Edward Gray II

···

On Feb 9, 2007, at 5:35 PM, Phrogz wrote:

  # Built-in classes for testing #4
  :class_from_string => [
    ["OpenSSL", OpenSSL ],
    ["OpenSSL::Digest", OpenSSL::Digest ],
    ["OpenSSL::Digest::DigestError", OpenSSL::Digest::DigestError ],
  ],

Not that there are any known positive numbers that are not wondrous... indeed, it would be wondrous to find such a number. Brute force attacks from eight years ago place any non-wondrous number, should it exist, above 2E16.

That said, there are three known cycles of /negative/ non-wondrous numbers.

The real name of this problem is the Collatz Problem.

Dan

···

On Feb 9, 2007, at 2:15 PM, James Edward Gray II wrote:

On Feb 9, 2007, at 3:10 PM, Luke Ivers wrote:

* 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].

One final question: you say "given a wondrous number"... does this mean that
the input is guaranteed to be wondrous, and we therefore don't need to check
it...

Correct.

When I saw that, I thought that it could definitely be shortened by
using an inject. After all, there'd be no assignments of r, no final
returning of r, no semicolons. But when all was said and done, it
ended up being the same length. Compare:

    quiz.inject(){|r,a|r+[*a]}
    r=;quiz.each{|a|r+=[*a]};r

Oh well....

Eric

···

On Feb 12, 1:07 am, "Phrogz" <g...@refinery.com> wrote:

On Feb 11, 4:40 pm, Alex Young <a...@blackkettle.org> wrote:

> # One-level flatten().
> r=;quiz.each{|a|r+=[*a]};r

Elegantly small!

----------------
Are you interested in on-site Ruby training that uses well-designed,
real-world, hands-on exercises? http://LearnRuby.com

<cut>

Looking at others' solutions, I really like Robert Dober's
sort_by{rand} solution to the array shuffle.

That is kind of you but there is a funny story behind it.

Many others came up with it and it is a known idiom.

But yet I got so confused that I failed to post this solution.

I was about to post, DO NOT LAUGH

sort{ rand <=> rand} (which works BTW)

and eventually there was a thread about the issue and I (not even so kindly)
asked the people to wait for the spoiler period and it was my full intention
to tell them later that
sort_by { rand } was stupid when I found out -in time- that sort {rand} was
stupid and not sort_by{ rand }.

Actually I really do not deserve credit for this one.
<cut>

Cheers
Robert

···

On 2/12/07, Krishna Dole <dontfall@gmail.com> wrote:

--
We have not succeeded in answering all of our questions.
In fact, in some ways, we are more confused than ever.
But we feel we are confused on a higher level and about more important
things.
-Anonymous

What if it's a file too big to read into memory? :wink:

James Edward Gray II

···

On Feb 9, 2007, at 3:19 PM, Jamie Macey wrote:

On 2/9/07, James Edward Gray II <james@grayproductions.net> wrote:

Someone should build a test suite and post
it though, if they feel motivated to do so.

Okay. The only one I'm taking significant liberties with is the
random-line-from-a-file - the test is expecting that the contents of
the file be loaded using File#read...

[snip]

  # Given an Array of String words, build an Array of only those words that
  # are anagrams of the first word in the Array.
  def test_find_anagrams
    anagrams = %w(cat act)
    assert_equal anagrams, OneLiner.find_anagrams(%w(tac bat cat rat act))
  end

[snip]

I changed anagrams to %w(tac cat act). I'm the sort of person that
thinks a word is an anagram of itself (although, I have a feeling
"tac" isn't a word).

I had wondered about that. The wording of the quiz says "class", but I
figure that:
a) Given the number of modules-as-namespaces over classes-as-
namespaces, a good solution to the problem allows module stops along
the way, and thus
b) If you're going to allow non-classes along the way, you generally
want the "resolve this as though I typed it" functionality, which may
end in constants.

BUT...if the quiz has the wrinkle that only Classes should be returned
(and thus you should get nil for the cases above), that would be
interesting to program, too.

···

On Feb 9, 4:46 pm, James Edward Gray II <j...@grayproductions.net> wrote:

On Feb 9, 2007, at 5:35 PM, Phrogz wrote:

> # Built-in classes for testing #4
> :class_from_string => [
> ["OpenSSL", OpenSSL ],
> ["OpenSSL::Digest", OpenSSL::Digest ],
> ["OpenSSL::Digest::DigestError", OpenSSL::Digest::DigestError ],
> ],

Technically those are constants, not classes. For example, OpenSSL
is a module. That neatly foils a solution someone sent me off list
today. :wink:

I guess it is time now :wink:

This was a great quiz and it thought me that I did not *know anything about
Regexps*.
Now I know a little bit:
I also got acquainted with an old enemy of mine, #inject :slight_smile:

# 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{ |ele| ele.split("").sort == quiz.first.split("").sort }
# I left the first word in the list one could discard it within the 80 chars
limit be deleting WS in my solution.

# Given a Ruby class name in String form (like
# "GhostWheel::Expression::LookAhead"), fetch the actual class object.

sol113.tgz (4.53 KB)

···

#
x=quiz.split("::").inject(self.class){|k,m|k.const_get(m)};Class===x ? x :
nil

# * 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".
m=/[^\.]*/.match(quiz.to_s
);p=$';m[0].reverse.gsub(/\d{3}\B/,'\&,').reverse+p
# I could not believe how tough this one was for me!!!!

# * 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( [] ) { |a,e| a + ( Array === e ? e : [e] ) }

# 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{ |w| s=""; w.each_byte{ |b| s<<b.to_s(2)}; s}.join("\n")

# * Convert an Array of objects to nested Hashes such that %w[one two three
four
# five] becomes {"one" => {"two" => {"three" => {"four" => "five"}}}}.
quiz[0..-3].reverse.inject( { quiz[-2] => quiz[-1] } ){ |h,e| { e => h } }
# I found a fitting solution with a recursive method in 80 chars but
eventually
# I was saved by #inject from ridiculing myself.

# Provided with an open File object, select a random line of content.
q=quiz.readlines; q[ rand( q.size ) ].chomp

# * Shuffle the contents of a provided Array.
#
quiz.sort_by{ rand }
# This was a great Quiz but this problem was somehow an anticlimax for me,
did I miss something?

# 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].
q=quiz; x=[q]; until x[-1]==1 do x << ( q= q%2==0 ? q/2 : 3 * q + 1 ); end;
x
# Maybe not very challenging but a good read on Google:)

# Insert newlines into a paragraph of prose (provided in a String) so lines
will
# wrap at 40 characters.
#
quiz.gsub(/(.{1,40})(\b|\z)/){$1+"\n"}.gsub(/\s*\n\s*/,"\n").chomp
# tougher than I thought I am not sure about all edge cases

I attached a test suite and the solutions

Cheers
Robert
--
We have not succeeded in answering all of our questions.
In fact, in some ways, we are more confused than ever.
But we feel we are confused on a higher level and about more important
things.
-Anonymous

# Largely just to play around with rspec a bit.
# The random_line solution too long as well.

require 'rubygems'
require 'spec'

def commify(quiz)
quiz.to_s.reverse.gsub(/(\d{3})(?=\d)(?!\d*\.)/) {"#$1,"}.reverse
end

context "integers to strings" do
specify "should get commas every three digits from the right" do
   commify(123).should == '123'
   commify(1234).should == '1,234'
   commify(123456).should == '123,456'
   commify(-12345).should == '-12,345'
   commify(-1001001).should == '-1,001,001'
end
end

context "floats to strings" do
specify "should not get commas after decimal" do
   commify(123.456).should == '123.456'
   commify(123.456789).should == '123.456789'
   commify(123456.789).should == '123,456.789'
   commify(-123456.789).should == '-123,456.789'
end
end

def flatten1(quiz)
  quiz.inject([]){|r,n| n.respond_to?(:each) ? n.each {|i| r<< i} : (r<< n) ; r}
end

context "arrays nested arrays only one level deep" do
setup do
   @ary = [[1],2,[:symbol],'foo']
   @random = []
   10.times do
     n = rand(100)
     @random << (rand > 0.5 ? n : [n] )
   end
end

specify "should flatten1 the same as flatten" do
   flatten1(@ary).should == @ary.flatten
   flatten1(@random).should == @random.flatten
end
end

context "arrays nested multiple levels" do
specify "should only loose 1 level of arrays" do
   flatten1([1, [2, [3]]]).should == [1,2,[3]]
   flatten1([[[[[1]]]]]).should == [[[[1]]]]
end
end

def shuffle(quiz)
  quiz.sort_by { rand }
end

context "An array with several elements" do
  setup do
    @rands = [3,2,1,6,5,4,9,8,7,10]
    @a = (1..@rands.length).to_a
    x = -1

    self.stub!(:rand).and_return { @rands[x+= 1] }
  end

  specify "should sort randomly w/ shuffle" do
    shuffle(@a).should == @rands[0..@a.length-1]
  end
end

module GhostWheel
module Expression
   class LookAhead
   end
end
end

def to_class(quiz)
quiz.split(/::/).inject(Object) {|s,c| s.const_get(c.to_sym)}
end

context %{given a "class expression"} do
  specify "to_class should return that class" do
    GhostWheel.should_receive(:const_get).with(:Expression).and_return(GhostWheel::Expression)
    GhostWheel::Expression.should_receive(:const_get).with(:LookAhead).and_return(GhostWheel::Expression::LookAhead)

    to_class("GhostWheel::Expression::LookAhead")
  end

  specify "to_class should work for built-in classes" do
    to_class("Net::HTTP").should == Net::HTTP
  end
end

def wrap(quiz)
  quiz.gsub(/(.{1,40})\s+/){ "$1\n" }
end

context "A paragraph of text w/ less than 40 lines" do
  setup do
    @text = 'f' * 40
  end

  specify "should not be changed by wrap" do
    wrap(@text).should == @text
  end
end

context "A paragraph with more than 40 characters" do
  setup do
    @paragraph = <<-END_PARA.gsub(/\s+/, ' ').strip
       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].
    END_PARA
  end

  specify "should have no lines longer than 40 wide after wrapping" do
    wrap(@paragraph).split(/\n/).each do |line|
      line.length.should_not > 40
    end
  end
end

context "An paragraph with a word longer than 40 characters" do
  setup do
    @text = 'f' * 60
  end

  specify "should not be split mid word" do
    wrap(@text).should_not_include '\n'
  end
end

def anagrams(quiz)
  n=quiz[0].split(//).sort; quiz.select {|i| i.split(//).sort == n }
end

context "An array of words" do
  setup do
    @a = %w/silly lilsi looloo yllis yuf silly2 islyl/
  end

  specify "anagrams should contain words with same letters same number
of times" do
    anagrams(@a).should == %w/silly yllis islyl/
  end
end

def to_bin(quiz)
  quiz.scan(/\w/).collect {|c| sprintf "%b", c[0] }.join('')
end

context "A ascii string" do
  setup do
    @str = "you are dumb"
  end

  specify "should be converted to binary by to_bin" do
    to_bin(@str).should == '111100111011111110101' +
                           '110000111100101100101' +
                           '1100100111010111011011100010'
  end
end

def rand_line(quiz)
  z=0;quiz.each{z+=1};quiz.seek 0;n=rand
z;quiz.each_with_index{|x,i|;return x if i==n}
end

context "an open file handle" do
  setup do
    require 'stringio'
    @fh = StringIO.new <<-END
      one
      two
      three
      four
    END
  end

  specify "should return a random line" do
    line = rand_line(@fh).strip
    %w/one two three four/.should_include(line)
  end
end

def wonder(quiz)
  @a=[quiz];until quiz==1;@a<< quiz=quiz%2==0?quiz/2: quiz*3+1 end;@a
end

context "The wonderous sequence for 15" do
  setup { @w = wonder(15) }
  specify "should end with 1" do
    @w[@w.length-1].should == 1
  end
  specify "should match the test data" do
    @w.should == [15, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5,
16, 8, 4, 2, 1]
  end
end

def hashify(quiz)
  quiz.reverse.inject(){|m,i|{i=>m}}
end

context "An array of strings" do
  specify "should return nested hashes when hashified" do
    hashify(%w/one two three four five/).should ==
        {"one" => {"two" => {"three" => {"four" => "five"}}}}
  end
end

Comparing this with James' solution we can it down to
quiz.inject{|r,a|[*r]+[*a]}

···

On 2/12/07, Eric I. <rubytraining@gmail.com> wrote:

On Feb 12, 1:07 am, "Phrogz" <g...@refinery.com> wrote:
> On Feb 11, 4:40 pm, Alex Young <a...@blackkettle.org> wrote:
>
> > # One-level flatten().
> > r=;quiz.each{|a|r+=[*a]};r
>
> Elegantly small!

When I saw that, I thought that it could definitely be shortened by
using an inject. After all, there'd be no assignments of r, no final
returning of r, no semicolons. But when all was said and done, it
ended up being the same length. Compare:

    quiz.inject(){|r,a|r+[*a]}
    r=;quiz.each{|a|r+=[*a]};r

Oh well....

Eric

=================
I think this quiz is just lovely!!!

Robert

----------------

Are you interested in on-site Ruby training that uses well-designed,
real-world, hands-on exercises? http://LearnRuby.com

--
We have not succeeded in answering all of our questions.
In fact, in some ways, we are more confused than ever.
But we feel we are confused on a higher level and about more important
things.
-Anonymous

Neither did I, I was just looking for an easy test. And yet, lo and behold:

http://cancerweb.ncl.ac.uk/cgi-bin/omd?query=tac
A kind of customary payment by a tenant; a word used in old records.
Origin: Cf. Tack, 4.
Source: Websters Dictionary

···

On 2/9/07, Chris Shea <cmshea@gmail.com> wrote:

[snip]
> # Given an Array of String words, build an Array of only those words that
> # are anagrams of the first word in the Array.
> def test_find_anagrams
> anagrams = %w(cat act)
> assert_equal anagrams, OneLiner.find_anagrams(%w(tac bat cat rat act))
> end
[snip]

I changed anagrams to %w(tac cat act). I'm the sort of person that
thinks a word is an anagram of itself (although, I have a feeling
"tac" isn't a word).

My solution handles either.

James Edward Gray II

···

On Feb 9, 2007, at 6:00 PM, Phrogz wrote:

On Feb 9, 4:46 pm, James Edward Gray II <j...@grayproductions.net> > wrote:

On Feb 9, 2007, at 5:35 PM, Phrogz wrote:

  # Built-in classes for testing #4
  :class_from_string => [
    ["OpenSSL", OpenSSL ],
    ["OpenSSL::Digest", OpenSSL::Digest ],
    ["OpenSSL::Digest::DigestError", OpenSSL::Digest::DigestError ],
  ],

Technically those are constants, not classes. For example, OpenSSL
is a module. That neatly foils a solution someone sent me off list
today. :wink:

I had wondered about that. The wording of the quiz says "class", but I
figure that:
a) Given the number of modules-as-namespaces over classes-as-
namespaces, a good solution to the problem allows module stops along
the way, and thus
b) If you're going to allow non-classes along the way, you generally
want the "resolve this as though I typed it" functionality, which may
end in constants.

BUT...if the quiz has the wrinkle that only Classes should be returned
(and thus you should get nil for the cases above), that would be
interesting to program, too.

def anagrams(quiz)
  n=quiz[0].split(//).sort; quiz.select {|i| i.split(//).sort == n }
end

Argh! #select is exactly what I should have used. Nice.

def hashify(quiz)
  quiz.reverse.inject(){|m,i|{i=>m}}
end

How very elegant. I kept thinking there should be a way to bootstrap
the inner pair to be like all the outers, but couldn't find it. Well
done.

···

On Feb 11, 9:27 am, "Louis J Scoras" <louis.j.sco...@gmail.com> wrote:

please kindly insert the string "get" on the appropriate place in my last
post :wink:

Robert Dober wrote:

>
> > # One-level flatten().
> > r=;quiz.each{|a|r+=[*a]};r
>
> Elegantly small!

When I saw that, I thought that it could definitely be shortened by
using an inject. After all, there'd be no assignments of r, no final
returning of r, no semicolons. But when all was said and done, it
ended up being the same length. Compare:

    quiz.inject(){|r,a|r+[*a]}

Given the equal lengths, I prefer this - the r=;...;r idiom strikes me as rather ugly.

    r=;quiz.each{|a|r+=[*a]};r
    Oh well....

Eric

Comparing this with James' solution we can it down to
quiz.inject{|r,a|[*r]+[*a]}

Wow :slight_smile:

···

On 2/12/07, Eric I. <rubytraining@gmail.com> wrote:

On Feb 12, 1:07 am, "Phrogz" <g...@refinery.com> wrote:
> On Feb 11, 4:40 pm, Alex Young <a...@blackkettle.org> wrote:

--
Alex

Alex Young wrote:

Robert Dober wrote:

Hi All! Hi James! Hi Robert!

just wanted to note that there seems to be not a single message
from Robert on the newsgroup side of life.

(i'm not using googles interface, but to clarify)

comp.lang.ruby
http://groups.google.de/group/comp.lang.ruby/browse_thread/thread/90223b4082d864fa/5eaa58d92745fda3?lnk=gst&q=Re%3A+[QUIZ]+One-Liners+(%23113)&rnum=1&hl=de#5eaa58d92745fda3

ruby-talk-google
http://groups.google.de/group/ruby-talk-google/browse_thread/thread/90223b4082d864fa/b42536dc92412aeb?lnk=gst&q=Re%3A+[QUIZ]+One-Liners+(%23113)&rnum=1&hl=de#b42536dc92412aeb

If this is old news, feel free to ignore me.

cheers

Simon