Reputation: 1678
Here is an example of scan:
"abcdeabcabc".scan("a")
So it returns an array of 3 a's. Another example of scan:
"abcdeabcabc".scan("a") {|x| puts x}
which just output each "a", but still output an array, and this time it is actually the original string that it returns.
So from the documentation and the behavior above, the scan either returns an array (no block is given), or returns the original string before which some side-effects take place. The point is that both cases returns something.
Then what will happen if I put a "yield" inside of the block? What will be returned? Or, none? What will be the type of the return value?
"abcdeabcabc".scan("a") {|x| yield x}
The above will not work as Ruby complains that no block is given. Which makes some sense to me. But if it is part of a class method, say, self-implemented "each", then the following works:
class Test
def my_each
"abcdeabcabc".scan("a") {|x| yield x}
end
end
# => :my_each
t = Test.new
# => #<Test:0x007ff00a8d79b0>
t.my_each {|x| puts "so this is #{x}"}
# it works. Outpus 3 a's then return the original string.
So, what is the return value of the my_each method of Test class? Is that a list of yield's or something? But as discussed before "abcdeabcabc".scan("a") {|x| yield x} segment will be complained by Ruby until a block is given. What happened internally to give the block of my_each to the segment inside of my_each implementation?
Upvotes: 5
Views: 4874
Reputation: 9485
The block is passed similarly to the argument of that function. This can be specified explicitly, like so:
class Test
def my_each(&block)
"abcdeabcabc".scan("a") do |x|
puts "!!! block"
yield x
# Could be replaced with: block.call(x)
end
end
end
Technically, it's exactly the same (puts
put in there for clarification), its presence is not checked the way it is usually done for arguments. Should you forget to give it a block, the function will halt on the first yield
it has to execute with exactly the same LocalJumpError
(at least, that's what I get on Rubinius). However, notice the "!!! block" in the console before it happens.
It works like that for a reason. You could check whether your function is given a block, if it is specified explicitly as above, using if block
, and then skip the yield
s. A good example of that is a content_tag
helper for Rails. Calls of this helper can be block-nested. A simplistic example:
content_tag :div do
content_tag :div
end
...to produce output like:
<div>
<div></div>
</div>
So, the block is executed "on top" (in terms of call stack) of your method. It is called each time a yield
happens as some sort of function call on a block. It's not accumulated anywhere to execute the block afterwards.
UPD:
The Enumerator
returned by many each
es is explicitly constructed by many iterators to save context of what should happen.
It could be implemented like this on my_each
:
class Test
def my_each(&block)
if block
"abcdeabcabc".scan("a") { |x| yield x }
else
Enumerator.new(self, :my_each)
end
end
end
Upvotes: 4
Reputation: 118261
"abcdeabcabc".scan("a") {|x| yield x}
In the above case, #scan
is passing the each matched character to the block associated to it.
Now inside the block of #scan
, you are calling yield
, which actually then calling the block you passed to the method my_each
. Value of x
is passed to the block you passed with the method my_each
call.
It is too simple, no confusions.
what is the return value of the my_each method of Test class?
As per your current code, the return value should be the #scan
method return value, which in turn causes the result of the last statement of the block associated with the method #my_each
(if called) or the receiver on which you called the method #scan
.
Is that a list of yield's or something?
Yes, yield
will be called, as many matches will be found by the #scan
method.
Consider the below example :-
"abcdeabcabc".scan("w") {|x| yield x}
Here the block associated with the method #scan
will not be called, as #scan
didn't find any match, that's why yield
wouldn't be called also and as a result method #my_each
wouldn't output block expression(passed with the method) result, but "abcdeabcabc"
.
Upvotes: 1
Reputation: 168081
Since a block is given to scan
, the original string is returned. It does not matter what is done inside the block.
Upvotes: 1