[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

info on block arguments

Russell Me

2/7/2008 2:26:00 AM

I'm trying to pick up ruby and I'm impressed by all the cool stuff it
does out of the box. I'm reading a few books but i'm finding why's
poignant guide to ruby to be the most helpful and entertaining
(http://poignantguid...).

However, I'm a bit stuck understanding how block arguments work. in his
example:

kitty_toys = [
{:shape => 'sock', :fabric => 'cashmere'},
{:shape => 'mouse', :fabric => 'calico'},
{:shape => 'eggroll', :fabric => 'chenille'}
]

kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end

I know what it does. I put it through irb and it works. But i don't
understand HOW it works, exactly. Mainly, the toy bit. I don't quite get
what role it plays in the code and how the code uses it.

I've read up online and in both books I have, but, for whatever reason i
STILL cannot grasp block arguments and what role they play.

Maybe I'm just brain farting here, but if anyone could lend some tips on
this I'd appreciate it.
--
Posted via http://www.ruby-....

25 Answers

Jano Svitok

2/7/2008 2:51:00 AM

0

On Feb 7, 2008 3:25 AM, Russell Me <russ@geekwhiz.com> wrote:
> I'm trying to pick up ruby and I'm impressed by all the cool stuff it
> does out of the box. I'm reading a few books but i'm finding why's
> poignant guide to ruby to be the most helpful and entertaining
> (http://poignantguid...).
>
> However, I'm a bit stuck understanding how block arguments work. in his
> example:
>
> kitty_toys = [
> {:shape => 'sock', :fabric => 'cashmere'},
> {:shape => 'mouse', :fabric => 'calico'},
> {:shape => 'eggroll', :fabric => 'chenille'}
> ]
>
> kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
> puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
> end
>
> I know what it does. I put it through irb and it works. But i don't
> understand HOW it works, exactly. Mainly, the toy bit. I don't quite get
> what role it plays in the code and how the code uses it.
>
> I've read up online and in both books I have, but, for whatever reason i
> STILL cannot grasp block arguments and what role they play.
>
> Maybe I'm just brain farting here, but if anyone could lend some tips on
> this I'd appreciate it.

As you may know, "toy" is the argument to block. Block is a special
form of function,
and using this |argument| the function sort_by passes data to the block.

It makes more sense if you put yield in the picture as well. Let's see
how e.g. upto is implemented:

class Integer
def upto_(limit)
counter = self
while counter <= limit
yield counter
counter += 1
end
end
end

In other words: we do something, and in special places we call the
block function (yield). We pass
the counter as the block argument.

Now we can try the outer view of this thing -- we'll see the block
being called with various numbers:

10.upto_(20) { |counter| puts counter }

And back to your/_why's example: it's a chain of several method calls:

1. we call sort_by with { |toy| toy[:shape] } block on kitty_toys
2. then we call each with do ... end block on the result of step 1.

sort_by uses the block to generate sort index for each item in the
array. The block gets an item,
and returns the index for that item. (in this case, we want to sort_by
toy's :shape)
sort_by returns sorted array.

Then we call each on the result array. each calls the block with each
item one by one.
So, the do...end block gets called 3 times, once for each item. That
block just prints the item,
as you have noticed.

So, this is my attempt to explain. If it doesn't make sense, maybe
it's because it's 4 AM here ;-)

Jano

Siep Korteling

2/7/2008 11:43:00 AM

0

Russell Me wrote:
> I'm trying to pick up ruby and I'm impressed by all the cool stuff it
> does out of the box. I'm reading a few books but i'm finding why's
> poignant guide to ruby to be the most helpful and entertaining
> (http://poignantguid...).
>
> However, I'm a bit stuck understanding how block arguments work. in his
> example:
>
> kitty_toys = [
> {:shape => 'sock', :fabric => 'cashmere'},
> {:shape => 'mouse', :fabric => 'calico'},
> {:shape => 'eggroll', :fabric => 'chenille'}
> ]
>
> kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
> puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
> end
>
> I know what it does. I put it through irb and it works. But i don't
> understand HOW it works, exactly. Mainly, the toy bit. I don't quite get
> what role it plays in the code and how the code uses it.
>
> I've read up online and in both books I have, but, for whatever reason i
> STILL cannot grasp block arguments and what role they play.
>
> Maybe I'm just brain farting here, but if anyone could lend some tips on
> this I'd appreciate it.

I think of a block as Cookiemonster from Sesame street. When you throw
something at him, he will name it "cookie" and then eat it. In this case
you throw each element of kitty_toys, the block will call them all "toy"
and process them.
--
Posted via http://www.ruby-....

ThoML

2/7/2008 1:28:00 PM

0

> I think of a block as Cookiemonster from Sesame street. When you throw
> something at him, he will name it "cookie" and then eat it. In this case
> you throw each element of kitty_toys, the block will call them all "toy"
> and process them.

Unfortunately, my knowledge of Sesame Street is rather limited. But I
think you're forgetting something. I'm quite sure there is something
or someone sitting between you and the Cookiemonster, let's call it/
him/her Sorty. Sorty takes all the kitty_toys from you and hands one
toy after the other over to the Cookiemonster. So the Cookiemonster
doesn't get them all at once but only one at a time. That's because it
is a well educated Cookiemonster that doesn't eat two cookies at a
time. Sorty then takes the toys back from the Cookiemonster and hands
them over to Mrs. Each.

Nice analogy.

Russell Me

2/7/2008 8:56:00 PM

0

tho_mica_l wrote:
>> I think of a block as Cookiemonster from Sesame street. When you throw
>> something at him, he will name it "cookie" and then eat it. In this case
>> you throw each element of kitty_toys, the block will call them all "toy"
>> and process them.
>
> Unfortunately, my knowledge of Sesame Street is rather limited. But I
> think you're forgetting something. I'm quite sure there is something
> or someone sitting between you and the Cookiemonster, let's call it/
> him/her Sorty. Sorty takes all the kitty_toys from you and hands one
> toy after the other over to the Cookiemonster. So the Cookiemonster
> doesn't get them all at once but only one at a time. That's because it
> is a well educated Cookiemonster that doesn't eat two cookies at a
> time. Sorty then takes the toys back from the Cookiemonster and hands
> them over to Mrs. Each.
>
> Nice analogy.

Thank you both for your help. it's making more sense but i imagine it
should make total sense to me, no?

Still not 100% though about it and I certainly hope this isn't a preview
of things to come. Things have been going well until i hit this simple
roadblock of understanding.

>>>kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
>>> puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
>>> end

ok, so i see that a sort is being performed on kitty toys with
"kitty_toys.sort_by". but what's the next bit about? namely: { |toy|
toy[:shape] }.each do |toy| I see that the sort is based on the shape
from toy[:shape] but how? and what is the "|toy|" before it doing? and
then i assume that the .each means do it for all the items, right? and
then... for each toy, output the puts bit? (which then get's sorted by
the sort_by)?

Sorry to be a bother with something so trivial.



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

Gary Wright

2/7/2008 9:43:00 PM

0


On Feb 7, 2008, at 3:56 PM, Russell Me wrote:
>
>>>> kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
>>>> puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
>>>> end
>
> ok, so i see that a sort is being performed on kitty toys with
> "kitty_toys.sort_by". but what's the next bit about? namely: { |toy|
> toy[:shape] }.each do |toy| I see that the sort is based on the
> shape
> from toy[:shape] but how?

One thing to keep in mind that the 'purpose' of a block depends
entirely on the method that is being called. Syntactically a block
is a block is a block but the semantics depend entirely on how the
method is utilizing the block.

3.times {|x| puts x } # 0 1 2

3 is the object
times is the method
{|x| puts x } is the block associated with the call to times.

In this case, 3.times, 'yields' control to the block 3 times. The
first time, 0 is passed as the argument. The second time 1 is passed
as the argument. The third time 2 is passed as the argument.

The times method doesn't have a clue as to what the block is going to
do. It just knows to yield control to the block three times.

sum = 0
3.times { |x| sum = sum + x }
puts sum

In this example the block is summing the arguments, 'times' doesn't
know that is what the block is doing, it just does its job of
iteration and lets the block do what it wants.

Back to your original example, with a twist

kitty_toys.sort { |t1, t2| t1[:shape] <=> t2[:shape] }

Any sorting algorithm needs to know how to compare two items. By
picking a different comparison procedure you can cause a collection
to be sorted in different ways. From the sorting algorithm's
perspective nothing changes other than the manner by which any two
items are 'compared'.

In this example with 'sort' (instead of sort_by) it is clear that the
block is given two toys and returns the verdict as to how the two
toys 'compare' (by using the comparison operator on the shape
property of the two toys). As sort is making its way through the
collection, it will periodically yield control to the block when it
needs to compare two items. The sort method really doesn't know
anything at all about the contents of the block it just expects the
block to return -1, 0, or 1 and goes from there.

Switching to sort_by

kitty_toys.sort_by { |toy| toy[:shape] }

This is a slight variation of sort. Instead of the block providing a
way of comparing two elements of the collection, the block is
expected to return the 'comparable' characteristic of the element.
So sort_by runs through the entire collection calling the block for
each element and remembering what the block returns for each element.
Now sort_by begins sorting the collection just like 'sort' but this
time instead calling out to a comparison block it makes comparisons
by using the comparable 'characteristic' that it collected during its
first pass through the collection.

The decision to use sort or sort_by really depends on how expensive
it is to do the comparison.
Sometimes it makes sense to pre-compute the comparison
characteristic, sometimes it doesn't make sense.

What I'm trying to point out is that in order to understand a block
in any particular situation you must look at the documentation for
the method that is being called. A method may ignore a block, save
it for use later on (like a callback), call it once, or call it many
times. The only way to know how the block is being used is to look
at the documentation for that particular method.

Think of a block as just another argument to a method. It has its
own syntax but it really is just another argument that the method can
use as it wants just as with any 'regular' argument.

Gary Wright

Siep Korteling

2/7/2008 10:05:00 PM

0

Russell Me wrote:

>>>>kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
>>>> puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
>>>> end
>
> ok, so i see that a sort is being performed on kitty toys with
> "kitty_toys.sort_by". but what's the next bit about? namely: { |toy|
> toy[:shape] }.each do |toy| I see that the sort is based on the shape
> from toy[:shape] but how? and what is the "|toy|" before it doing? and
> then i assume that the .each means do it for all the items, right? and
> then... for each toy, output the puts bit? (which then get's sorted by
> the sort_by)?
>
> Sorry to be a bother with something so trivial.

Read the code from left to right -Ruby does too.
"sort_by" knows how to sort, but it does not know what to sort on.
The block { |toy| toy[:shape] } is a simple machine. It gets each
element of Kitty_toys and immediatly puts a label on it, by means of
"|toy|".(or |Cookie| or whatever). Then it goes to work on this toy- It
gets the :shape from it, and gives it to sort_by. sort_by does it's
magick, which Gary Wright explains better than I can.

The result is an array, sorted on shape.

This (nameless) array is also iterated, this time with .each . This
method .each does not know anything, it just executes the block for each
element . The block again labels each element with "|toy|" . This not
the same thing as the "toy" in de first block, which is finished and
does not exist anymore anyway. "|sorted_toy|" might have been more
clear. Then it goes to work on it:

puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
in which "Puts"does the printing. If it encounters #{ toy[:shape] }
puts pauses the printing and ruby executes what's in the curly braces.
That's it.

If you add "p kitty_toys" at the end of the code you'll see that
kitty_toys is still unsorted. The unnamed sorted array has done it's
work and disappeared.

Regards,

Siep
--
Posted via http://www.ruby-....

ThoML

2/7/2008 10:19:00 PM

0


Okay, I saw I was too slow. So I only add something to the following
point:

> but what's the next bit about? namely: { |toy|
> toy[:shape] }.each do |toy|

Let's break it up. If you don't understand what's going, break the
code
in smaller pieces and step through it. Or test it in irb.

The each{} part is a totally different story.

a.sort_by {|n| n}.each {|n| p n}

actually should be read rather like:

a1 = a.sort_by {|n| n}
a2 = a1.each {|n| n}

The period joins these two methods into a chain so that each()
operates
directly on the value of sort_by(), which happens to be the sorted
array.

Or to stick with the Sesame Street'ish analogy: Mrs Each doesn't care
about the Cookiemonster. That's Sorty who sees the Cookiemonster. But
Mrs. Each is a grown-up, she cannot see Cookiemonster and thinks such
things are just childish phantasies.

Day

2/7/2008 10:46:00 PM

0

I think it's kind of funny no one's pointed this out yet. The anatomy
of a block:
{ |argument, list| code.to(execute) }

A block is built a little like a method, though it has no name and
only exists while it is executing (as others has said). The bits
between pipes are like the argument list in a method and the bits
after that are to be run. Maybe this will help:

def some_method(argument, list)
code.to(execute)
end

See the similarity? Now, the reason the semantics change based on what
method the block is attached to is because each method (not THE each
method) passes a different thing (or set of things) as arguments.
Integer.times passes numbers (as illustrated above). Other methods
pass nothing at all. It's almost like... passing a little method to
another method as an argument, knowing that the method you're calling
will execute that code in a certain way (just like you know it'll do
other stuff to normal arguments you pass in) and give you back
something helpful.

I hope that's clear. Sometimes I get rambly and don't actually explain anything.


Ben

Russell Me

2/8/2008 5:08:00 AM

0

You've all contributed a great deal of information and I'm glad to be
receiving help versus "RTFM" comments.


kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end

So in this case (with the sort_by) I gather that a sorted array is being
thrown into |toy|, and then for each item in |toy| it's key and value
are printed out by the puts statement? does that about sum it up? is it
for each item in the array (that is, getting the number 3) or somehow
incrementing through?

and with this...

3.times {|x| puts x }

the output is 0,1,2 because... x is initially nil and a value of 1 is
returned (by... what?) to .times? is that right?

I see that everything uses block arguments differently, which is good to
know.

Anyway, I really do appreciate such a helpful group of folk. It's
encouraging. Thanks for all your input!

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

Day

2/8/2008 6:01:00 AM

0

On Feb 7, 2008 11:07 PM, Russell Me <russ@geekwhiz.com> wrote:
> kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
> puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
> end
>
> So in this case (with the sort_by) I gather that a sorted array is being
> thrown into |toy|, and then for each item in |toy| it's key and value
> are printed out by the puts statement? does that about sum it up? is it
> for each item in the array (that is, getting the number 3) or somehow
> incrementing through?

No. sort_by is taking the block and for each item in the array, it
puts that item into |toy|, and then grabs the :shape from it. it
knows, because of it's nature as sort_by that this is the value you
want compared between all the elements, and so it does so, and returns
the sorted array for .each to be called on. Does that make any sense?
Once you get this, I promise you will love the concept.


Ben