Writing a method to handle a code block?

B

Brian Ross

[Note: parts of this message were removed to make it a legal post.]

From Beginning Ruby:

def each_vowel(&code_block)
%w{a e i o u}.each { |vowel| code_block.call(vowel) }
end
each_vowel { |vowel| puts vowel }

I am trying to figure out how that works but I'm still having a bit of
trouble. Could someone break it down bit by bit to show what it's doing?

def each_vowel(&code_block)

It defines a method that takes a code block (is the & necessary?). What does
it mean to have a method that takes a code block?

%w{a e i o u}.each { |vowel| code_block.call(vowel) }

Then it takes an array of vowels, which call the each method to pass each
one into the following block through |vowel| as a block argument. The block
arguments are then called by the variable code_block (I don't understand
this).

Brian
 
A

Adam Shelly

From Beginning Ruby:

def each_vowel(&code_block)
%w{a e i o u}.each { |vowel| code_block.call(vowel) }
end
each_vowel { |vowel| puts vowel }

I am trying to figure out how that works but I'm still having a bit of
trouble. Could someone break it down bit by bit to show what it's doing?

def each_vowel(&code_block)

It defines a method that takes a code block (is the & necessary?).

Yes the & is necessary, otherwise when you try to call this method
with `each_vowel {|v|puts v}` you will get an argument error, since
the method will expect a normal object, not a block.
What does it mean to have a method that takes a code block?

At first approximation you can think of a code block as an anonymous method.
You are writing a method that can take another method as an argument.
%w{a e i o u}.each { |vowel| code_block.call(vowel) }

Then it takes an array of vowels, which call the each method to pass each
one into the following block through |vowel| as a block argument. The block
arguments are then called by the variable code_block (I don't understand
this).

code_block.call(vowel) is calling the method stored in the code_block
variable, and
passing it the argument called vowel. When you call each_vowel like this:

each_vowel {|vowel| puts vowel}

The block with 'puts' is stored as code_block, and run 5 times, once
for each letter in the array:
%w{a e i o u}.each { |vowel| code_block.call(vowel) }

So the end result is that each vowel is printed out.


HTH,
-Adam
 
M

Michael Morin

Brian said:
From Beginning Ruby:

def each_vowel(&code_block)
%w{a e i o u}.each { |vowel| code_block.call(vowel) }
end
each_vowel { |vowel| puts vowel }

I am trying to figure out how that works but I'm still having a bit of
trouble. Could someone break it down bit by bit to show what it's doing?

def each_vowel(&code_block)

It defines a method that takes a code block (is the & necessary?). What does
it mean to have a method that takes a code block?

%w{a e i o u}.each { |vowel| code_block.call(vowel) }

Then it takes an array of vowels, which call the each method to pass each
one into the following block through |vowel| as a block argument. The block
arguments are then called by the variable code_block (I don't understand
this).

Brian

Code (in the form of Proc objects and similar) can be stored in a variable.
test
=> nil
The &argument to each_vowel is a local variable that stores any block or
closure passed to it using the special method() { syntax }. After that,
calling argument.call is the same as code.call in the previous example.

I got a little turned around in your question, either I'm just not
understanding it or you're using a term wrong. The each method itself
takes a block, the block is everything in the { curly braces }. The
argument to the block is |vowel|, for every vowel in the array, the code
block passed to each is called with that vowel passing it in |vowel|.
That code block then goes an calls the code_block argument, which in
turn contains the code block given when you called each_vowel.

I can see how that explanation gets a little confusing. Two things are
called vowel, there are three things called a "code block," and a few
layers of calls through the code blocks. This example may be simpler.
It duplicated the Fixnum#times method.
test
test
test
test
test
test
test
test
test
test
=> 10
--
Michael Morin
Guide to Ruby
http://ruby.about.com/
Become an About.com Guide: beaguide.about.com
About.com is part of the New York Times Company
 
B

Brian Ross

[Note: parts of this message were removed to make it a legal post.]

code_block.call(vowel) is calling the method stored in the code_block
variable, and
passing it the argument called vowel. When you call each_vowel like this:

each_vowel {|vowel| puts vowel}

The block with 'puts' is stored as code_block, and run 5 times, once
for each letter in the array:
%w{a e i o u}.each { |vowel| code_block.call(vowel) }

So the end result is that each vowel is printed out.

Intuitively I'd think that:

each_vowel {|vowel| puts vowel}

would lead to something that looked like:

%w{a e i o u}.each { |vowel| |vowel| puts vowel.call(vowel) }

which is incomprehensible to me. I guess I am trying to really figure out
how it's working so that I can construct my own and really understand it. To
sound like an idiot: I still don't really understand how it's working.

Brian
 
B

Brian Ross

[Note: parts of this message were removed to make it a legal post.]

I guess I am trying to really figure out
how it's working so that I can construct my own and really understand it.
To
sound like an idiot: I still don't really understand how it's working.

Alternatively, if I just run

puts each_vowel {}

it returns 5 vowels, each on its own line and seems to function the same as:

each_vowel {|vowel| puts vowel}

Maybe my question is, what is the importance of the second |vowel|? Does the
first block argument just get passed to the second block through whichever
block argument is given in the second? Also I'm having trouble with the
documentation for the call method to figure out how it works.

Brian
 
A

Adam Shelly

Intuitively I'd think that:

each_vowel {|vowel| puts vowel}

would lead to something that looked like:

%w{a e i o u}.each { |vowel| |vowel| puts vowel.call(vowel) }

which is incomprehensible to me. I guess I am trying to really figure out
how it's working so that I can construct my own and really understand it. To
sound like an idiot: I still don't really understand how it's working.
I'm probably not the best explainer... but you are on the right track.
Michael is right that having two things called vowel here can be
confusing. So let's rename the variables - the code will work
exactly the same:

def each_vowel &code_block
%w{a e i o u}.each {|item| code_block.call(item) }
end
each_vowel {|v| puts v}

code_block.call(item) simply executes the block using 'item' in place
of the variable inside the || pipes, so conceptually, this 'expands'
to the following pseudo-code:

%w{a e i o u}.each { |item| {|v=item| puts v}}

Alternatively, if I just run
puts each_vowel {}
it returns 5 vowels, each on its own line and seems to function the same as:
each_vowel {|vowel| puts vowel}

Something completely different is happening with `puts each_vowel{}`.
Remember that Ruby methods return the result of the last line, which
for each_vowel, is the result of Array.each, which is the array
itself. So after executing an empty block, which does nothing, you
are returning the array and passing that to puts. To see the
difference, compare to :
each_vowel{|v| p v.succ}

-Adam
 
D

David Masover

From Beginning Ruby:

def each_vowel(&code_block)
%w{a e i o u}.each { |vowel| code_block.call(vowel) }
end
each_vowel { |vowel| puts vowel }

First, I'd like to show a simplified version of it. I think this works:

def each_vowel
%w{a e i o u}.each { |vowel| yield vowel }
end

In the simplest form, to define a method that takes a code block, you can
ignore the block until you need it, and then call it with "yield". This will
actually execute faster, but it's not as flexible.

You can also call 'block_given?' to find out if you have a block.

So...
It defines a method that takes a code block (is the & necessary?).

Strictly, no.
What does
it mean to have a method that takes a code block?

ALL methods can take a code block. Most of them don't do anything with it. You
can verify this:

"foo".length {|x| raise "THIS BLOCK SHOULD NEVER BE CALLED!!!" }

So, with that in mind, the &foo says that you're binding whatever code block
was passed in to a local variable, so you can do things to it.
%w{a e i o u}.each { |vowel| code_block.call(vowel) }

%w{a e i o u}, of course, translates to ['a', 'e', 'i', 'o', 'u']

The rest is a simple each loop. You could also do this:

['a', 'e', 'i', 'o', 'u'].each do |vowel|
puts 'I got a vowel!'
puts vowel
end

That block runs once for each vowel. To make it simpler, you could disregard
all the less common vowels, and just use 'e':

def each_important_vowel(&code_block)
code_block.call('e')
end


If you're using it like in your example:

each_vowel {|vowel| puts vowel}

The easiest thing to do is to think of the code block as a function in its own
right. (It's not, which is one of the more disappointing things about Ruby,
but we can pretend that it is.)

So, it's really more like this:

def my_code_block(vowel)
puts vowel
end
%w{a e i o u}.each {|vowel| my_code_block(vowel) }



If you made it through that, I'd like to justify my claim that yielding isn't
as flexible as the &block syntax. I'd say, yield when you can, and use &block
when you need to.

One example would be stored callbacks. I'm not sure what you've done with
classes and objects, so I'll simplify this:

before_printing_callbacks = []
def before_printing &block
before_printing_callbacks.push(block)
end

# Call it a few times, with various arguments...
# Then take a look at what's in that array.

def print_stuff string
before_printing_callbacks.each do |block|
block.call
end
print string
end
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top