Newbie Question: Blocks and Parameters

D

Derek Michael

Hi Guys,

Please have patience with my possibly basic basic question. I am
learning Ruby and studying blocks/closures right now and confused
without being able to find an answer. I was studying and first came
across this example

[1,2,3].each {|x| puts "This is #{x}. "}


and this makes sense to me I believe. for each object (which is
integers 1, 2, 3), it runs the {} block code, passing in the integer
objects in as variable 'x', then puts out the string including the 'x'
variable.

Now what gets me confused is once I get to these following examples:

class MyClass
def command1()
yield(Time.now)
end
end

m = MyClass.new
m.command1() {|x| puts "Current time is #{x}"}

In the above example, how does 'x' get the Time.now value? In the first
code I have above, 'x' becomes the actual integer object (1, 2, and 3).
So in this case, how does the 'x' variable in this block code get the
Time.now value when executed through yield?

I hope my question even makes sense. I'm hoping by asking I can clarify
any mistaken ideas I have on blocks/closures.
 
W

Walton Hoops

-----Original Message-----
From: (e-mail address removed) [mailto:[email protected]]
Please have patience with my possibly basic basic question. I am
learning Ruby and studying blocks/closures right now and confused
without being able to find an answer. I was studying and first came
across this example

[1,2,3].each {|x| puts "This is #{x}. "}


and this makes sense to me I believe. for each object (which is
integers 1, 2, 3), it runs the {} block code, passing in the integer
objects in as variable 'x', then puts out the string including the 'x'
variable.

Now what gets me confused is once I get to these following examples:

class MyClass
def command1()
yield(Time.now)
end
end

m = MyClass.new
m.command1() {|x| puts "Current time is #{x}"}

In the above example, how does 'x' get the Time.now value? In the
first
code I have above, 'x' becomes the actual integer object (1, 2, and 3).
So in this case, how does the 'x' variable in this block code get the
Time.now value when executed through yield?

You can pass any function a block, whether it will use it, or not. In
order to execute the block passed to a function, that function must
call yield. The arguments to yield get passed in as arguments to the
block.

Going back to your first example. A possible implementation of .each

class Array
def each
x=0
while x<self.size
yield(self[x])
x+=1
end
end
end

Thus, in this example calling each calls yield passing in one at a time
each member of the array. Thus your block gets excuted once for each
member of the array. Does that help? I'm not sure how to explain it
better.
 
G

Gary Wright

=20
Now what gets me confused is once I get to these following examples:
=20
class MyClass
def command1()
yield(Time.now)
end
end
=20
m =3D MyClass.new
m.command1() {|x| puts "Current time is #{x}"}
=20
In the above example, how does 'x' get the Time.now value? In the = first
code I have above, 'x' becomes the actual integer object (1, 2, and = 3).
So in this case, how does the 'x' variable in this block code get the
Time.now value when executed through yield?

Your definition of command1 depends on an implicit rather than explicit =
block parameter. The implicit block parameter is called by the yield =
keyword. An alternative definition of command1 that uses an explicit =
block parameter would be:

class MyClass
def command1(&block)
block.call(Time.now)
end
end

There is a small penalty for using an explicit block parameter as the =
implicit block must be wrapped up in a Proc object and bound to the =
formal parameter rather remaining implicit. The implicit form is most =
often used when the block is invoked during the execution of the method =
whereas the explicit form is more common when the block is going to be =
stored to be called at some later time (such as with callbacks).

Gary Wright
 
D

Derek Michael

Walton said:
Going back to your first example. A possible implementation of .each

class Array
def each
x=0
while x<self.size
yield(self[x])
x+=1
end
end
end

Thus, in this example calling each calls yield passing in one at a time
each member of the array. Thus your block gets excuted once for each
member of the array. Does that help? I'm not sure how to explain it
better.

Thanks to the both of you for the explanations. I think I understand
now. I assume you mentioned the 'possible' implementation of .each
method to imply to me that the each method must have a yield in it since
it does something when you pass a block/closure, yes?
 
J

Jesús Gabriel y Galán

Walton said:
Going back to your first example. =A0A possible implementation of .each

class Array
=A0 def each
=A0 =A0 x=3D0
=A0 =A0 while x<self.size
=A0 =A0 =A0 yield(self[x])
=A0 =A0 =A0 x+=3D1
=A0 =A0 end
=A0 end
end

Thus, in this example calling each calls yield passing in one at a time
each member of the array. =A0Thus your block gets excuted once for each
member of the array. =A0Does that help? I'm not sure how to explain it
better.

Thanks to the both of you for the explanations. =A0I think I understand
now. =A0I assume you mentioned the 'possible' implementation of .each
method to imply to me that the each method must have a yield in it since
it does something when you pass a block/closure, yes?

Correct. The each method (which in MRI is probably implemented in C)
yields values to the block. Walton provided a possible implementation
in Ruby of the each method to show you the possible internal logic of
each: go through the elements of the array and yield them to the block
one at a time.

In Ruby all methods can receive a block. They can use it without
having to "declare" it if you use the yield keyword. If you want to
explicitly refer to it, you need to "declare" it in the argument list,
as Gary showed:

def m(arg1, arg2, &blk)
# do something with blk
end

As Gary said, you usually use the explicit version when you want to
store the block for later use, and use yield in the other cases. For
example:

class Callback
def initialize &blk
@callback =3D blk
end
def random
n =3D rand(100)
@callback.call(n)
end
end

irb(main):033:0> c =3D Callback.new {|rnd| puts "a random number is #{rnd}"=
}
=3D> #<Callback:0xb7d10f44 @callback=3D#<Proc:0xb7d11098@(irb):33>>
irb(main):034:0> c.random
a random number is 26
=3D> nil
irb(main):035:0> c.random
a random number is 73
=3D> nil

Calling the random method will use the block provided when
constructing the object as a callback.

Jesus.
 
D

Derek Michael

Thanks guys. I think I'm slowly wrapping my head around this.

Now sort of in the lines of the callback you mentioned, I started
reading about the & operator and being able to convert blocks to procs
and vice versa.

Here is an example of the code I was reviewing:

puts "----Define a method which takes block as a Proc object argument"
def my_method(count, &my_block)
value = 1

# Execute the block "count" times while updating the vlaue i
1.upto(count) do |i|
value = value * i
my_block.call(i, value)
end

end

puts "----Invoke the method passing a code block"
my_method(5) {|i, result| puts "my_method(#{i}) = #{result} "}


So in this case, the variable 'i' gets the value of variable 'count'
assigned to it when executing the upto method which invokes a yield? Am
I understanding this correctly? It took me a while looking at that code
again, to understand where the 'i' was getting it's value...
 
J

Jesús Gabriel y Galán

Thanks guys. =A0I think I'm slowly wrapping my head around this.

Now sort of in the lines of the callback you mentioned, I started
reading about the & operator and being able to convert blocks to procs
and vice versa.

Here is an example of the code I was reviewing:

puts "----Define a method which takes block as a Proc object argument"
def my_method(count, &my_block)
=A0value =3D 1

=A0# Execute the block "count" times while updating the vlaue i
=A01.upto(count) do |i|
=A0 =A0value =3D value * i
=A0 =A0my_block.call(i, value)
=A0end

end

puts "----Invoke the method passing a code block"
my_method(5) {|i, result| puts "my_method(#{i}) =3D #{result} "}


So in this case, the variable 'i' gets the value of variable 'count'
assigned to it when executing the upto method which invokes a yield? =A0A= m
I understanding this correctly? =A0It took me a while looking at that cod= e
again, to understand where the 'i' was getting it's value...

There are two 'i' in your code. The one inside my_method, which is the
block param for the block passed to the upto method, will get each
value from 1 to count in each iteration, because that is what upto
yields to the block. The 'i' outside, the one which is a block param
of the block passed to mymethod, will get the same value in each
iteration, since you are calling it with the same 'i' that upto
yields. So it will get 1,2,3,4 and 5.

The example is a little bit contrived, if you are wrapping your head
around yielding, blocks, etc. Try with simpler examples first:

def my_method(&my_block)
my_block.call(55)
end

my_method {|x| puts x} #=3D> 55

def my_method(&my_block)
1.upto(5) do |i|
my_block.call(i)
end
end

my_method {|number| puts number} #=3D> 1 2 3 4 5

BTW, the above are "equivalent" to this:

def my_method
yield 55
end

and

def my_method
1.upto(5) do |i|
yield i
end
end

which for this simple cases I prefer. I usually use the &blk form when
I want to store the block for later use.

Jesus.
 
D

Derek Michael

Thank you so much Jesus, and the others who commented. You all have
helped tremendously! I really didn't want to move onto RoR until I
completely understood the basics of Ruby and I think I'm getting there
finally!
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top