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
;
39 enumerator_mark(void *p
)
41 struct enumerator
*ptr
= p
;
43 rb_gc_mark(ptr
->args
);
48 static struct enumerator
*
49 enumerator_ptr(VALUE obj
)
51 struct enumerator
*ptr
;
53 Data_Get_Struct(obj
, struct enumerator
, ptr
);
54 if (RDATA(obj
)->dmark
!= enumerator_mark
) {
55 rb_raise(rb_eTypeError
,
56 "wrong argument type %s (expected %s)",
57 rb_obj_classname(obj
), rb_class2name(rb_cEnumerator
));
59 if (!ptr
|| ptr
->obj
== Qundef
) {
60 rb_raise(rb_eArgError
, "uninitialized enumerator");
67 * obj.to_enum(method = :each, *args)
68 * obj.enum_for(method = :each, *args)
70 * Returns Enumerable::Enumerator.new(self, method, *args).
76 * enum = str.enum_for(:each_byte)
77 * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
79 * # protects an array from being modified
81 * some_method(a.to_enum)
85 obj_to_enum(int argc
, VALUE
*argv
, VALUE obj
)
87 VALUE meth
= sym_each
;
93 return rb_enumeratorize(obj
, meth
, argc
, argv
);
97 each_slice_i(VALUE val
, VALUE
*memo
)
101 long size
= (long)memo
[1];
103 rb_ary_push(ary
, val
);
105 if (RARRAY_LEN(ary
) == size
) {
107 memo
[0] = rb_ary_new2(size
);
115 * e.each_slice(n) {...}
118 * Iterates the given block for each slice of <n> elements. If no
119 * block is given, returns an enumerator.
122 * (1..10).each_slice(3) {|a| p a}
131 enum_each_slice(VALUE obj
, VALUE n
)
133 long size
= NUM2LONG(n
);
136 if (size
<= 0) rb_raise(rb_eArgError
, "invalid slice size");
137 RETURN_ENUMERATOR(obj
, 1, &n
);
138 args
[0] = rb_ary_new2(size
);
139 args
[1] = (VALUE
)size
;
141 rb_block_call(obj
, SYM2ID(sym_each
), 0, 0, each_slice_i
, (VALUE
)args
);
144 if (RARRAY_LEN(ary
) > 0) rb_yield(ary
);
150 each_cons_i(VALUE val
, VALUE
*memo
)
154 long size
= (long)memo
[1];
156 if (RARRAY_LEN(ary
) == size
) {
159 rb_ary_push(ary
, val
);
160 if (RARRAY_LEN(ary
) == size
) {
161 v
= rb_yield(rb_ary_dup(ary
));
171 * Iterates the given block for each array of consecutive <n>
172 * elements. If no block is given, returns an enumerator.
175 * (1..10).each_cons(3) {|a| p a}
188 enum_each_cons(VALUE obj
, VALUE n
)
190 long size
= NUM2LONG(n
);
193 if (size
<= 0) rb_raise(rb_eArgError
, "invalid size");
194 RETURN_ENUMERATOR(obj
, 1, &n
);
195 args
[0] = rb_ary_new2(size
);
196 args
[1] = (VALUE
)size
;
198 rb_block_call(obj
, SYM2ID(sym_each
), 0, 0, each_cons_i
, (VALUE
)args
);
204 enumerator_allocate(VALUE klass
)
206 struct enumerator
*ptr
;
209 enum_obj
= Data_Make_Struct(klass
, struct enumerator
, enumerator_mark
, -1, ptr
);
216 enumerator_each_i(VALUE v
, VALUE enum_obj
, int argc
, VALUE
*argv
)
218 return rb_yield_values2(argc
, argv
);
222 enumerator_init(VALUE enum_obj
, VALUE obj
, VALUE meth
, int argc
, VALUE
*argv
)
224 struct enumerator
*ptr
;
226 Data_Get_Struct(enum_obj
, struct enumerator
, ptr
);
229 rb_raise(rb_eArgError
, "unallocated enumerator");
233 ptr
->meth
= rb_to_id(meth
);
234 if (argc
) ptr
->args
= rb_ary_new4(argc
, argv
);
237 ptr
->no_next
= Qfalse
;
244 * Enumerable::Enumerator.new(obj, method = :each, *args)
246 * Creates a new Enumerable::Enumerator object, which is to be
247 * used as an Enumerable object using the given object's given
248 * method with the given arguments.
250 * Use of this method is discouraged. Use Kernel#enum_for() instead.
253 enumerator_initialize(int argc
, VALUE
*argv
, VALUE obj
)
255 VALUE recv
, meth
= sym_each
;
258 rb_raise(rb_eArgError
, "wrong number of argument (0 for 1)");
264 return enumerator_init(obj
, recv
, meth
, argc
, argv
);
269 enumerator_init_copy(VALUE obj
, VALUE orig
)
271 struct enumerator
*ptr0
, *ptr1
;
273 ptr0
= enumerator_ptr(orig
);
275 /* Fibers cannot be copied */
276 rb_raise(rb_eTypeError
, "can't copy execution context");
279 Data_Get_Struct(obj
, struct enumerator
, ptr1
);
282 rb_raise(rb_eArgError
, "unallocated enumerator");
285 ptr1
->obj
= ptr0
->obj
;
286 ptr1
->meth
= ptr0
->meth
;
287 ptr1
->args
= ptr0
->args
;
294 rb_enumeratorize(VALUE obj
, VALUE meth
, int argc
, VALUE
*argv
)
296 return enumerator_init(enumerator_allocate(rb_cEnumerator
), obj
, meth
, argc
, argv
);
303 * Iterates the given block using the object and the method specified
304 * in the first place. If no block is given, returns self.
308 enumerator_each(VALUE obj
)
310 struct enumerator
*e
;
314 if (!rb_block_given_p()) return obj
;
315 e
= enumerator_ptr(obj
);
317 argc
= RARRAY_LEN(e
->args
);
318 argv
= RARRAY_PTR(e
->args
);
320 return rb_block_call(e
->obj
, e
->meth
, argc
, argv
,
321 enumerator_each_i
, (VALUE
)e
);
325 enumerator_with_index_i(VALUE val
, VALUE
*memo
)
327 val
= rb_yield_values(2, val
, INT2FIX(*memo
));
334 * e.with_index {|(*args), idx| ... }
337 * Iterates the given block for each element with an index, which
338 * start from 0. If no block is given, returns an enumerator.
342 enumerator_with_index(VALUE obj
)
344 struct enumerator
*e
;
349 RETURN_ENUMERATOR(obj
, 0, 0);
350 e
= enumerator_ptr(obj
);
352 argc
= RARRAY_LEN(e
->args
);
353 argv
= RARRAY_PTR(e
->args
);
355 return rb_block_call(e
->obj
, e
->meth
, argc
, argv
,
356 enumerator_with_index_i
, (VALUE
)&memo
);
360 enumerator_with_object_i(VALUE val
, VALUE memo
)
362 return rb_yield_values(2, val
, memo
);
367 * e.with_object(obj) {|(*args), memo_obj| ... }
370 * Iterates the given block for each element with an arbitrary
371 * object given, and returns the memo object.
373 * If no block is given, returns an enumerator.
377 enumerator_with_object(VALUE obj
, VALUE memo
)
379 struct enumerator
*e
;
383 RETURN_ENUMERATOR(obj
, 0, 0);
384 e
= enumerator_ptr(obj
);
386 argc
= RARRAY_LEN(e
->args
);
387 argv
= RARRAY_PTR(e
->args
);
389 rb_block_call(e
->obj
, e
->meth
, argc
, argv
,
390 enumerator_with_object_i
, memo
);
396 next_ii(VALUE i
, VALUE obj
, int argc
, VALUE
*argv
)
398 rb_fiber_yield(argc
, argv
);
403 next_i(VALUE curr
, VALUE obj
)
405 struct enumerator
*e
= enumerator_ptr(obj
);
408 rb_block_call(obj
, rb_intern("each"), 0, 0, next_ii
, obj
);
410 return rb_fiber_yield(1, &nil
);
414 next_init(VALUE obj
, struct enumerator
*e
)
416 VALUE curr
= rb_fiber_current();
418 e
->fib
= rb_fiber_new(next_i
, obj
);
425 * Returns the next object in the enumerator, and move the internal
426 * position forward. When the position reached at the end, internal
427 * position is rewinded then StopIteration is raised.
429 * Note that enumeration sequence by next method does not affect other
430 * non-external enumeration methods, unless underlying iteration
431 * methods itself has side-effect, e.g. IO#each_line.
436 enumerator_next(VALUE obj
)
438 struct enumerator
*e
= enumerator_ptr(obj
);
440 curr
= rb_fiber_current();
442 if (!e
->fib
|| !rb_fiber_alive_p(e
->fib
)) {
446 v
= rb_fiber_resume(e
->fib
, 1, &curr
);
451 rb_raise(rb_eStopIteration
, "iteration reached at end");
460 * Rewinds the enumeration sequence by the next method.
464 enumerator_rewind(VALUE obj
)
466 struct enumerator
*e
= enumerator_ptr(obj
);
475 Init_Enumerator(void)
477 rb_define_method(rb_mKernel
, "to_enum", obj_to_enum
, -1);
478 rb_define_method(rb_mKernel
, "enum_for", obj_to_enum
, -1);
480 rb_define_method(rb_mEnumerable
, "each_slice", enum_each_slice
, 1);
481 rb_define_method(rb_mEnumerable
, "each_cons", enum_each_cons
, 1);
483 rb_cEnumerator
= rb_define_class_under(rb_mEnumerable
, "Enumerator", rb_cObject
);
484 rb_include_module(rb_cEnumerator
, rb_mEnumerable
);
486 rb_define_alloc_func(rb_cEnumerator
, enumerator_allocate
);
487 rb_define_method(rb_cEnumerator
, "initialize", enumerator_initialize
, -1);
488 rb_define_method(rb_cEnumerator
, "initialize_copy", enumerator_init_copy
, 1);
489 rb_define_method(rb_cEnumerator
, "each", enumerator_each
, 0);
490 rb_define_method(rb_cEnumerator
, "each_with_index", enumerator_with_index
, 0);
491 rb_define_method(rb_cEnumerator
, "with_index", enumerator_with_index
, 0);
493 rb_define_method(rb_cEnumerator
, "with_object", enumerator_with_object
, 1);
495 (void)enumerator_with_object
;
497 rb_define_method(rb_cEnumerator
, "next", enumerator_next
, 0);
498 rb_define_method(rb_cEnumerator
, "rewind", enumerator_rewind
, 0);
500 rb_eStopIteration
= rb_define_class("StopIteration", rb_eIndexError
);
502 sym_each
= ID2SYM(rb_intern("each"));
504 rb_provide("enumerator.so"); /* for backward compatibility */