user1110749
user1110749

Reputation: 37

How do I convert Ruby code to pseudo code?

I do not know Ruby, but I am trying to understand this code for the Euler #17 problem. I understand the problem, and I understand the first few lines of the code. And, I googled about individual methods like puts and injects. I do not understand what the code is trying to do after |sum,n|.

Can some one translate it to some kind of pseudo-code?

This is the code:

digit = [ 4, 3, 3, 5, 4, 4, 3, 5, 5, 4, 3, 6, 6, 8, 8, 8, 7, 9, 8, 8 ]
decade = [4, 3, 6, 6, 5, 5, 5, 7, 6, 6]

puts (1..1000).inject(0) { |sum, n| 
  sum, n = sum + 11, n % 1000 if n > 999
  sum, n = sum + digit[n / 100] + (n % 100 > 0 ? 10 : 7), n % 100 if n > 99
  sum, n = sum + decade[n / 10], n % 10 if n > 19
  sum += digit[n] if n > 0
  sum
}

Upvotes: 2

Views: 1755

Answers (2)

Paolo Capriotti
Paolo Capriotti

Reputation: 4072

First, note that there's a mistake in that program: digit[15] should be 7, not 8.

I'm not sure if translating it to pseudocode would make it any clearer, but here is a line by line explanation:

sum, n = sum + 11, n % 1000 if n > 999

If n is at least 1000, add the number of non-space characters of the word one thousand into the running total sum, then replace n with the remainder of n divided by 1000. For example, if n were 1538, n % 1000 would be 538, thus removing the first digit.

sum, n = sum + digit[n / 100] + (n % 100 > 0 ? 10 : 7), n % 100 if n > 99

If n is at least 100, add the length of the name of the first digit, plus 7 (the length of the word hundred). If n is not a multiple of 100, you also need to add the word and, for a total of 10 characters. Then remove the first digit of n, as before.

sum, n = sum + decade[n / 10], n % 10 if n > 19

Now add the number of characters needed to express the first digit (twenty for 2, thirty for 3, etc...), provided that digit is at least 2. The "teens" are handled separately in the last line. Finally, replace n with its last digit.

sum += digit[n] if n > 0

At this point, n is either a single digit or a "teen", and the number of characters in its name are all precomputed in the digit array, so we add that value and we're done.


Some possibly obscure syntax features being used here:

  • Multiple assignment

    In ruby you can write statements like

    a, b = 3, 5
    

    to assign values to more than one variable simultaneously. There's no real reason to do so in this case, except to make the code shorter (though arguably less readable).

  • Postfix conditional

    Conditionals whose body has only 1 expression can be written in a postfix form. For example:

    puts "hi" if n > 0
    

    is exactly equivalent to:

    if n > 0
      puts "hi"
    end
    

    Again, this is used only to make the code shorter.

  • Ternary operator

    Yet another way to write a conditional expression:

    n > 0 ? n : 1
    

    translates to

    if n > 0
      n
    else
      1
    end
    

By "desugaring" all the special syntax explained above (and fixing the mistake about 15), the program becomes:

digit = [ 4, 3, 3, 5, 4, 4, 3, 5, 5, 4, 3, 6, 6, 8, 8, 7, 7, 9, 8, 8 ]
decade = [4, 3, 6, 6, 5, 5, 5, 7, 6, 6]

puts (1..1000).inject(0) { |sum, n|
  if n > 999
    sum += 11
    n = n % 1000
  end

  if n > 99
    sum += digit[n / 100] + 7
    if n % 100 > 0
      sum += 3
    end
    n = n % 100
  end

  if n > 19
    sum += decade[n / 10]
    n = n % 10
  end

  if n > 0
    sum += digit[n]
  end

  sum
}

Upvotes: 3

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230521

I assume you are already familiar with modulo operand (n % 100) and ternary operator ( condition ? result_if_true : result_if_false)

The only two new concepts here are:

  • suffix condition

So, the code

sum += digit[n] if n > 0

is equivalent to

if n > 0
  sum += digit[n]
end
  • mass assignment

code

sum, n = sum + decade[n / 10], n % 10 if n > 19

is equivalent to

if n > 19
  sum = sum + decade[n / 10]
  n = n % 10
end

Now the code should be obvious.

Upvotes: 0

Related Questions