1 /************************************************
3 enumerator.c - provides Enumerator class
7 Copyright (C) 2001-2003 Akinori MUSHA
9 $Idaemons: /home/cvs/rb/enumerator/enumerator.c,v 1.1.1.1 2001/07/15 10:12:48 knu Exp $
10 $RoughId: enumerator.c,v 1.6 2003/07/27 11:03:24 nobu Exp $
13 ************************************************/
15 #include "ruby/ruby.h"
19 * Document-class: Enumerable::Enumerator
21 * A class which provides a method `each' to be used as an Enumerable
25 static VALUE sym_each
;
27 VALUE rb_eStopIteration
;
33 rb_block_call_func
*iter
;
40 enumerator_mark(void *p
)
42 struct enumerator
*ptr
= p
;
44 rb_gc_mark(ptr
->args
);
49 static struct enumerator
*
50 enumerator_ptr(VALUE obj
)
52 struct enumerator
*ptr
;
54 Data_Get_Struct(obj
, struct enumerator
, ptr
);
55 if (RDATA(obj
)->dmark
!= enumerator_mark
) {
56 rb_raise(rb_eTypeError
,
57 "wrong argument type %s (expected Enumerable::Enumerator)",
58 rb_obj_classname(obj
));
61 rb_raise(rb_eArgError
, "uninitialized enumerator");
68 * obj.to_enum(method = :each, *args)
69 * obj.enum_for(method = :each, *args)
71 * Returns Enumerable::Enumerator.new(self, method, *args).
77 * enum = str.enum_for(:each_byte)
78 * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
80 * # protects an array from being modified
82 * some_method(a.to_enum)
86 obj_to_enum(int argc
, VALUE
*argv
, VALUE obj
)
88 VALUE meth
= sym_each
;
94 return rb_enumeratorize(obj
, meth
, argc
, argv
);
98 each_slice_i(VALUE val
, VALUE
*memo
)
102 long size
= (long)memo
[1];
104 rb_ary_push(ary
, val
);
106 if (RARRAY_LEN(ary
) == size
) {
108 memo
[0] = rb_ary_new2(size
);
116 * e.each_slice(n) {...}
118 * Iterates the given block for each slice of <n> elements.
121 * (1..10).each_slice(3) {|a| p a}
130 enum_each_slice(VALUE obj
, VALUE n
)
132 long size
= NUM2LONG(n
);
135 if (size
<= 0) rb_raise(rb_eArgError
, "invalid slice size");
136 RETURN_ENUMERATOR(obj
, 1, &n
);
137 args
[0] = rb_ary_new2(size
);
138 args
[1] = (VALUE
)size
;
140 rb_block_call(obj
, SYM2ID(sym_each
), 0, 0, each_slice_i
, (VALUE
)args
);
143 if (RARRAY_LEN(ary
) > 0) rb_yield(ary
);
149 each_cons_i(VALUE val
, VALUE
*memo
)
153 long size
= (long)memo
[1];
155 if (RARRAY_LEN(ary
) == size
) {
158 rb_ary_push(ary
, val
);
159 if (RARRAY_LEN(ary
) == size
) {
160 v
= rb_yield(rb_ary_dup(ary
));
169 * Iterates the given block for each array of consecutive <n>
173 * (1..10).each_cons(3) {|a| p a}
186 enum_each_cons(VALUE obj
, VALUE n
)
188 long size
= NUM2LONG(n
);
191 if (size
<= 0) rb_raise(rb_eArgError
, "invalid size");
192 RETURN_ENUMERATOR(obj
, 1, &n
);
193 args
[0] = rb_ary_new2(size
);
194 args
[1] = (VALUE
)size
;
196 rb_block_call(obj
, SYM2ID(sym_each
), 0, 0, each_cons_i
, (VALUE
)args
);
202 enumerator_allocate(VALUE klass
)
204 struct enumerator
*ptr
;
205 return Data_Make_Struct(klass
, struct enumerator
,
206 enumerator_mark
, -1, ptr
);
210 enumerator_each_i(VALUE v
, VALUE enum_obj
, int argc
, VALUE
*argv
)
212 return rb_yield_values2(argc
, argv
);
216 enumerator_init(VALUE enum_obj
, VALUE obj
, VALUE meth
, int argc
, VALUE
*argv
)
218 struct enumerator
*ptr
= enumerator_ptr(enum_obj
);
221 ptr
->meth
= rb_to_id(meth
);
222 ptr
->iter
= enumerator_each_i
;
223 if (argc
) ptr
->args
= rb_ary_new4(argc
, argv
);
226 ptr
->no_next
= Qfalse
;
233 * Enumerable::Enumerator.new(obj, method = :each, *args)
235 * Creates a new Enumerable::Enumerator object, which is to be
236 * used as an Enumerable object using the given object's given
237 * method with the given arguments.
242 * enum = Enumerable::Enumerator.new(str, :each_byte)
243 * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
247 enumerator_initialize(int argc
, VALUE
*argv
, VALUE obj
)
249 VALUE recv
, meth
= sym_each
;
252 rb_raise(rb_eArgError
, "wrong number of argument (0 for 1)");
258 return enumerator_init(obj
, recv
, meth
, argc
, argv
);
263 enumerator_init_copy(VALUE obj
, VALUE orig
)
265 struct enumerator
*ptr0
, *ptr1
;
267 ptr0
= enumerator_ptr(orig
);
269 /* Fibers cannot be copied */
270 rb_raise(rb_eTypeError
, "can't copy execution context");
272 ptr1
= enumerator_ptr(obj
);
274 ptr1
->obj
= ptr0
->obj
;
275 ptr1
->meth
= ptr0
->meth
;
276 ptr1
->iter
= ptr0
->iter
;
277 ptr1
->args
= ptr0
->args
;
284 rb_enumeratorize(VALUE obj
, VALUE meth
, int argc
, VALUE
*argv
)
286 return enumerator_init(enumerator_allocate(rb_cEnumerator
), obj
, meth
, argc
, argv
);
293 * Iterates the given block using the object and the method specified
294 * in the first place.
298 enumerator_each(VALUE obj
)
300 struct enumerator
*e
;
304 if (!rb_block_given_p()) return obj
;
305 e
= enumerator_ptr(obj
);
307 argc
= RARRAY_LEN(e
->args
);
308 argv
= RARRAY_PTR(e
->args
);
310 return rb_block_call(e
->obj
, e
->meth
, argc
, argv
, e
->iter
, (VALUE
)e
);
314 enumerator_with_index_i(VALUE val
, VALUE
*memo
)
316 val
= rb_yield_values(2, val
, INT2FIX(*memo
));
323 * e.with_index {|(*args), idx| ... }
325 * Iterates the given block for each elements with an index, which
330 enumerator_with_index(VALUE obj
)
332 struct enumerator
*e
= enumerator_ptr(obj
);
337 RETURN_ENUMERATOR(obj
, 0, 0);
339 argc
= RARRAY_LEN(e
->args
);
340 argv
= RARRAY_PTR(e
->args
);
342 return rb_block_call(e
->obj
, e
->meth
, argc
, argv
,
343 enumerator_with_index_i
, (VALUE
)&memo
);
347 next_ii(VALUE i
, VALUE obj
, int argc
, VALUE
*argv
)
349 rb_fiber_yield(argc
, argv
);
354 next_i(VALUE curr
, VALUE obj
)
356 struct enumerator
*e
= enumerator_ptr(obj
);
359 rb_block_call(obj
, rb_intern("each"), 0, 0, next_ii
, obj
);
361 return rb_fiber_yield(1, &nil
);
365 next_init(VALUE obj
, struct enumerator
*e
)
367 VALUE curr
= rb_fiber_current();
369 e
->fib
= rb_fiber_new(next_i
, obj
);
376 * Returns the next object in the enumerator, and move the internal
377 * position forward. When the position reached at the end, internal
378 * position is rewinded then StopIteration is raised.
380 * Note that enumeration sequence by next method does not affect other
381 * non-external enumeration methods, unless underlying iteration
382 * methods itself has side-effect, e.g. IO#each_line.
387 enumerator_next(VALUE obj
)
389 struct enumerator
*e
= enumerator_ptr(obj
);
391 curr
= rb_fiber_current();
393 if (!e
->fib
|| !rb_fiber_alive_p(e
->fib
)) {
397 v
= rb_fiber_resume(e
->fib
, 1, &curr
);
402 rb_raise(rb_eStopIteration
, "iteration reached at end");
411 * Rewinds the enumeration sequence by the next method.
415 enumerator_rewind(VALUE obj
)
417 struct enumerator
*e
= enumerator_ptr(obj
);
426 Init_Enumerator(void)
428 rb_define_method(rb_mKernel
, "to_enum", obj_to_enum
, -1);
429 rb_define_method(rb_mKernel
, "enum_for", obj_to_enum
, -1);
431 rb_define_method(rb_mEnumerable
, "each_slice", enum_each_slice
, 1);
432 rb_define_method(rb_mEnumerable
, "each_cons", enum_each_cons
, 1);
434 rb_cEnumerator
= rb_define_class_under(rb_mEnumerable
, "Enumerator", rb_cObject
);
435 rb_include_module(rb_cEnumerator
, rb_mEnumerable
);
437 rb_define_alloc_func(rb_cEnumerator
, enumerator_allocate
);
438 rb_define_method(rb_cEnumerator
, "initialize", enumerator_initialize
, -1);
439 rb_define_method(rb_cEnumerator
, "initialize_copy", enumerator_init_copy
, 1);
440 rb_define_method(rb_cEnumerator
, "each", enumerator_each
, 0);
441 rb_define_method(rb_cEnumerator
, "with_index", enumerator_with_index
, 0);
442 rb_define_method(rb_cEnumerator
, "next", enumerator_next
, 0);
443 rb_define_method(rb_cEnumerator
, "rewind", enumerator_rewind
, 0);
445 rb_eStopIteration
= rb_define_class("StopIteration", rb_eIndexError
);
447 sym_each
= ID2SYM(rb_intern("each"));
449 rb_provide("enumerator.so"); /* for backward compatibility */