From: Gary Wright Date: 2008-02-08T06:42:49+09:00 Subject: Re: info on block arguments 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