@@ -119,6 +119,8 @@ struct enumerator {
119
119
VALUE lookahead ;
120
120
VALUE feedvalue ;
121
121
VALUE stop_exc ;
122
+ VALUE size ;
123
+ VALUE (* size_fn )(ANYARGS );
122
124
};
123
125
124
126
static VALUE rb_cGenerator , rb_cYielder ;
@@ -148,6 +150,7 @@ enumerator_mark(void *p)
148
150
rb_gc_mark (ptr -> lookahead );
149
151
rb_gc_mark (ptr -> feedvalue );
150
152
rb_gc_mark (ptr -> stop_exc );
153
+ rb_gc_mark (ptr -> size );
151
154
}
152
155
153
156
#define enumerator_free RUBY_TYPED_DEFAULT_FREE
@@ -216,7 +219,7 @@ obj_to_enum(int argc, VALUE *argv, VALUE obj)
216
219
-- argc ;
217
220
meth = * argv ++ ;
218
221
}
219
- return rb_enumeratorize (obj , meth , argc , argv );
222
+ return rb_enumeratorize (obj , meth , argc , argv , 0 );
220
223
}
221
224
222
225
static VALUE
@@ -232,7 +235,7 @@ enumerator_allocate(VALUE klass)
232
235
}
233
236
234
237
static VALUE
235
- enumerator_init (VALUE enum_obj , VALUE obj , VALUE meth , int argc , VALUE * argv )
238
+ enumerator_init (VALUE enum_obj , VALUE obj , VALUE meth , int argc , VALUE * argv , VALUE ( * size_fn )( ANYARGS ), VALUE size )
236
239
{
237
240
struct enumerator * ptr ;
238
241
@@ -250,13 +253,15 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
250
253
ptr -> lookahead = Qundef ;
251
254
ptr -> feedvalue = Qundef ;
252
255
ptr -> stop_exc = Qfalse ;
256
+ ptr -> size = size ;
257
+ ptr -> size_fn = size_fn ;
253
258
254
259
return enum_obj ;
255
260
}
256
261
257
262
/*
258
263
* call-seq:
259
- * Enumerator.new { |yielder| ... }
264
+ * Enumerator.new(size = nil) { |yielder| ... }
260
265
* Enumerator.new(obj, method = :each, *args)
261
266
*
262
267
* Creates a new Enumerator object, which can be used as an
@@ -276,6 +281,10 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
276
281
*
277
282
* p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
278
283
*
284
+ * The optional parameter can be used to specify how to calculate the size
285
+ * in a lazy fashion (see Enumerable#size). It can either be a value or
286
+ * a callable object.
287
+ *
279
288
* The block form can be used to create a lazy enumeration that only processes
280
289
* elements as-needed. The generic pattern for this is:
281
290
*
@@ -349,14 +358,23 @@ static VALUE
349
358
enumerator_initialize (int argc , VALUE * argv , VALUE obj )
350
359
{
351
360
VALUE recv , meth = sym_each ;
361
+ VALUE size = Qnil ;
352
362
353
- if (argc == 0 ) {
354
- if (!rb_block_given_p ())
355
- rb_check_arity (argc , 1 , UNLIMITED_ARGUMENTS );
356
-
363
+ if (rb_block_given_p ()) {
364
+ rb_check_arity (argc , 0 , 1 );
357
365
recv = generator_init (generator_allocate (rb_cGenerator ), rb_block_proc ());
366
+ if (argc ) {
367
+ if (NIL_P (argv [0 ]) || rb_obj_is_proc (argv [0 ]) ||
368
+ (TYPE (argv [0 ]) == T_FLOAT && RFLOAT_VALUE (argv [0 ]) == INFINITY )) {
369
+ size = argv [0 ];
370
+ } else {
371
+ size = rb_to_int (argv [0 ]);
372
+ }
373
+ argc = 0 ;
374
+ }
358
375
}
359
376
else {
377
+ rb_check_arity (argc , 1 , UNLIMITED_ARGUMENTS );
360
378
rb_warn ("Enumerator.new without a block is deprecated; use Object#to_enum" );
361
379
recv = * argv ++ ;
362
380
if (-- argc ) {
@@ -365,7 +383,7 @@ enumerator_initialize(int argc, VALUE *argv, VALUE obj)
365
383
}
366
384
}
367
385
368
- return enumerator_init (obj , recv , meth , argc , argv );
386
+ return enumerator_init (obj , recv , meth , argc , argv , 0 , size );
369
387
}
370
388
371
389
/* :nodoc: */
@@ -393,14 +411,16 @@ enumerator_init_copy(VALUE obj, VALUE orig)
393
411
ptr1 -> fib = 0 ;
394
412
ptr1 -> lookahead = Qundef ;
395
413
ptr1 -> feedvalue = Qundef ;
414
+ ptr1 -> size = ptr0 -> size ;
415
+ ptr1 -> size_fn = ptr0 -> size_fn ;
396
416
397
417
return obj ;
398
418
}
399
419
400
420
VALUE
401
- rb_enumeratorize (VALUE obj , VALUE meth , int argc , VALUE * argv )
421
+ rb_enumeratorize (VALUE obj , VALUE meth , int argc , VALUE * argv , VALUE ( * size_fn )( ANYARGS ) )
402
422
{
403
- return enumerator_init (enumerator_allocate (rb_cEnumerator ), obj , meth , argc , argv );
423
+ return enumerator_init (enumerator_allocate (rb_cEnumerator ), obj , meth , argc , argv , size_fn , Qnil );
404
424
}
405
425
406
426
static VALUE
@@ -942,6 +962,34 @@ enumerator_inspect(VALUE obj)
942
962
return rb_exec_recursive (inspect_enumerator , obj , 0 );
943
963
}
944
964
965
+ /*
966
+ * call-seq:
967
+ * e.size -> int, Float::INFINITY or nil
968
+ *
969
+ * Returns the size of the enumerator, or +nil+ if it can't be calculated lazily.
970
+ *
971
+ * (1..100).to_a.permutation(4).size # => 94109400
972
+ * loop.size # => Float::INFINITY
973
+ * (1..100).drop_while.size # => nil
974
+ */
975
+
976
+ static VALUE
977
+ enumerator_size (VALUE obj )
978
+ {
979
+ struct enumerator * e = enumerator_ptr (obj );
980
+
981
+ if (e -> size_fn ) {
982
+ return (* e -> size_fn )(e -> obj , e -> args );
983
+ }
984
+ if (rb_obj_is_proc (e -> size )) {
985
+ if (e -> args )
986
+ return rb_proc_call (e -> size , e -> args );
987
+ else
988
+ return rb_proc_call_with_block (e -> size , 0 , 0 , Qnil );
989
+ }
990
+ return e -> size ;
991
+ }
992
+
945
993
/*
946
994
* Yielder
947
995
*/
@@ -1253,7 +1301,7 @@ lazy_initialize(int argc, VALUE *argv, VALUE self)
1253
1301
rb_block_call (generator , id_initialize , 0 , 0 ,
1254
1302
(rb_block_given_p () ? lazy_init_block_i : lazy_init_block ),
1255
1303
obj );
1256
- enumerator_init (self , generator , meth , argc - offset , argv + offset );
1304
+ enumerator_init (self , generator , meth , argc - offset , argv + offset , 0 , Qnil );
1257
1305
rb_ivar_set (self , id_receiver , obj );
1258
1306
1259
1307
return self ;
@@ -1749,6 +1797,7 @@ InitVM_Enumerator(void)
1749
1797
rb_define_method (rb_cEnumerator , "feed" , enumerator_feed , 1 );
1750
1798
rb_define_method (rb_cEnumerator , "rewind" , enumerator_rewind , 0 );
1751
1799
rb_define_method (rb_cEnumerator , "inspect" , enumerator_inspect , 0 );
1800
+ rb_define_method (rb_cEnumerator , "size" , enumerator_size , 0 );
1752
1801
1753
1802
/* Lazy */
1754
1803
rb_cLazy = rb_define_class_under (rb_cEnumerator , "Lazy" , rb_cEnumerator );
0 commit comments