What do these 2 lines of code actually do? - ruby

I don’t know Ruby but I need to understand how the input value is manipulated in the lines below.
I think that the input is converted to the sum of the values of its characters but the second line is confusing; does it take the final value of the sum and perform the bitwise operations or per iteration? Could you provide a simple explanation of the steps followed?
Thank you in advance!
input.to_s.each_char.inject(0) do |sum, ch|
(sum << 8) ^ (ch.ord) ^ (sum >> 4)

inject is the same as reduce, and is similar to reduce in many other languages.
There are a number of different ways to call it but the way shown in the question is this:
inject(initial) { |memo, obj| block } → obj
If you specify a block, then for each element in enum the block is
passed an accumulator value (memo) and the element. If you specify a
symbol instead, then each element in the collection will be passed to
the named method of memo. In either case, the result becomes the new
value for memo. At the end of the iteration, the final value of memo
is the return value for the method.
If you do not explicitly specify an initial value for memo, then the
first element of collection is used as the initial value of memo.
So in your case:
input.to_s.each_char.inject(0) do |sum, ch|
(sum << 8) ^ (ch.ord) ^ (sum >> 4)
end
The initial value of sum is 0, which is used for the first iteration, but for each subsequent iteration the result of the block is used for sum in the next iteration.
For example the following should produce the same value without using inject/reduce.
sum = 0
input.to_s.each_char do |ch|
sum = (sum << 8) ^ (ch.ord) ^ (sum >> 4)
end

Related

Deeper explanation of reduce / inject method in ruby

I've been racking my brains around this for some time.
When using reduce - why is the first element returned without executing the operation defined in the block? Or am I missing a crucial point in how reduce works?
In the following example:
arr = [1, 3, 5]
arr.reduce {|sum, n| sum + (n * 3) }
#=> 25
I would have expected the result to be 27.
Since:
0 + (1 * 3) = 3
3 + (3 * 3) = 12
12 + (5 * 3) = 27
After some time playing around with it I figured out that in the first "tick" - the object from the array just get's added to the sum instead of being multiplied. So that the calculation is more like:
??? = 1
1 + (3 * 3) = 10
10 + (5 * 3) = 25
Could someone help me figure out where I've gotten off the path?
It's in the docs.
If you do not explicitly specify an initial value for memo, then the first element of collection is used as the initial value of memo.
I had a similar issue with the default values in Ruby inject/reduce methods, so I've tried to visualize it:
I thnk that the explanation is in the help o¡f the methos in this case:
[1] pry(main)> cd Array
[2] pry(Array):1> ? reduce
From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Signature: reduce(*arg1)
Number of lines: 33
Combines all elements of enum by applying a binary
operation, specified by a block or a symbol that names a
method or operator.
The inject and reduce methods are aliases. There
is no performance benefit to either.
If you specify a block, then for each element in enum
the block is passed an accumulator value (memo) and the element.
If you specify a symbol instead, then each element in the collection
will be passed to the named method of memo.
In either case, the result becomes the new value for memo.
At the end of the iteration, the final value of memo is the
return value for the method.
If you do not explicitly specify an initial value for memo,
then the first element of collection is used as the initial value
of memo.
# Sum some numbers
(5..10).reduce(:+) #=> 45
# Same using a block and inject
(5..10).inject { |sum, n| sum + n } #=> 45
# Multiply some numbers
(5..10).reduce(1, :*) #=> 151200
# Same using a block
(5..10).inject(1) { |product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo, word|
memo.length > word.length ? memo : word
end
longest #=> "sheep"
So you need to specify the first memo as 0, in your case is one:
[4] pry(Array):1> [1,3,5].reduce(0) {|sum, n| sum + (n * 3) }
=> 27

About the inject method: How does it work?

This part of code calculates the value of each arithmetic series up to and including the number that the user put in:
print "enter a number: "
num = gets.to_i
(1..num).inject(0) do |res, e|
res += e
p res
end
I think that (1..num) is the range, with num being the user input. I know that inject combines all elements of enum by applying a binary operation specified by a block or a symbol that names a method or operator.
I don't understand what each element in this line does:
(1..num).inject(0) do |res, e|
What does |res, e| mean? It must be the block that defines what inject does, but what does for instance res and e stand for? (e is probably element?)
What does (0) stand for?
What does the command do do?
what is its connection in regard to (1..num) and inject(0)?
Am I right to assume that p at the end just stands for puts or print?
inject takes an optional start value, and a block taking an intermediate value and element and returning a new intermediate value.
So:
What does (0) stand for?
The start value parameter to inject.
What does the command "do" do?
It is not a command; it marks the start of the block (terminated by end). .inject(0) do ... end is almost (except for some syntactic issues) the same as .inject(0) { ... }. Usually, do ... end is used for multi-line blocks and { ... } for single-line blocks, but it is not a rule.
What does |res, e| mean?
Those are the block parameters (intermediate value and current element), here probably called after "result" and "element", respectively.
Let's see on a simplified example: (1..3).inject(0) do |res, e| res + e end will set the intermediate result to 0. Then it will pass this intermediate result and the first element of the enumerable being injected: res is 0 and e is 1. The value of the block is the value of its last expression, which is 1 (result of 0 + 1). This 1 now becomes the new intermediate value, and 2 becomes the next current element. The value of the block is 3 (result of 1 + 2). In the next iteration, intermediate value is 3, and the current element also 3, resulting in 6 (3 + 3). The range will stop yielding elements now that we reached its upper boundary, and inject returns with the last intermediate result calculated, 6.
Also, the last question am I right to assume that "p" at the end just stands for puts or print?
Almost. p is its own beast. p x is roughly synonymous with puts x.inspect; x - i.e. it prints the value in a bit different format, and unlike puts which always returns nil, p returns the value unchanged. Thus, p res at the end of your block will not destroy the code by making it return nil, but transparently return res.
inject is a method that boils a collection (eg an array or range) down to a single value. It does this by executing the block once for each element in the collection. The block takes two arguments: the current value being worked on, and the single value that will eventually be returned. inject itself takes one argument (aside from the block), which is its initial starting value.
Take this example.
x = [1, 2, 3, 4, 5].inject(0) do |result, current|
result + current
end
We have a list of numbers, [1, 2, 3, 4, 5]. We're going to boil them down into one single number.
We start with 0, because that's inject's argument. That means that the first time the block runs, result will be 0.
So the block runs for the first time. result is 0, current is 1, the first element. We say result + current (which is 1). It's the last expression inside the block, so it's what that block 'returns'.
At the end of the block, inject says "Okay, do we have more elements in the collection?" Yeah. So the block runs again. This time, result is whatever the last block returned, which was 1, and current is the second element, 2.
The block runs, and finishes with result + current, or 1 + 2, which is 3. There are still elements left, so we run again. This time, result is 3, and current is 3. result + current, 6. Still more values to go, on the next run result is 6 and current is 4. 6 + 4 = 10. Still more values to go, on the next run result is 10 and current is 5. 10 + 5 = 15.
Then the block finishes, and there are no more elements left. So inject itself returns with the final value, 15. So in the end, x = 15. We boiled down our list into one number by adding things up.
res in your example stands for result, and e for element. You can call them anything you want. You might call them sum while adding, or product if multiplying. But they don't have to be numbers. You could use inject to boil an array of strings, a range of characters, an array of arrays, whatever collection you want. The block just tells it how.
Excited to see that you're learning Ruby, welcome!
At your level of expertise best learn from a book.
Get yourself a copy of the "Pickaxe Book" — this is by far the best book.
The first edition fir Ruby 1.9 is available online, http://ruby-doc.com/docs/ProgrammingRuby
And here is a popular online tutorial, http://poignant.guide
Some quick answers
1..num is a range object
.inject(0) do ... end is a method call with TWO parameters, the value 0 and a code block
do |a, b| ... end is a code block with two parameters
res and e are VERY bad variable names, maybe better use sum and each?
p is a global method that prints debug information, similar to puts but not the same
Hope that helps to unblock you.

Why do I get a 'typeerror' when using inject in Ruby?

I am using this inject method to make a running total of values into an array. I am trying to figure out why I am getting an error.
def running_totals(myarray)
results = []
myarray.inject([]) do |sum,n|
results << sum + n
end
results
end
p running_totals([1,2,3,4,5])
I am getting the error
in `+': no implicit conversion of Fixnum into Array (TypeError)
When breaking this method down, isn't this the same as adding two integers and adding that into an array? I'm a bit confused here. Thanks for the help.
In the first iteration sum will be an array (as you specified an array as the default when calling inject([])) and you try to add a number to it. in the results << sum + n statement
Instead, set the initial value to 0, then add, then add the result to the array, then make sure you let sum get passed into the next iteration of inject.
def running_totals(myarray)
results = []
myarray.inject(0) do |sum,n| # First iteration sum will be 0.
sum += n # Add value to sum.
results << sum # Push to the result array.
sum # Make sure sum is passed to next iteration.
end
results
end
p running_totals([1,2,3,4,5]) #=> [1, 3, 6, 10, 15]
The result of results << sum + n is an array results and it's this that's replacing the sum value and so the next iteration you're trying to add a fixnum n into an array sum ... plus it doesn't help that you're initializing the value of sum to be an array.
Make sure that the last executed statement in your inject block is what you want the accumulated value to be.
def running_totals(myarray)
results = []
results << myarray.inject do |sum, n|
results << sum
sum + n
end
results
end
p running_totals([1,2,3,4,5])
=> [1, 3, 6, 10, 15]
Note that I moved the result of the inject into results array as well, so that the final value is also included, otherwise you'd only have the four values and would be missing the final (15) value.
The return value of the inject block is passed as the first argument the next time the block is called, so those have to match. In your code, you're passing an array as an intital value, and then returning an array; so far, so good. But inside the code block you treat that array parameter (sum) as a number, which won't work. Try this:
def running_totals(myarray)
myarray.inject([]) do |results,n|
results << n + (results.last || 0)
end
end
The [] passed as an argument to inject becomes the first value of results; the first array element (1 in your example) becomes the first value of n. Since results is empty, results.last is nil and the result of (results.last || 0) is 0, which we add to n to get 1, which we push onto results and then return that newly-modified array value from the block.
The second time into the block, results is the array we just returned from the first pass, [1], and n is 2. This time results.last is 1 instead of nil, so we add 1 to 2 to get 3 and push that onto the array, returning [1,3].
The third time into the block, results is [1,3], and n is 3, so it returns [1,3,6]. And so on.
According to ri, you have to return result of the computation from inject's block.
From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Signature: inject(*arg1)
Number of lines: 31
Combines all elements of enum by applying a binary
operation, specified by a block or a symbol that names a
method or operator.
If you specify a block, then for each element in enum
the block is passed an accumulator value (memo) and the element.
If you specify a symbol instead, then each element in the collection
will be passed to the named method of memo.
In either case, the result becomes the new value for memo.
At the end of the iteration, the final value of memo is the
return value for the method.
If you do not explicitly specify an initial value for memo,
then uses the first element of collection is used as the initial value
of memo.
Examples:
# Sum some numbers
(5..10).reduce(:+) #=> 45
# Same using a block and inject
(5..10).inject {|sum, n| sum + n } #=> 45
# Multiply some numbers
(5..10).reduce(1, :*) #=> 151200
# Same using a block
(5..10).inject(1) {|product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo,word|
memo.length > word.length ? memo : word
end
longest
So your sample would work if you return computation result for each iteration, something like this:
def running_totals(myarray)
results = []
myarray.inject do |sum,n|
results << sum + n
results.last # return computation result back to Array's inject
end
results
end
Hope it helps.

Simple ruby loop to get sum of cubes

I was given this problem to solve with Ruby:
Compute the sum of cubes for a given range a through b. Write a method called sum_of_cubes to accomplish this task.
I wrote this:
def sum_of_cubes(a, b)
sum = 0
for x in a..b
c = x ** 3
end
sum += c
end
I got the value of the cube of b. What is wrong with this code? How can I solve this problem with a simple loop?
Thanks!
I would use Enumerable#reduce
def sum_of_cubes min, max
(min..max).reduce(0) { |a, b| a + b ** 3 }
end
A little explanation of what's happening here
We start with range (min..max) which is an Enumerable
irb> (1..3).is_a? Enumerable
=> true
Using the reduce instance method we get from Enumerable, we can use a block of code that gets called for each item in our (enumerable) range, and ultimately returns a single value.
The function name makes sense if you think "take my group of items and reduce them to a single value."
Here's our block
{ |a, b| a + b ** 3 }
We called reduce with 0 which is the initial value given to the block's a param
The return value of the block is passed to the block's a param on subsequent calls
Each item in the range will be passed to the block's b param
Let's step through and see how it works
(1..3).reduce(0) { |a, b| a + b ** 3 }
the first block call gets a=0 (initial value) and b=1 (first item in our range)
the return value of our block is 0 + 1 ** 3 or 1
the second block call gets a=1 (return value from the last call) and b=2 (the second item in our range)
the return value of our block is 1 + 2 ** 3 or 9
the third block call gets a=9 (return value from the last call) and b=3 (the third and last item in our range)
the return value of our block is 9 + 3 ** 3 or 36
the final return value of reduce is the last-called block's return value
in this case 36
You need to have sum += c inside the loop. And then return sum when done.
Here’s another way to calculate this. It doesn’t address your problems with your loop but I think it’s worth mentioning.
The sum of cubes of integers 13 + 23 + 33 + ... + n3 is given by the formula (n(n + 1)/2)2, so the sum of cubes of a given range min..max is therefore given by:
(max(max + 1)/2)2 - ((min-1)((min-1) + 1)/2)2
In code this could look like:
def sum_of_cubes_fixed min, max
lower = min - 1
(((max * (max + 1))/2) ** 2) - (((lower * (lower + 1))/2) ** 2)
end
This code avoids the loop, and so is O(1) rather than O(n) (almost – I’m hand waving a bit here, the time complexity of the multiplications and exponentiations will depend on the size of the numbers). For small sized ranges you won’t notice this, but for larger sizes the difference between this and the loop version becomes increasingly obvious. I haven’t done any strict benchmarks, but a quick test on my machine with the range 1 to 10,000,000 takes several seconds with the reduce method but is almost instantaneous with this method.
Normally I would just use reduce for something like this, but the structure of the task suggested that there might be a better way. With the help of Google I found the formula and came up with a more efficient solution (at least for large ranges).
(a..b).map{|i| i**3 }.inject(&:+) map and inject does the job, elegantly.
EDIT: Although it walks through the list twice ;)

Calculate missing number

Here's the exercise:
You have been given a list of sequential numbers from 1 to 10,000, but
they are all out of order; furthermore, a single number is missing
from the list. The object of the task is to find out which number is
missing.
The strategy to this problem is to sum the elements in the array, then sum the range 1 to 10,000, and subtract the difference. This is equal to the missing number. The formula for calculating the sum of the range from 1..n being n(n+1)/2.
This is my current approach:
def missing_number(array)
sum = 0
array.each do |element|
sum += element
end
((10000*10001)/2) - sum
end
Where I am getting tripped up is the output when I input an array such as this:
puts missing_number(*1..10000) #=> 0
Why does this happen?
Thanks!
No need to sort the array. An array of length N is supposed to have all but one of the numbers 1..(N+1) so the array length + 1 is the basis for figuring out what the grand_sum would be if all values were there.
def missing_number(array)
grand_sum = (array.length + 1) * (array.length + 2) / 2
grand_sum - array.inject(:+)
end
ADDENDUM
This method takes an array as an argument, not a range. You can't use a range directly because there wouldn't be a missing value. Before calling the method you need some mechanism for generating an array which meets the problem description. Here's one possible solution:
PROBLEM_SIZE = 10_000
# Create an array corresponding to the range
test_array = (1..PROBLEM_SIZE).to_a
# Target a random value for deletion -- rand(N) generates values in
# the range 0..N-1, inclusive, so add 1 to shift the range to 1..N
target_value = rand(PROBLEM_SIZE) + 1
# Delete the value and print so we can check the algorithm
printf "Deleting %d from the array\n", test_array.delete(target_value)
# Randomize the order of remaining values, as per original problem description
test_array.shuffle!
# See what the missing_number() method identifies as the missing number
printf "Algorithm identified %d as the deleted value\n", \
missing_number(test_array)
An alternative approach to solving the problem if it's not performance critical, because of its readability:
def missing_number(array)
(1..10_000).to_a - array
end
Instead of *1..10000, the argument should be (1..10000).to_a.
You shouldn't be using *1..10000, this will just expand to 10,000 arguments. (1..10000).to_a will return zero because there are no elements missing between 1..10000 you need to remove one. Below is some code with a detailed explanation.
def missing_number array
# put elements in order
array.sort!
# get value of last array element
last = array[-1]
# compute the expected total of the numbers
# 1 - last
# (n + 1)(n)/2
expected = (last + 1) * (last / 2)
# actual sum
actual = array.inject{|sum,x| sum + x}
# find missing number by subtracting
(expected - actual)
end
test = (1..10000).to_a
test.delete 45
puts "Missing number is: #{missing_number(test)}"

Resources