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