Forum: Ruby Breaking Down the Block

Posted by incag neato (incagneato)
on 2012-09-13 04:09
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?
Posted by Matthew Kerwin (mattyk)
on 2012-09-13 04:30
(Received via mailing list)
`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 <lists@ruby-forum.com> 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 Kerwin, 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
Posted by 7stud -- (7stud)
on 2012-09-13 05:46
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.
Posted by Matt Neuburg (Guest)
on 2012-09-13 05:50
(Received via mailing list)
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.
Posted by 7stud -- (7stud)
on 2012-09-13 05:55
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
Posted by 7stud -- (7stud)
on 2012-09-13 06:04
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
Posted by Sung Pae (Guest)
on 2012-09-13 06:13
(Received via mailing list)
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.
Posted by Brian Candler (candlerb)
on 2012-09-13 09:42
Sung Pae 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.
Posted by Bartosz DziewoƄski (matmarex)
on 2012-09-13 12:46
(Received via mailing list)
2012/9/13 7stud -- <lists@ruby-forum.com>:
> 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 Rex
Posted by Robert Klemme (robert_k78)
on 2012-09-13 12:55
(Received via mailing list)
On Thu, Sep 13, 2012 at 5:55 AM, 7stud -- <lists@ruby-forum.com> 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. ;-)

Kind regards

robert
Posted by 7stud -- (7stud)
on 2012-09-14 06:02
Robert Klemme wrote in post #1075769:
> On Thu, Sep 13, 2012 at 5:55 AM, 7stud -- <lists@ruby-forum.com> 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. ;-)
>


I write it anyway.  In my opinion, it's 10 times clearer with an
explicit receiver.
Posted by incag neato (incagneato)
on 2012-09-17 03:06
This is quite helpful. If we ever meet I owe you each a beer.

In my newbie opinion, Matt Neuburg 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").

> 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.)

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!
Posted by 7stud -- (7stud)
on 2012-09-17 06:03
> 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]]
Posted by 7stud -- (7stud)
on 2012-09-17 06:25
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.
Posted by 7stud -- (7stud)
on 2012-09-17 06:52
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?
Posted by incag neato (incagneato)
on 2012-09-17 19:29
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.
Posted by 7stud -- (7stud)
on 2012-09-17 22:34
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.
Posted by incag neato (incagneato)
on 2012-09-18 05:55
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]
Posted by 7stud -- (7stud)
on 2012-09-18 07:06
> 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.
Posted by 7stud -- (7stud)
on 2012-09-18 07:13
>
>(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.

2)
>> 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?
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.