Vroryn
Vroryn

Reputation: 125

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|

Upvotes: 2

Views: 238

Answers (3)

akuhn
akuhn

Reputation: 27793

Excited to see that you're learning Ruby, welcome!

At your level of expertise best learn from a book.

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.

Upvotes: 1

Ryan Plant
Ryan Plant

Reputation: 1047

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.

Upvotes: 3

Amadan
Amadan

Reputation: 198314

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.

Upvotes: 5

Related Questions