On Tue, 18 May 2010 00:52:31 +0900, Branden Tanga
<branden.tanga / gmail.com> wrote:
> Hello all,
> 
> I just compiled ruby 1.9.1 from source on my computer (osx snow
> leopard), and I'm going through the old ruby quiz exercises for fun. I'm
> on the secret santa one, and I wrote the code that determines that each
> persons secret santa does not have the same last name, to ensure that
> each person has to get a gift for someone from a different family. The
> quiz is listed here:
> 
> http://rubyquiz.com/quiz2.html
> 
> My code runs fine most of the time. However, occasionally I'll get a
> strange "undefined method" error. See the code below and sample output:
> 
> class Person
>   attr_accessor :first, :last, :recipient
>   def initialize(first, last)
>     @first = first
>     @last = last
>     @recipient = nil
>   end
> end
> 
> a = [ giant listing of names in the format of "first last" ]
> 
> santa_array = [] # instantiate an empty array
> a.each do |full_name|
>   split_array = full_name.split
>   my_person = Person.new(split_array[0], split_array[1])
>   santa_array.push(my_person)
> end
> 
> recipient_array = Array.new(santa_array)
> santa_array.shuffle!
> recipient_array.shuffle!
> 
> santa_array.each do |element|
>   index = 0
>   while element.recipient == nil
>     if element.last != recipient_array[index].last # if the last names
> are different, process
>       element.recipient = recipient_array[index]
>       recipient_array.delete_at(index)
>     else
>       index += 1 # else the last names are the same, loop again on index
> += 1
>     end
>   end
> end
> 
> santa_array.each { |element| puts "#{element.first} #{element.last},
> recipient == #{element.recipient.first} " +
>                   "#{element.recipient.last}" }
> puts "\n\n"
> puts "santa_array.count == #{santa_array.count}"
> puts "recipient_array.count == #{recipient_array.count}"
> 
> And here's some sample output:
> brandentanga$ ./secretSanta.rb
> ./secretSanta.rb:67:in `block in <main>': undefined method `last' for
> nil:NilClass (NoMethodError)
>   from ./secretSanta.rb:64:in `each'
>   from ./secretSanta.rb:64:in `<main>'
> brandentanga$ ./secretSanta.rb
> luke skywalker, recipient == lando calrissian
> darth vader, recipient == han solo
> anakin skywalker, recipient == jeanluc piccard
> ... so on and so forth, continuing to print properly...
> 
> As you can see from the above output, I ran my code twice, with no
> modifications, all names are hard coded, and guaranteed to have first
> and last names separated by a space. So how can the same code with the
> same input produce a runtime error, then on the next run process as
> expected?

You don't check that index < recipient_array.size so if enough last names
match recipient_array[index] will be out of bounds. In Ruby Array.[]
returns nil for out of bounds indices. To answer your exact question the
random order given by shuffle! will be different each time you run the
program so although the input is the same the result of your algorithm is
not.

-- 
Alex Gutteridge