Ruby Quiz <james / grayproductions.net> writes:

> You have shown me the light and it tells me...  Daniel Martin is crazy.  I'll
> leave it to him to explain his own solution, as punishment for the time it took
> me to puzzle it out.  I had to print that Array inside of the inject() call
> during each iteration to see how it built up the answer.

As if you needed further evidence after my third solution to pp
Pascal.

And the one in my solution is not *so* bad.  The really nasty Luhn
implementation was what I posted as a follow-up
(http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/249669)
That algorithm was:

  def luhn(s)
    s.scan(/\d/).map{|x|x.to_i}.inject([0,0]){
    |(a,b),c|[b+c%9,a+2*c%9]}[0]%10 == s.scan(/9/).size%10
  end

Now, here's an explanation of the version in my posted solution:

First, the code in my solution was this:

  def luhn(s)
    s.scan(/\d/).inject([0,0]){|(a,b),c|[b+c.to_i,
    a+c.to_i*2%9+(c=='9' ? 9 : 0)]}[0]%10 == 0
  end

The main issue with doing the Luhn algorithm as a straight-forward
inject command is that you don't know on each digit whether this digit
is one that should be doubled or not.

Now, there are a few ways around this:
1) have a "should_double" variable that you initialize to
      should_double = (s.length % 2 == 0)
   and then in your block do
      should_double = !should_double
2) reverse the data, and use something like each_index to get
   you the index each time, and double when the index is even
   (most people did this)
3) like choice (1), but reverse the data and initialize
   should_double to false.

I chose a different path: at each stage, compute both possibilities.
That is, do an inject loop with two running totals - one in which the
number we're dealing with now should be doubled, and one in which it
shouldn't.  At each stage, swap the two totals.  At the end, pick the
total that did not involve doubling the last digit.

If the Luhn algorithm just involved adding the doubled digits (and
didn't involve the added complication of adding their digits),
generating both sums would be just:

   s.scan(/\d/).map{|x|x.to_i}.inject([0,0]){
       |(sum2, sum1),x| [sum1 + x, sum2 + 2*x]
   }

Note how I swapped sum1 and sum2 in the process.

Then, to take the sum that did not involve doubling the last digit,
just do:

   s.scan(/\d/).map{|x|x.to_i}.inject([0,0]){
       |(sum2, sum1),x| [sum1 + x, sum2 + 2*x]
   }[0]

Now, adding digits of numbers together is the well-known process of
"casting out nines", and essentially what you're doing with that sum
is taking the residue modulo 9.  (in other language: "finding the
remainder when dividing by 9")  That is, adding the digits together
turns 2*x into 2*x%9, except that if x is 9 then you get 9, not 0.
This makes our sum algorithm into:

   s.scan(/\d/).map{|x|x.to_i}.inject([0,0]){
       |(sum2, sum1),x| [sum1 + x, sum2 + 2*x%9 + (x==9 ? 9 : 0)]
   }[0]

This is almost my luhn method above, except that I used unhelpful
one-letter variable names and didn't do the map call, preferring
instead to use .to_i in the body of my inject loop.

Now, go back and puzzle out the version I mentioned at the top of this
email.  That version was born out of a desire to avoid the ? :
operator, since I tend to find it ugly.

-- 
s=%q(  Daniel Martin -- martin / snowplow.org
       puts "s=%q(#{s})",s.to_a.last       )
       puts "s=%q(#{s})",s.to_a.last