views:

140

answers:

5

The |m,k| thing kind of throws me off. Does this have anything to do with order of precedence? m standing for 0 (or 1 in some languages) and k for the last in the Array/Hash whatever?

So why do people put a number in .inject()?

Alternatively, is there an easy way to learn how to use this, and exactly what it's value is? Judging from this question I hope you all know I'm pretty noobish to any programming language and Ruby was my first choice.

+1  A: 

The |x,y| are used when using blocks. When the yield statement is called you tell it to map the calling variables to x and y.

Look here Blocks and Yield

See here for a breakdown of an inject() call.

kjfletch
+5  A: 

You seem to be confusing block arguments with method arguments.

The number people pass into the inject() method is a method argument that determines the initial value for "m" when you use |m,k| for the block. I don't know where you saw them named m and k, or why they were named so, but it's certainly not because they stand for the first and last element.

It would be simpler to look at it in the way described in kjfletch's link, breakdown of an inject(), where they're named "result" and "element" instead.

The value in using inject() is the ability to be concise. Anything you want to do with inject() could also be done with a call to the each() method, with a much longer block, and extra variable declarations. See this question and the answer to get a feeling for it.

ehsanul
+6  A: 

Let's try an example.

numbers = [ 3, 1, 4, 1, 5, 9 ]
sum = numbers.inject(0) do |prefix_sum, number|
  prefix_sum + number
end

#inject takes one argument and a block. The block should take two values, and return a new value.

In the above example, the argument to #inject is 0, and the block is do |prefix_sum, number| prefix_sum + number end. The values that will be passed to the block are named in between the two | markers: prefix_sum and number.

Each value of the enumerable #inject was called on is passed as the second value to the block in turn. In this example number will be 3, then 1, then 4, then 1, then 5, then finally 9. So in this example, the block will be invoked six times; once for each position in numbers.

The first value passed to a block (here named prefix_sum) is usually called an accumulator. Its initial value, the value used the first time the block is called by #inject, is set by the argument passed to #inject (in this example, 0). The return value of the block determines the value of the accumulator (prefix_sum) for the next invocation of the block.

When there are no more elements to process, the value of the accumulator is returned (and here stored in sum).

So lets walk through it:

  • #inject receives 0 and our block.
  • #inject invokes our block, binding prefix_sum to 0 (the initial accumulator value) and number to 3 (the first array value).
  • our block calculates 0+3 as 3 and returns it.
  • #inject invokes our block, binding prefix_sum to 3 (the returned value) and number to 1 (the second array value)
  • our block calculates 3+1 as 4 and returns it.
  • #inject invokes our block, binding prefix_sum to 4 (the returned value) and number to 4 (the third array value)
  • our block calculates 4+4 as 8 and returns it.
  • #inject invokes our block, binding prefix_sum to 8 (the returned value) and number to 1 (the fourth array value)
  • our block calculates 8+1 as 9 and returns it.
  • #inject invokes our block, binding prefix_sum to 9 (the returned value) and number to 5 (the fifth array value)
  • our block calculates 9+5 as 14 and returns it.
  • #inject invokes our block, binding prefix_sum to 14 (the returned value) and number to 9 (the sixth array value)
  • our block calculates 14+9 as 23 and returns it.
  • since there are no more array elements, #inject returns 23, and we bind sum to be that value.

You can look at inject as parenthesizing an operation on a list of items, in this example, caluculating:

((((((0 + 3) + 1) + 4) + 1) + 5) + 9)

This lets you take any operation which normally only operates on a pair of arguments, and apply it to a list.

rampion
+1 great explanation
glenn jackman
+2  A: 

If you want to find out what any method does in Ruby, you can use the bundled ri tool (so you could type "ri Enumerable.inject" to look up the docs) or search Ruby-Doc. In this case, you would see:

Combines the elements of enum by applying the block to an accumulator value (memo) and each element in turn. At each step, memo is set to the value returned by the block. The first form lets you supply an initial value for memo. The second form uses the first element of the collection as a the initial value (and skips that element while iterating).

# Sum some numbers
(5..10).inject {|sum, n| sum + n }              #=> 45
# Multiply some numbers
(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"

# find the length of the longest word
longest = %w{ cat sheep bear }.inject(0) do |memo,word|
  memo >= word.length ? memo : word.length
end
longest                                         #=> 5
Chuck
A: 

For completeness: m usually means memo; k may mean key (often uses in conjunction with v, which means value). So, for example:

stuff.inject({}) { |m,(k,v)| m.merge(k.to_sym => v) }

I've also seen people use a for accumulator and e for element, like this:

numbers.inject { |a,e| a+e }
ddfreyne