Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 415 lines (361 sloc) 8.652 kB
cde1e57 @knu * enumerator.c, inits.c (rb_call_inits), ruby.h, intern.h,
knu authored
1 /************************************************
2
3 enumerator.c - provides Enumerator class
4
5 $Author$
6
7 Copyright (C) 2001-2003 Akinori MUSHA
8
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 $
11 $Id$
12
13 ************************************************/
14
15 #include "ruby.h"
16
17 /*
18 * Document-class: Enumerable::Enumerator
19 *
20 * A class which provides a method `each' to be used as an Enumerable
21 * object.
22 */
23 VALUE rb_cEnumerator;
24 static VALUE sym_each, sym_call;
25
26 static VALUE
27 proc_call(proc, args)
28 VALUE proc;
29 VALUE args;
30 {
31 if (TYPE(args) != T_ARRAY) {
32 args = rb_ary_new3(1, args);
33 }
34 return rb_proc_call(proc, args);
35 }
36
37 struct enumerator {
38 VALUE method;
39 VALUE proc;
40 VALUE args;
41 rb_block_call_func *iter;
42 };
43
44 static void
45 enumerator_mark(p)
46 void *p;
47 {
48 struct enumerator *ptr = p;
49 rb_gc_mark(ptr->method);
50 rb_gc_mark(ptr->proc);
51 rb_gc_mark(ptr->args);
52 }
53
54 static struct enumerator *
55 enumerator_ptr(obj)
56 VALUE obj;
57 {
58 struct enumerator *ptr;
59
60 Data_Get_Struct(obj, struct enumerator, ptr);
61 if (RDATA(obj)->dmark != enumerator_mark) {
62 rb_raise(rb_eTypeError,
63 "wrong argument type %s (expected Enumerable::Enumerator)",
64 rb_obj_classname(obj));
65 }
66 if (!ptr) {
67 rb_raise(rb_eArgError, "uninitialized enumerator");
68 }
69 return ptr;
70 }
71
72 static VALUE
73 enumerator_iter_i(i, enum_obj, argc, argv)
74 VALUE i;
75 VALUE enum_obj;
76 int argc;
77 VALUE *argv;
78 {
79 struct enumerator *e = (struct enumerator *)enum_obj;
80 return rb_yield(proc_call(e->proc, i));
81 }
82
83 /*
84 * call-seq:
85 * obj.to_enum(method = :each, *args)
86 * obj.enum_for(method = :each, *args)
87 *
88 * Returns Enumerable::Enumerator.new(self, method, *args).
89 *
90 * e.g.:
91 *
92 * str = "xyz"
93 *
94 * enum = str.enum_for(:each_byte)
95 * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
96 *
97 * # protects an array from being modified
98 * a = [1, 2, 3]
99 * some_method(a.to_enum)
100 *
101 */
102 static VALUE
103 obj_to_enum(argc, argv, obj)
104 int argc;
105 VALUE *argv;
106 VALUE obj;
107 {
108 VALUE meth = sym_each;
109
110 if (argc > 0) {
111 --argc;
112 meth = *argv++;
113 }
114 return rb_enumeratorize(obj, meth, argc, argv);
115 }
116
117 static VALUE
118 each_slice_i(val, memo)
119 VALUE val;
120 VALUE *memo;
121 {
122 VALUE ary = memo[0];
123 VALUE v = Qnil;
124 long size = (long)memo[1];
125
126 rb_ary_push(ary, val);
127
128 if (RARRAY_LEN(ary) == size) {
129 v = rb_yield(ary);
130 memo[0] = rb_ary_new2(size);
131 }
132
133 return v;
134 }
135
136 /*
137 * call-seq:
138 * e.each_slice(n) {...}
139 *
140 * Iterates the given block for each slice of <n> elements.
141 *
142 * e.g.:
143 * (1..10).each_slice(3) {|a| p a}
144 * # outputs below
145 * [1, 2, 3]
146 * [4, 5, 6]
147 * [7, 8, 9]
148 * [10]
149 *
150 */
151 static VALUE
152 enum_each_slice(obj, n)
153 VALUE obj, n;
154 {
155 long size = NUM2LONG(n);
156 VALUE args[2], ary;
157
158 if (size <= 0) rb_raise(rb_eArgError, "invalid slice size");
159 RETURN_ENUMERATOR(obj, 1, &n);
160 args[0] = rb_ary_new2(size);
161 args[1] = (VALUE)size;
162
163 rb_block_call(obj, SYM2ID(sym_each), 0, 0, each_slice_i, (VALUE)args);
164
165 ary = args[0];
166 if (RARRAY_LEN(ary) > 0) rb_yield(ary);
167
168 return Qnil;
169 }
170
171 static VALUE
172 each_cons_i(val, memo)
173 VALUE val;
174 VALUE *memo;
175 {
176 VALUE ary = memo[0];
177 VALUE v = Qnil;
178 long size = (long)memo[1];
179
180 if (RARRAY_LEN(ary) == size) {
181 rb_ary_shift(ary);
182 }
183 rb_ary_push(ary, val);
184 if (RARRAY_LEN(ary) == size) {
185 v = rb_yield(rb_ary_dup(ary));
186 }
187 return v;
188 }
189
190 /*
191 * call-seq:
192 * each_cons(n) {...}
193 *
194 * Iterates the given block for each array of consecutive <n>
195 * elements.
196 *
197 * e.g.:
198 * (1..10).each_cons(3) {|a| p a}
199 * # outputs below
200 * [1, 2, 3]
201 * [2, 3, 4]
202 * [3, 4, 5]
203 * [4, 5, 6]
204 * [5, 6, 7]
205 * [6, 7, 8]
206 * [7, 8, 9]
207 * [8, 9, 10]
208 *
209 */
210 static VALUE
211 enum_each_cons(obj, n)
212 VALUE obj, n;
213 {
214 long size = NUM2LONG(n);
215 VALUE args[2];
216
217 if (size <= 0) rb_raise(rb_eArgError, "invalid size");
218 RETURN_ENUMERATOR(obj, 1, &n);
219 args[0] = rb_ary_new2(size);
220 args[1] = (VALUE)size;
221
222 rb_block_call(obj, SYM2ID(sym_each), 0, 0, each_cons_i, (VALUE)args);
223
224 return Qnil;
225 }
226
227 static VALUE
228 enumerator_allocate(klass)
229 VALUE klass;
230 {
231 struct enumerator *ptr;
232 return Data_Make_Struct(klass, struct enumerator,
233 enumerator_mark, -1, ptr);
234 }
235
236 static VALUE
237 enumerator_each_i(v, enum_obj)
238 VALUE v;
239 VALUE enum_obj;
240 {
241 return rb_yield(v);
242 }
243
244 static VALUE
245 enumerator_init(enum_obj, obj, meth, argc, argv)
246 VALUE enum_obj;
247 VALUE obj;
248 VALUE meth;
249 int argc;
250 VALUE *argv;
251 {
252 struct enumerator *ptr = enumerator_ptr(enum_obj);
253
254 ptr->method = rb_obj_method(obj, meth);
255 if (rb_block_given_p()) {
256 ptr->proc = rb_block_proc();
257 ptr->iter = enumerator_iter_i;
258 }
259 else {
260 ptr->iter = enumerator_each_i;
261 }
262 if (argc) ptr->args = rb_ary_new4(argc, argv);
263
264 return enum_obj;
265 }
266
267 /*
268 * call-seq:
269 * Enumerable::Enumerator.new(obj, method = :each, *args)
270 *
271 * Creates a new Enumerable::Enumerator object, which is to be
272 * used as an Enumerable object using the given object's given
273 * method with the given arguments.
274 *
275 * e.g.:
276 * str = "xyz"
277 *
278 * enum = Enumerable::Enumerator.new(str, :each_byte)
279 * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
280 *
281 */
282 static VALUE
283 enumerator_initialize(argc, argv, obj)
284 int argc;
285 VALUE *argv;
286 VALUE obj;
287 {
288 VALUE recv, meth = sym_each;
289
290 if (argc == 0)
291 rb_raise(rb_eArgError, "wrong number of argument (0 for 1)");
292 recv = *argv++;
293 if (--argc) {
294 meth = *argv++;
295 --argc;
296 }
297 return enumerator_init(obj, recv, meth, argc, argv);
298 }
299
300 /* :nodoc: */
301 static VALUE
302 enumerator_init_copy(obj, orig)
303 VALUE obj;
304 VALUE orig;
305 {
306 struct enumerator *ptr0, *ptr1;
307
308 ptr0 = enumerator_ptr(orig);
309 ptr1 = enumerator_ptr(obj);
310
311 ptr1->method = ptr0->method;
312 ptr1->proc = ptr0->proc;
313 ptr1->iter = ptr0->iter;
314 ptr1->args = ptr0->args;
315
316 return obj;
317 }
318
319 VALUE
320 rb_enumeratorize(obj, meth, argc, argv)
321 VALUE obj;
322 VALUE meth;
323 int argc;
324 VALUE *argv;
325 {
326 return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv);
327 }
328
329 /*
330 * call-seq:
331 * enum.each {...}
332 *
333 * Iterates the given block using the object and the method specified
334 * in the first place.
335 *
336 */
337 static VALUE
338 enumerator_each(obj)
339 VALUE obj;
340 {
341 struct enumerator *e;
342 int argc = 0;
343 VALUE *argv = 0;
344
345 if (!rb_block_given_p()) return obj;
346 e = enumerator_ptr(obj);
347 if (e->args) {
348 argc = RARRAY_LEN(e->args);
349 argv = RARRAY_PTR(e->args);
350 }
351 return rb_block_call(e->method, SYM2ID(sym_call), argc, argv, e->iter, (VALUE)e);
352 }
353
354 static VALUE
355 enumerator_with_index_i(val, memo)
356 VALUE val;
357 VALUE *memo;
358 {
359 val = rb_yield_values(2, val, INT2FIX(*memo));
360 ++*memo;
361 return val;
362 }
363
364 /*
365 * call-seq:
366 * e.with_index {|(*args), idx| ... }
367 *
368 * Iterates the given block for each elements with an index, which
369 * start from 0.
370 *
371 */
372 static VALUE
373 enumerator_with_index(obj)
374 VALUE obj;
375 {
376 struct enumerator *e = enumerator_ptr(obj);
377 VALUE memo = 0;
378 int argc = 0;
379 VALUE *argv = 0;
380
381 RETURN_ENUMERATOR(obj, 0, 0);
382 if (e->args) {
383 argc = RARRAY_LEN(e->args);
384 argv = RARRAY_PTR(e->args);
385 }
386 return rb_block_call(e->method, SYM2ID(sym_call), argc, argv,
387 enumerator_with_index_i, (VALUE)&memo);
388 }
389
390 void
391 Init_Enumerator()
392 {
393 rb_define_method(rb_mKernel, "to_enum", obj_to_enum, -1);
394 rb_define_method(rb_mKernel, "enum_for", obj_to_enum, -1);
395
396 rb_define_method(rb_mEnumerable, "each_slice", enum_each_slice, 1);
397 rb_define_method(rb_mEnumerable, "enum_slice", enum_each_slice, 1);
398 rb_define_method(rb_mEnumerable, "each_cons", enum_each_cons, 1);
399 rb_define_method(rb_mEnumerable, "enum_cons", enum_each_cons, 1);
400
401 rb_cEnumerator = rb_define_class_under(rb_mEnumerable, "Enumerator", rb_cObject);
402 rb_include_module(rb_cEnumerator, rb_mEnumerable);
403
404 rb_define_alloc_func(rb_cEnumerator, enumerator_allocate);
405 rb_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
406 rb_define_method(rb_cEnumerator, "initialize_copy", enumerator_init_copy, 1);
407 rb_define_method(rb_cEnumerator, "each", enumerator_each, 0);
408 rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, 0);
409
410 sym_each = ID2SYM(rb_intern("each"));
411 sym_call = ID2SYM(rb_intern("call"));
412
413 rb_provide("enumerator.so"); /* for backward compatibility */
414 }
Something went wrong with that request. Please try again.