Breaking Down the Block

Can someone please explain in plain english how this block treats the
given variables (and values in the array)? I am struggling to understand
the relationship of ‘t’ and ‘n’ with the array.

class Array
def sum
inject(0) {|t,n| t + n}
end
end
puts [1,2,3].sum

Is ‘t’ effectively labeled as ‘0’ (zero), given the assignment on the
inject method?
What “is” ‘t’ exactly?

my_array.inject(0) {|t,n| t + n} basically breaks down like:

t = 0
my_array.each {|n| t = (t + n) }
t

Similary, leaving out the inital value, my_array.inject {|t,n| t + n} basically breaks down like:

t = my_array[0]
my_array[1..-1].each {|n| t = (t + n) }
t

What you called ‘t’ (often called the ‘memory’) is the accumulation,
or the result of the last iteration of the block.

On 13 September 2012 12:09, incag neato [email protected] wrote:

Is ‘t’ effectively labeled as ‘0’ (zero), given the assignment on the
inject method?
What “is” ‘t’ exactly?


Posted via http://www.ruby-forum.com/.


Matthew K., B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

“You’ll never find a programming language that frees
you from the burden of clarifying your ideas.” - xkcd

incag neato wrote in post #1075712:

Can someone please explain in plain english how this block treats the
given variables (and values in the array)? I am struggling to understand
the relationship of ‘t’ and ‘n’ with the array.

class Array
def sum
inject(0) {|t,n| t + n}
end
end
puts [1,2,3].sum

Is ‘t’ effectively labeled as ‘0’ (zero), given the assignment on the
inject method?
What “is” ‘t’ exactly?

Just forget inject() even exists. It’s really quite a pathetic method.

Think of it this way:

inject(memo) { | memo, enum | block sets memo } => memo

So memo has three stages:

  1. Before the first iteration, memo is set to the value of the parameter
    (in parentheses after “inject”).

  2. After each iteration, the result of the block becomes the new value
    of memo.

  3. When it’s all over, the value of memo is the result.

So it’s all about accumulating results into memo. (It is sometimes
called the accumulator.)

In your example, the accumulation involves adding the successive values
of enum to the accumulator. The successive values of enum are the values
of the array. So the result is the sum of the array. (It’s the sum of
the array plus the initial value of the memo, but that initial value is
zero, so it really is just the sum.)

m.

However, with better variable names, it might be clearer what’s going on
with inject:

class Array
def sum1
inject(0) {|total, array_element| total += array_element}
end

def sum2
total = 0

self.each do |array_element|
  total += array_element
end

total

end

def multiply_em
inject(1) {|total, array_element| total *= array_element}
end

end

puts [1, 2, 3].sum1
puts [1, 2, 3].sum2
puts [1, 2, 3].multiply_em

–output:–
6
6
6

Or even better:

class Array
def sum1
initial_val_for_total = 0
inject(initial_val_for_total) {|total, array_element| total +=
array_element}
end
end

On Thu, Sep 13, 2012 at 12:46:23PM +0900, 7stud – wrote:

Just forget inject() even exists. It’s really quite a pathetic method.

inject is just reduce/foldl from other languages. It is just sugar for
building an accumulator in a foreach loop, but it has a long history.

Sung P. wrote in post #1075726:

On Thu, Sep 13, 2012 at 12:46:23PM +0900, 7stud – wrote:

Just forget inject() even exists. It’s really quite a pathetic method.

inject is just reduce/foldl from other languages. It is just sugar for
building an accumulator in a foreach loop, but it has a long history.

It’s also a baby step towards “functional programming” - realising that
you don’t actually need mutable variables to do useful computation,
which in turn frees you from certain kinds of programming logic errors.

I really like how Ruby lets you do things like this on a small scale,
without having to go full-out functional.

2012/9/13 7stud – [email protected]:

Or even better:

class Array
def sum1
initial_val_for_total = 0
inject(initial_val_for_total) {|total, array_element| total +=
array_element}
end
end

Sorry, but that’s bad practice in best case. #inject doesn’t depend on
the total being assigned to by the block; it depends on the value
returned by the block. Your code works, but somebody may be tempted to
do something like adding “p array_element” at the end and suddenly it
breaks for no obvious reason, if somebody believed the assignment was
essential.

– Matma R.

On Thu, Sep 13, 2012 at 5:55 AM, 7stud – [email protected] wrote:

However, with better variable names, it might be clearer what’s going on
with inject:

class Array
def sum1
inject(0) {|total, array_element| total += array_element}
end

The assignment is totally superfluous.

def sum2
total = 0

self.each do |array_element|
  total += array_element
end

total

end

Here “self.” is superfluous. :wink:

Kind regards

robert

Robert K. wrote in post #1075769:

On Thu, Sep 13, 2012 at 5:55 AM, 7stud – [email protected] wrote:

However, with better variable names, it might be clearer what’s going on
with inject:

class Array
def sum1
inject(0) {|total, array_element| total += array_element}
end

The assignment is totally superfluous.

Whoops. Yep, otherwise it wouldn’t be such a strange method.

def sum2
total = 0

self.each do |array_element|
  total += array_element
end

total

end

Here “self.” is superfluous. :wink:

I write it anyway. In my opinion, it’s 10 times clearer with an
explicit receiver.

This is quite helpful. If we ever meet I owe you each a beer.

In my newbie opinion, Matt N. did a fantastic job above:

Think of it this way:

inject(memo) { | memo, enum | block sets memo } => memo

So memo has three stages:

  1. Before the first iteration, memo is set to the value of the parameter
    (in parentheses after “inject”).
  1. After each iteration, the result of the block becomes the new value
    of memo.
  1. When it’s all over, the value of memo is the result.

So it’s all about accumulating results into memo. (It is sometimes
called the accumulator.)

What I’m really trying to get at with this question however, is
digesting something more like the following (and yes, I know it gets
slightly more complicated when I introduce a hash…and a map).

def mode(array)
recur = array.inject(Hash.new(0)) {|k,v| k[v]+=1; k}.sort_by { | (a,b)
| -b }
recur.select { | (a,b) | b == recur[0][1] }.map{ |(a,b) | a }
end

This is meant to find the mode of an array, but I can’t understand it
clearly. HELP!

z = y.select {|a, b| b == y[0][1]}
p z

–output:–
[[30, 2]]

In this example, the conditional b == y[0][1] is equivalent to b == 2.
If instead you had results like this,

[[10, 3], [30, 3], [20, 1], [40, 1]]

…then the conditional would be b == 3, which just selects all the
numbers that tie for the most appearances in the original array.

z = z.map {|a, b| a}
p z

–output:–
[30]

Finally, map selects the first element of all the sub arrays, where the
first element is a number that appeared the most times in the original
array.

recur = array.inject(Hash.new(0)) {|k,v| k[v]+=1; k}

Horrible variable names are the culprit again.

hash = {}
p hash[10]

–output:–
nil

hash = Hash.new(0)
p hash[10]

–output:–
0

array = [10, 20, 30, 30, 40]

result = array.inject(Hash.new(0)) {|hash, array_element|
hash[array_element] += 1; hash}

p result

–output:–
{10=>1, 20=>1, 30=>2, 40=>1}

x = result.sort_by {|key, val| val}
p x

–output:–
[[10, 1], [20, 1], [40, 1], [30, 2]]

y = result.sort_by {|key, val| -val}
p y

–output:–
[[30, 2], [10, 1], [20, 1], [40, 1]]

def mode(array)
freq_for = Hash.new(0)
array.each {|number| freq_for[number] += 1}

nums_with_frequs = freq_for.sort_by {|key, val| -val}
top_count = nums_with_frequs[0][1]
most_seen = nums_with_frequs.take_while {|number, freq| freq ==
top_count}

most_seen.map {|number, freq| number}
end

p mode([10, 10, 10, 20, 30, 30, 40, 40, 40])

–output:–
[10, 40]

Which do you prefer? Saving a few lines of code, or writing clear code?

incag neato wrote in post #1076331:

When I call “top_count = nums_with_frequs[0][1]” am I referring to the
first key and value in the sorted hash “freq_for” (i.e. the var
“nums_with_frequs”)?

I’ve played around with it but I don’t see how to use “b == 3” to arrive
at the tied values.

In my example:

nums_with_frequs = [[30,2]]

…which you could have easily determined yourself by printing it out.

Sorry, didn’t word my question very clearly.
In your previous example, you printed the double mode, [10, 40].
How could you make the program print [40, 10]?

(It’s the “b == 3” comment you made that I’m curious about.)
And how it would pertain to this code of yours:

def mode(array)
freq_for = Hash.new(0)
array.each {|number| freq_for[number] += 1}

nums_with_frequs = freq_for.sort_by {|key, val| -val}
top_count = nums_with_frequs[0][1]
most_seen = nums_with_frequs.take_while {|number, freq| freq ==
top_count}

most_seen.map {|number, freq| number}
end

p mode([10, 10, 10, 20, 30, 30, 40, 40, 40])

–output:–
[10, 40]

Wow, so helpful. Thanks for taking so much of your time to explain.
That must’ve taken at least an hour.

Now, the one question I have left pertains to the following (the tied
values (double mode)).


7stud – wrote in post #1076263:
z = y.select {|a, b| b == y[0][1]}
p z

–output:–
[[30, 2]]

In this example, the conditional b == y[0][1] is equivalent to b == 2.
If instead you had results like this,

[[10, 3], [30, 3], [20, 1], [40, 1]]

…then the conditional would be b == 3, which just selects all the
numbers that tie for the most appearances in the original array.


How is “b == y[0][1]” similar to “b == 2” ?
Do you mean the value in the sorted hash?

When I call “top_count = nums_with_frequs[0][1]” am I referring to the
first key and value in the sorted hash “freq_for” (i.e. the var
“nums_with_frequs”)?

I’ve played around with it but I don’t see how to use “b == 3” to arrive
at the tied values.

In your previous example, you printed the double mode, [10, 40].
How could you make the program print [40, 10]?

Start at the top of the thread. Read every post, and write down every
method in every post. See if you can’t find a method that will do that.

(It’s the “b == 3” comment you made that I’m curious about.)

  1. If x = 2, then the statement:

result = x + 4

is equivalent to:

result = 6.

the conditional b == y[0][1]
If … you had results like this,

y = [[10, 3], [30, 3], [20, 1], [40, 1]]

What is y[0][1]? Now plug that value into this conditional:

b == y[0][1]

What do you get?