Reputation: 37
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
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
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:
So, the code
sum += digit[n] if n > 0
is equivalent to
if n > 0
sum += digit[n]
end
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