Reputation: 6193
I'm curious about the mechanism of collection and block in Ruby. We can define a class like this:
class Foo
include Enumerable
def initialize
@data = []
end
def each
if block_given?
@data.each { |e| yield(e) }
else
Enumerator.new(self, :each)
end
end
end
I want to know how @data.each
can use the block { |e| yield(e) }
as the parameter. I searched the implementation of Array#each:
rb_ary_each(VALUE array)
{
long i;
volatile VALUE ary = array;
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(RARRAY_AREF(ary, i));
}
return ary;
}
but this doesn't even seem like a Ruby language implementation. How is Array#each
implemented in Ruby?
Edit:
It looks like C code traverses the first to final array element and call ruby yield function with the parameter of the traversed array element. But where is the block passed to rb_ary_each
function?
Upvotes: 4
Views: 3671
Reputation: 1172
I may come late to this topic, but I guess that VALUE array is a object/struct instead of an primitive value as int, char, etc. So, what rb_ary_each method is receiving is a pointer/reference to the object/struct of the type VALUE. If that is true (I'm just guessing here for what I know about C) then the block is passed inside the object/struct with the remain arguments.
I'm also guessing that another object/struct should exist to hold the block/proc/lambda, so the VALUE object/struct should hold a pointer/reference to the first one.
If I'm wrong, please, someone correct me! Your question are much more related to how C works than Ruby.
Upvotes: 0
Reputation: 48368
Here's a simple Ruby implementation of an each
-like method that doesn't rely on the existence of any of Ruby's built-in iterator methods:
class List
def initialize(arr)
@arr = arr
end
def each
i = 0
while i < @arr.length
yield @arr[i]
i += 1
end
end
end
Usage:
l = List.new(['a', 'b', 'c'])
l.each {|x| puts x}
Output:
a
b
c
Upvotes: 4