Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
diff --git a/array.c b/array.c
index 51d3ad2..29f2ca0 100644
--- a/array.c
+++ b/array.c
@@ -22,7 +22,7 @@
VALUE rb_cArray;
-static ID id_cmp;
+static ID id_cmp, id_div, id_power;
#define ARY_DEFAULT_SIZE 16
#define ARY_MAX_SIZE (LONG_MAX / (int)sizeof(VALUE))
@@ -1399,6 +1399,9 @@ rb_ary_insert(int argc, VALUE *argv, VALUE ary)
return ary;
}
+static VALUE
+rb_ary_length(VALUE ary);
+
/*
* call-seq:
* ary.each {|item| block } -> ary
@@ -1422,7 +1425,7 @@ rb_ary_each(VALUE ary)
{
long i;
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(RARRAY_PTR(ary)[i]);
}
@@ -1452,7 +1455,7 @@ static VALUE
rb_ary_each_index(VALUE ary)
{
long i;
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(LONG2NUM(i));
@@ -1481,7 +1484,7 @@ rb_ary_reverse_each(VALUE ary)
{
long len;
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
len = RARRAY_LEN(ary);
while (len--) {
rb_yield(RARRAY_PTR(ary)[len]);
@@ -2128,7 +2131,7 @@ rb_ary_sort_by_bang(VALUE ary)
{
VALUE sorted;
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
rb_ary_modify(ary);
sorted = rb_block_call(ary, rb_intern("sort_by"), 0, 0, sort_by_i, 0);
rb_ary_replace(ary, sorted);
@@ -2160,7 +2163,7 @@ rb_ary_collect(VALUE ary)
long i;
VALUE collect;
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
collect = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_ary_push(collect, rb_yield(RARRAY_PTR(ary)[i]));
@@ -2192,7 +2195,7 @@ rb_ary_collect_bang(VALUE ary)
{
long i;
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
rb_ary_modify(ary);
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_ary_store(ary, i, rb_yield(RARRAY_PTR(ary)[i]));
@@ -2272,7 +2275,7 @@ rb_ary_select(VALUE ary)
VALUE result;
long i;
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
result = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
if (RTEST(rb_yield(RARRAY_PTR(ary)[i]))) {
@@ -2302,7 +2305,7 @@ rb_ary_select_bang(VALUE ary)
{
long i1, i2;
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
rb_ary_modify(ary);
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); i1++) {
VALUE v = RARRAY_PTR(ary)[i1];
@@ -2337,7 +2340,7 @@ rb_ary_select_bang(VALUE ary)
static VALUE
rb_ary_keep_if(VALUE ary)
{
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
rb_ary_select_bang(ary);
return ary;
}
@@ -2528,7 +2531,7 @@ rb_ary_reject_bang(VALUE ary)
{
long i1, i2;
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
rb_ary_modify(ary);
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); i1++) {
VALUE v = RARRAY_PTR(ary)[i1];
@@ -2561,7 +2564,7 @@ rb_ary_reject_bang(VALUE ary)
static VALUE
rb_ary_reject(VALUE ary)
{
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
ary = rb_ary_dup(ary);
rb_ary_reject_bang(ary);
return ary;
@@ -2585,7 +2588,7 @@ rb_ary_reject(VALUE ary)
static VALUE
rb_ary_delete_if(VALUE ary)
{
- RETURN_ENUMERATOR(ary, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ary, 0, 0, rb_ary_length);
rb_ary_reject_bang(ary);
return ary;
}
@@ -3863,6 +3866,20 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary)
return result;
}
+static VALUE
+rb_ary_cycle_size(VALUE self, VALUE args)
+{
+ long mul;
+ VALUE n = Qnil;
+ if (args && (RARRAY_LEN(args) > 0)) {
+ n = RARRAY_PTR(args)[0];
+ }
+ if (RARRAY_LEN(self) == 0) return INT2FIX(0);
+ if (n == Qnil) return DBL2NUM(INFINITY);
+ mul = NUM2LONG(n);
+ if (mul <= 0) return INT2FIX(0);
+ return rb_funcall(rb_ary_length(self), '*', 1, LONG2FIX(mul));
+}
/*
* call-seq:
@@ -3891,7 +3908,7 @@ rb_ary_cycle(int argc, VALUE *argv, VALUE ary)
rb_scan_args(argc, argv, "01", &nv);
- RETURN_ENUMERATOR(ary, argc, argv);
+ RETURN_SIZED_ENUMERATOR(ary, argc, argv, rb_ary_cycle_size);
if (NIL_P(nv)) {
n = -1;
}
@@ -3958,6 +3975,41 @@ permute0(long n, long r, long *p, long index, char *used, VALUE values)
}
/*
+ * Returns the product of from, from-1, ..., from - how_many + 1.
+ * http://en.wikipedia.org/wiki/Pochhammer_symbol
+ */
+static VALUE
+descending_factorial(long from, long how_many)
+{
+ VALUE cnt = LONG2FIX(1);
+ while(how_many-- > 0) {
+ cnt = rb_funcall(cnt, '*', 1, LONG2FIX(from--));
+ }
+ return cnt;
+}
+
+static VALUE
+binomial_coefficient(long comb, long size)
+{
+ if (comb > size-comb) {
+ comb = size-comb;
+ }
+ if (comb < 0) {
+ return LONG2FIX(0);
+ }
+ return rb_funcall(descending_factorial(size, comb), id_div, 1, descending_factorial(comb, comb));
+}
+
+static VALUE
+rb_ary_permutation_size(VALUE ary, VALUE args)
+{
+ long n = RARRAY_LEN(ary);
+ long k = (args && (RARRAY_LEN(args) > 0)) ? NUM2LONG(RARRAY_PTR(args)[0]) : n;
+
+ return descending_factorial(n, k);
+}
+
+/*
* call-seq:
* ary.permutation { |p| block } -> ary
* ary.permutation -> an_enumerator
@@ -3990,7 +4042,7 @@ rb_ary_permutation(int argc, VALUE *argv, VALUE ary)
long r, n, i;
n = RARRAY_LEN(ary); /* Array length */
- RETURN_ENUMERATOR(ary, argc, argv); /* Return enumerator if no block */
+ RETURN_SIZED_ENUMERATOR(ary, argc, argv, rb_ary_permutation_size); /* Return enumerator if no block */
rb_scan_args(argc, argv, "01", &num);
r = NIL_P(num) ? n : NUM2LONG(num); /* Permutation size from argument */
@@ -4023,6 +4075,15 @@ rb_ary_permutation(int argc, VALUE *argv, VALUE ary)
return ary;
}
+static VALUE
+rb_ary_combination_size(VALUE ary, VALUE args)
+{
+ long n = RARRAY_LEN(ary);
+ long k = NUM2LONG(RARRAY_PTR(args)[0]);
+
+ return binomial_coefficient(k, n);
+}
+
/*
* call-seq:
* ary.combination(n) { |c| block } -> ary
@@ -4053,7 +4114,7 @@ rb_ary_combination(VALUE ary, VALUE num)
long n, i, len;
n = NUM2LONG(num);
- RETURN_ENUMERATOR(ary, 1, &num);
+ RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_combination_size);
len = RARRAY_LEN(ary);
if (n < 0 || len < n) {
/* yield nothing */
@@ -4135,6 +4196,19 @@ rpermute0(long n, long r, long *p, long index, VALUE values)
}
}
+static VALUE
+rb_ary_repeated_permutation_size(VALUE ary, VALUE args)
+{
+ long n = RARRAY_LEN(ary);
+ long k = NUM2LONG(RARRAY_PTR(args)[0]);
+
+ if (k < 0) {
+ return LONG2FIX(0);
+ }
+
+ return rb_funcall(LONG2NUM(n), id_power, 1, LONG2NUM(k));
+}
+
/*
* call-seq:
* ary.repeated_permutation(n) { |p| block } -> ary
@@ -4163,7 +4237,7 @@ rb_ary_repeated_permutation(VALUE ary, VALUE num)
long r, n, i;
n = RARRAY_LEN(ary); /* Array length */
- RETURN_ENUMERATOR(ary, 1, &num); /* Return enumerator if no block */
+ RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_repeated_permutation_size); /* Return enumerator if no block */
r = NUM2LONG(num); /* Permutation size from argument */
if (r < 0) {
@@ -4214,6 +4288,17 @@ rcombinate0(long n, long r, long *p, long index, long rest, VALUE values)
}
}
+static VALUE
+rb_ary_repeated_combination_size(VALUE ary, VALUE args)
+{
+ long n = RARRAY_LEN(ary);
+ long k = NUM2LONG(RARRAY_PTR(args)[0]);
+ if (k == 0) {
+ return LONG2FIX(1);
+ }
+ return binomial_coefficient(k, n + k - 1);
+}
+
/*
* call-seq:
* ary.repeated_combination(n) { |c| block } -> ary
@@ -4247,7 +4332,7 @@ rb_ary_repeated_combination(VALUE ary, VALUE num)
long n, i, len;
n = NUM2LONG(num); /* Combination size from argument */
- RETURN_ENUMERATOR(ary, 1, &num); /* Return enumerator if no block */
+ RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_repeated_combination_size); /* Return enumerator if no block */
len = RARRAY_LEN(ary);
if (n < 0) {
/* yield nothing */
@@ -4609,4 +4694,6 @@ Init_Array(void)
rb_define_method(rb_cArray, "drop_while", rb_ary_drop_while, 0);
id_cmp = rb_intern("<=>");
+ id_div = rb_intern("div");
+ id_power = rb_intern("**");
}
diff --git a/enum.c b/enum.c
index fc8a3c3..883ea22 100644
--- a/enum.c
+++ b/enum.c
@@ -14,7 +14,7 @@
#include "node.h"
VALUE rb_mEnumerable;
-static ID id_each, id_eqq, id_cmp, id_next, id_size;
+static ID id_each, id_eqq, id_cmp, id_next, id_size, id_div;
static VALUE
enum_values_pack(int argc, VALUE *argv)
@@ -296,6 +296,14 @@ find_all_i(VALUE i, VALUE ary, int argc, VALUE *argv)
return Qnil;
}
+static VALUE
+enum_size(VALUE self, VALUE args)
+{
+ VALUE r;
+ r = rb_check_funcall(self, id_size, 0, 0);
+ return (r == Qundef) ? Qnil : r;
+}
+
/*
* call-seq:
* enum.find_all {| obj | block } -> array
@@ -319,7 +327,7 @@ enum_find_all(VALUE obj)
{
VALUE ary;
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, find_all_i, ary);
@@ -357,7 +365,7 @@ enum_reject(VALUE obj)
{
VALUE ary;
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, reject_i, ary);
@@ -404,7 +412,7 @@ enum_collect(VALUE obj)
{
VALUE ary;
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, collect_i, ary);
@@ -450,7 +458,7 @@ enum_flat_map(VALUE obj)
{
VALUE ary;
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, flat_map_i, ary);
@@ -621,7 +629,7 @@ enum_partition(VALUE obj)
{
VALUE ary[2];
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary[0] = rb_ary_new();
ary[1] = rb_ary_new();
@@ -670,7 +678,7 @@ enum_group_by(VALUE obj)
{
VALUE hash;
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
hash = rb_hash_new();
rb_block_call(obj, id_each, 0, 0, group_by_i, hash);
@@ -867,7 +875,7 @@ enum_sort_by(VALUE obj)
VALUE ary;
long i;
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
if (TYPE(obj) == T_ARRAY) {
ary = rb_ary_new2(RARRAY_LEN(obj));
@@ -1382,7 +1390,7 @@ enum_min_by(VALUE obj)
{
VALUE memo[2];
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
memo[0] = Qundef;
memo[1] = Qnil;
@@ -1428,7 +1436,7 @@ enum_max_by(VALUE obj)
{
VALUE memo[2];
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
memo[0] = Qundef;
memo[1] = Qnil;
@@ -1526,7 +1534,7 @@ enum_minmax_by(VALUE obj)
{
struct minmax_by_t memo;
- RETURN_ENUMERATOR(obj, 0, 0);
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
memo.min_bv = Qundef;
memo.max_bv = Qundef;
@@ -1606,7 +1614,7 @@ enum_each_with_index(int argc, VALUE *argv, VALUE obj)
{
long memo;
- RETURN_ENUMERATOR(obj, argc, argv);
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
memo = 0;
rb_block_call(obj, id_each, argc, argv, each_with_index_i, (VALUE)&memo);
@@ -1631,7 +1639,7 @@ enum_reverse_each(int argc, VALUE *argv, VALUE obj)
VALUE ary;
long i;
- RETURN_ENUMERATOR(obj, argc, argv);
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
ary = enum_to_a(argc, argv, obj);
@@ -1679,7 +1687,7 @@ each_val_i(VALUE i, VALUE p, int argc, VALUE *argv)
static VALUE
enum_each_entry(int argc, VALUE *argv, VALUE obj)
{
- RETURN_ENUMERATOR(obj, argc, argv);
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
rb_block_call(obj, id_each, argc, argv, each_val_i, 0);
return obj;
}
@@ -1702,6 +1710,20 @@ each_slice_i(VALUE i, VALUE *memo, int argc, VALUE *argv)
return v;
}
+static VALUE
+enum_each_slice_size(VALUE obj, VALUE args)
+{
+ VALUE n, size;
+ long slice_size = NUM2LONG(RARRAY_PTR(args)[0]);
+ if (slice_size <= 0) rb_raise(rb_eArgError, "invalid slice size");
+
+ size = enum_size(obj, 0);
+ if (size == Qnil) return Qnil;
+
+ n = rb_funcall(size, '+', 1, LONG2NUM(slice_size-1));
+ return rb_funcall(n, id_div, 1, LONG2FIX(slice_size));
+}
+
/*
* call-seq:
* enum.each_slice(n) {...} -> nil
@@ -1726,7 +1748,7 @@ enum_each_slice(VALUE obj, VALUE n)
VALUE args[2], ary;
if (size <= 0) rb_raise(rb_eArgError, "invalid slice size");
- RETURN_ENUMERATOR(obj, 1, &n);
+ RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_slice_size);
args[0] = rb_ary_new2(size);
args[1] = (VALUE)size;
@@ -1756,6 +1778,20 @@ each_cons_i(VALUE i, VALUE *memo, int argc, VALUE *argv)
return v;
}
+static VALUE
+enum_each_cons_size(VALUE obj, VALUE args)
+{
+ VALUE n, size;
+ long cons_size = NUM2LONG(RARRAY_PTR(args)[0]);
+ if (cons_size <= 0) rb_raise(rb_eArgError, "invalid size");
+
+ size = enum_size(obj, 0);
+ if (size == Qnil) return Qnil;
+
+ n = rb_funcall(size, '+', 1, LONG2NUM(1 - cons_size));
+ return (rb_cmpint(rb_funcall(n, id_cmp, 1, LONG2FIX(0)), n, LONG2FIX(0)) == -1) ? LONG2FIX(0) : n;
+}
+
/*
* call-seq:
* enum.each_cons(n) {...} -> nil
@@ -1784,7 +1820,7 @@ enum_each_cons(VALUE obj, VALUE n)
VALUE args[2];
if (size <= 0) rb_raise(rb_eArgError, "invalid size");
- RETURN_ENUMERATOR(obj, 1, &n);
+ RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_cons_size);
args[0] = rb_ary_new2(size);
args[1] = (VALUE)size;
@@ -1818,7 +1854,7 @@ each_with_object_i(VALUE i, VALUE memo, int argc, VALUE *argv)
static VALUE
enum_each_with_object(VALUE obj, VALUE memo)
{
- RETURN_ENUMERATOR(obj, 1, &memo);
+ RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enum_size);
rb_block_call(obj, id_each, 0, 0, each_with_object_i, memo);
@@ -2124,6 +2160,24 @@ cycle_i(VALUE i, VALUE ary, int argc, VALUE *argv)
return Qnil;
}
+static VALUE
+enum_cycle_size(VALUE self, VALUE args)
+{
+ long mul;
+ VALUE n = Qnil;
+ VALUE size = enum_size(self, args);
+
+ if (size == Qnil) return Qnil;
+
+ if (args && (RARRAY_LEN(args) > 0)) {
+ n = RARRAY_PTR(args)[0];
+ }
+ if (n == Qnil) return DBL2NUM(INFINITY);
+ mul = NUM2LONG(n);
+ if (mul <= 0) return INT2FIX(0);
+ return rb_funcall(size, '*', 1, LONG2FIX(mul));
+}
+
/*
* call-seq:
* enum.cycle(n=nil) {|obj| block } -> nil
@@ -2154,7 +2208,7 @@ enum_cycle(int argc, VALUE *argv, VALUE obj)
rb_scan_args(argc, argv, "01", &nv);
- RETURN_ENUMERATOR(obj, argc, argv);
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_cycle_size);
if (NIL_P(nv)) {
n = -1;
}
@@ -2664,5 +2718,6 @@ Init_Enumerable(void)
id_cmp = rb_intern("<=>");
id_next = rb_intern("next");
id_size = rb_intern("size");
+ id_div = rb_intern("div");
}
diff --git a/enumerator.c b/enumerator.c
index 47a5c7e..fd5cc88 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -90,6 +90,8 @@ struct enumerator {
VALUE lookahead;
VALUE feedvalue;
VALUE stop_exc;
+ VALUE size;
+ VALUE (*size_fn)(VALUE, VALUE);
};
static VALUE rb_cGenerator, rb_cYielder;
@@ -119,6 +121,7 @@ enumerator_mark(void *p)
rb_gc_mark(ptr->lookahead);
rb_gc_mark(ptr->feedvalue);
rb_gc_mark(ptr->stop_exc);
+ rb_gc_mark(ptr->size);
}
#define enumerator_free RUBY_TYPED_DEFAULT_FREE
@@ -150,12 +153,19 @@ enumerator_ptr(VALUE obj)
return ptr;
}
+static VALUE
+enumerator_set_size(VALUE obj, VALUE size);
+
/*
* call-seq:
* obj.to_enum(method = :each, *args)
* obj.enum_for(method = :each, *args)
+ * obj.to_enum(method = :each, *args) {|obj, *args| block}
+ * obj.enum_for(method = :each, *args){|obj, *args| block}
*
* Returns Enumerator.new(self, method, *args).
+ * If a block is given, it will be used to calculate the size of
+ * the enumerator (see Enumerator#size=).
*
* e.g.:
*
@@ -172,13 +182,17 @@ enumerator_ptr(VALUE obj)
static VALUE
obj_to_enum(int argc, VALUE *argv, VALUE obj)
{
- VALUE meth = sym_each;
+ VALUE enumerator, meth = sym_each;
if (argc > 0) {
--argc;
meth = *argv++;
}
- return rb_enumeratorize(obj, meth, argc, argv);
+ enumerator = rb_enumeratorize(obj, meth, argc, argv, 0);
+ if (rb_block_given_p()) {
+ enumerator_set_size(enumerator, rb_block_proc());
+ }
+ return enumerator;
}
static VALUE
@@ -194,7 +208,7 @@ enumerator_allocate(VALUE klass)
}
static VALUE
-enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
+enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv, VALUE (*size_fn)(ANYARGS))
{
struct enumerator *ptr;
@@ -212,6 +226,8 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
ptr->lookahead = Qundef;
ptr->feedvalue = Qundef;
ptr->stop_exc = Qfalse;
+ ptr->size = Qnil;
+ ptr->size_fn = size_fn;
return enum_obj;
}
@@ -267,7 +283,7 @@ enumerator_initialize(int argc, VALUE *argv, VALUE obj)
}
}
- return enumerator_init(obj, recv, meth, argc, argv);
+ return enumerator_init(obj, recv, meth, argc, argv, 0);
}
/* :nodoc: */
@@ -294,14 +310,16 @@ enumerator_init_copy(VALUE obj, VALUE orig)
ptr1->fib = 0;
ptr1->lookahead = Qundef;
ptr1->feedvalue = Qundef;
+ ptr1->size = ptr0->size;
+ ptr1->size_fn = ptr0->size_fn;
return obj;
}
VALUE
-rb_enumeratorize(VALUE obj, VALUE meth, int argc, VALUE *argv)
+rb_enumeratorize(VALUE obj, VALUE meth, int argc, VALUE *argv, VALUE (*size_fn)(ANYARGS))
{
- return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv);
+ return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv, size_fn);
}
static VALUE
@@ -349,6 +367,9 @@ enumerator_with_index_i(VALUE val, VALUE m, int argc, VALUE *argv)
return rb_yield_values(2, rb_ary_new4(argc, argv), idx);
}
+static VALUE
+enumerator_size(VALUE obj);
+
/*
* call-seq:
* e.with_index(offset = 0) {|(*args), idx| ... }
@@ -364,7 +385,7 @@ enumerator_with_index(int argc, VALUE *argv, VALUE obj)
VALUE memo;
rb_scan_args(argc, argv, "01", &memo);
- RETURN_ENUMERATOR(obj, argc, argv);
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, enumerator_size);
memo = NIL_P(memo) ? 0 : (VALUE)NUM2LONG(memo);
return enumerator_block_call(obj, enumerator_with_index_i, (VALUE)&memo);
}
@@ -407,7 +428,7 @@ enumerator_with_object_i(VALUE val, VALUE memo, int argc, VALUE *argv)
static VALUE
enumerator_with_object(VALUE obj, VALUE memo)
{
- RETURN_ENUMERATOR(obj, 1, &memo);
+ RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enumerator_size);
enumerator_block_call(obj, enumerator_with_object_i, memo);
return memo;
@@ -794,6 +815,86 @@ enumerator_inspect(VALUE obj)
return rb_exec_recursive(inspect_enumerator, obj, 0);
}
+static VALUE
+enumerator_size_i(VALUE val, VALUE m, int argc, VALUE *argv)
+{
+ ++*(long *)m;
+ return rb_yield_values2(argc, argv);
+}
+
+/*
+ * call-seq:
+ * e.size -> int, Float::INFINITY or nil
+ * e.size {block} -> int
+ *
+ * Returns the size of the enumerator.
+ * The form with no block given will do a lazy evaluation of the size without going through the enumeration. If the size can not be determined then +nil+ is returned.
+ * The form with a block will always iterate through the enumerator and return the number of times it yielded.
+ *
+ * (1..100).to_a.permutation(4).size # => 94109400
+ * loop.size # => Float::INFINITY
+ *
+ * a = [1, 2, 3]
+ * a.keep_if.size # => 3
+ * a # => [1, 2, 3]
+ * a.keep_if.size{false} # => 3
+ * a # => []
+ *
+ * [1, 2, 3].drop_while.size # => nil
+ * [1, 2, 3].drop_while.size{|i| i < 3} # => 2
+ */
+
+static VALUE
+enumerator_size(VALUE obj)
+{
+ struct enumerator *e = enumerator_ptr(obj);
+
+ if (rb_block_given_p()) {
+ long size = 0;
+ enumerator_block_call(obj, enumerator_size_i, (VALUE)&size);
+ return LONG2NUM(size);
+ }
+ if (e->size_fn) {
+ return (*e->size_fn)(e->obj, e->args);
+ }
+ if (rb_obj_is_proc(e->size)) {
+ VALUE args = rb_ary_new3(1, e->obj);
+ if(e->args) {
+ rb_ary_concat(args, e->args);
+ }
+ return rb_proc_call(e->size, args);
+ }
+ return e->size;
+}
+
+/*
+ * call-seq:
+ * e.size = sz
+ *
+ * Sets the size of the enumerator. If +sz+ is a Proc or a Method, it will be called each time +size+ is requested, otherwise +sz+ is returned.
+ *
+ * first = [1, 2, 3]
+ * second = [4, 5]
+ * enum = Enumerator.new do |y|
+ * first.each{|o| y << o}
+ * second.each{|o| y << o}
+ * end
+ * enum.size # => nil
+ * enum.size = ->(e){first.size + second.size}
+ * enum.size # => 5
+ * first << 42
+ */
+
+static VALUE
+enumerator_set_size(VALUE obj, VALUE size)
+{
+ struct enumerator *e = enumerator_ptr(obj);
+ VALUE proc = rb_check_convert_type(size, T_DATA, "Proc", "to_proc");
+ e->size = NIL_P(proc) ? size : proc;
+ e->size_fn = 0;
+ return size;
+}
+
/*
* Yielder
*/
@@ -1099,6 +1200,8 @@ Init_Enumerator(void)
rb_define_method(rb_cEnumerator, "feed", enumerator_feed, 1);
rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
rb_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0);
+ rb_define_method(rb_cEnumerator, "size", enumerator_size, 0);
+ rb_define_method(rb_cEnumerator, "size=", enumerator_set_size, 1);
rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError);
rb_define_method(rb_eStopIteration, "result", stop_result, 0);
diff --git a/hash.c b/hash.c
index 8bba586..dd3f379 100644
--- a/hash.c
+++ b/hash.c
@@ -885,6 +885,8 @@ delete_if_i(VALUE key, VALUE value, VALUE hash)
return ST_CONTINUE;
}
+static VALUE rb_hash_size(VALUE hash);
+
/*
* call-seq:
* hsh.delete_if {| key, value | block } -> hsh
@@ -903,7 +905,7 @@ delete_if_i(VALUE key, VALUE value, VALUE hash)
VALUE
rb_hash_delete_if(VALUE hash)
{
- RETURN_ENUMERATOR(hash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
rb_hash_modify(hash);
rb_hash_foreach(hash, delete_if_i, hash);
return hash;
@@ -923,7 +925,7 @@ rb_hash_reject_bang(VALUE hash)
{
st_index_t n;
- RETURN_ENUMERATOR(hash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
rb_hash_modify(hash);
if (!RHASH(hash)->ntbl)
return Qnil;
@@ -1000,7 +1002,7 @@ rb_hash_select(VALUE hash)
{
VALUE result;
- RETURN_ENUMERATOR(hash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
result = rb_hash_new();
rb_hash_foreach(hash, select_i, result);
return result;
@@ -1030,7 +1032,7 @@ rb_hash_select_bang(VALUE hash)
{
st_index_t n;
- RETURN_ENUMERATOR(hash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
rb_hash_modify(hash);
if (!RHASH(hash)->ntbl)
return Qnil;
@@ -1055,7 +1057,7 @@ rb_hash_select_bang(VALUE hash)
VALUE
rb_hash_keep_if(VALUE hash)
{
- RETURN_ENUMERATOR(hash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
rb_hash_modify(hash);
rb_hash_foreach(hash, keep_if_i, hash);
return hash;
@@ -1239,7 +1241,7 @@ each_value_i(VALUE key, VALUE value)
static VALUE
rb_hash_each_value(VALUE hash)
{
- RETURN_ENUMERATOR(hash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
rb_hash_foreach(hash, each_value_i, 0);
return hash;
}
@@ -1273,7 +1275,7 @@ each_key_i(VALUE key, VALUE value)
static VALUE
rb_hash_each_key(VALUE hash)
{
- RETURN_ENUMERATOR(hash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
rb_hash_foreach(hash, each_key_i, 0);
return hash;
}
@@ -1311,7 +1313,7 @@ each_pair_i(VALUE key, VALUE value)
static VALUE
rb_hash_each_pair(VALUE hash)
{
- RETURN_ENUMERATOR(hash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
rb_hash_foreach(hash, each_pair_i, 0);
return hash;
}
@@ -2305,12 +2307,30 @@ env_keys(void)
}
static VALUE
+rb_env_size(VALUE ehash)
+{
+ char **env;
+ long cnt = 0;
+
+ rb_secure(4);
+
+ env = GET_ENVIRON(environ);
+ for (; *env ; ++env) {
+ if (strchr(*env, '=')) {
+ cnt++;
+ }
+ }
+ FREE_ENVIRON(environ);
+ return LONG2FIX(cnt);
+}
+
+static VALUE
env_each_key(VALUE ehash)
{
VALUE keys;
long i;
- RETURN_ENUMERATOR(ehash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
keys = env_keys(); /* rb_secure(4); */
for (i=0; i<RARRAY_LEN(keys); i++) {
rb_yield(RARRAY_PTR(keys)[i]);
@@ -2344,7 +2364,7 @@ env_each_value(VALUE ehash)
VALUE values;
long i;
- RETURN_ENUMERATOR(ehash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
values = env_values(); /* rb_secure(4); */
for (i=0; i<RARRAY_LEN(values); i++) {
rb_yield(RARRAY_PTR(values)[i]);
@@ -2359,7 +2379,7 @@ env_each_pair(VALUE ehash)
VALUE ary;
long i;
- RETURN_ENUMERATOR(ehash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
rb_secure(4);
ary = rb_ary_new();
@@ -2387,7 +2407,7 @@ env_reject_bang(VALUE ehash)
long i;
int del = 0;
- RETURN_ENUMERATOR(ehash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
keys = env_keys(); /* rb_secure(4); */
for (i=0; i<RARRAY_LEN(keys); i++) {
VALUE val = rb_f_getenv(Qnil, RARRAY_PTR(keys)[i]);
@@ -2406,7 +2426,7 @@ env_reject_bang(VALUE ehash)
static VALUE
env_delete_if(VALUE ehash)
{
- RETURN_ENUMERATOR(ehash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
env_reject_bang(ehash);
return envtbl;
}
@@ -2431,7 +2451,7 @@ env_select(VALUE ehash)
VALUE result;
char **env;
- RETURN_ENUMERATOR(ehash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
rb_secure(4);
result = rb_hash_new();
env = GET_ENVIRON(environ);
@@ -2458,7 +2478,7 @@ env_select_bang(VALUE ehash)
long i;
int del = 0;
- RETURN_ENUMERATOR(ehash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
keys = env_keys(); /* rb_secure(4); */
for (i=0; i<RARRAY_LEN(keys); i++) {
VALUE val = rb_f_getenv(Qnil, RARRAY_PTR(keys)[i]);
@@ -2477,7 +2497,7 @@ env_select_bang(VALUE ehash)
static VALUE
env_keep_if(VALUE ehash)
{
- RETURN_ENUMERATOR(ehash, 0, 0);
+ RETURN_SIZED_ENUMERATOR(ehash, 0, 0, rb_env_size);
env_select_bang(ehash);
return envtbl;
}
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index 0ae4d47..fd02da1 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -195,12 +195,13 @@ VALUE rb_fiber_current(void);
VALUE rb_fiber_alive_p(VALUE);
/* enum.c */
/* enumerator.c */
-VALUE rb_enumeratorize(VALUE, VALUE, int, VALUE *);
-#define RETURN_ENUMERATOR(obj, argc, argv) do { \
+VALUE rb_enumeratorize(VALUE, VALUE, int, VALUE *, VALUE (*)(ANYARGS));
+#define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn) do { \
if (!rb_block_given_p()) \
return rb_enumeratorize(obj, ID2SYM(rb_frame_this_func()), \
- argc, argv); \
+ argc, argv, size_fn); \
} while (0)
+#define RETURN_ENUMERATOR(obj, argc, argv) RETURN_SIZED_ENUMERATOR(obj, argc, argv, 0)
/* error.c */
VALUE rb_exc_new(VALUE, const char*, long);
VALUE rb_exc_new2(VALUE, const char*);
diff --git a/numeric.c b/numeric.c
index 740ef54..8e2ef90 100644
--- a/numeric.c
+++ b/numeric.c
@@ -97,7 +97,7 @@ round(double x)
}
#endif
-static ID id_coerce, id_to_i, id_eq;
+static ID id_coerce, id_to_i, id_eq, id_div;
VALUE rb_cNumeric;
VALUE rb_cFloat;
@@ -1596,6 +1596,44 @@ ruby_float_step(VALUE from, VALUE to, VALUE step, int excl)
return FALSE;
}
+static VALUE
+num_step_size(VALUE from, VALUE args) {
+ VALUE to = RARRAY_PTR(args)[0];
+ VALUE step = (RARRAY_LEN(args) > 1) ? RARRAY_PTR(args)[1] : INT2FIX(1);
+ if (FIXNUM_P(from) && FIXNUM_P(to) && FIXNUM_P(step)) {
+ long delta, diff, result;
+
+ delta = FIX2LONG(to) - FIX2LONG(from);
+ diff = FIX2LONG(step);
+ result = delta / diff + 1;
+ return LONG2FIX(result >= 0 ? result : 0);
+ }
+ else if (TYPE(from) == T_FLOAT || TYPE(to) == T_FLOAT || TYPE(step) == T_FLOAT) {
+ const double epsilon = DBL_EPSILON;
+ double beg = NUM2DBL(from);
+ double end = NUM2DBL(to);
+ double unit = NUM2DBL(step);
+ double n = (end - beg)/unit;
+ double err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon;
+
+ if (isinf(unit)) {
+ return (LONG2FIX(((unit > 0) == (end > beg)) || (end == beg)));
+ }
+ else {
+ if (n < 0) return LONG2FIX(0);
+ if (err>0.5) err=0.5;
+ return LONG2FIX(floor(n + err) + 1);
+ }
+ }
+ else {
+ VALUE delta;
+ ID cmp = RTEST(rb_funcall(step, '>', 1, INT2FIX(0))) ? '>' : '<';
+ if (RTEST(rb_funcall(from, cmp, 1, to))) return INT2FIX(0);
+ delta = rb_funcall(to, '-', 1, from);
+ return rb_funcall(rb_funcall(delta, id_div, 1, step), '+', 1, INT2FIX(1));
+ }
+}
+
/*
* call-seq:
* num.step(limit[, step]) {|i| block } -> self
@@ -1631,7 +1669,7 @@ num_step(int argc, VALUE *argv, VALUE from)
{
VALUE to, step;
- RETURN_ENUMERATOR(from, argc, argv);
+ RETURN_SIZED_ENUMERATOR(from, argc, argv, num_step_size);
if (argc == 1) {
to = argv[0];
step = INT2FIX(1);
@@ -3049,6 +3087,20 @@ fix_size(VALUE fix)
return INT2FIX(sizeof(long));
}
+static VALUE
+int_upto_size(VALUE from, VALUE args) {
+ VALUE to = RARRAY_PTR(args)[0];
+
+ if (FIXNUM_P(from) && FIXNUM_P(to)) {
+ long size = FIX2LONG(to) + 1 - FIX2LONG(from);
+ return LONG2NUM(size >= 0 ? size : 0);
+ }
+ else {
+ if (RTEST(rb_funcall(from, '>', 1, to))) return INT2FIX(0);
+ return rb_funcall(rb_funcall(to, '+', 1, INT2FIX(1)), '-', 1, from);
+ }
+}
+
/*
* call-seq:
* int.upto(limit) {|i| block } -> self
@@ -3069,7 +3121,7 @@ fix_size(VALUE fix)
static VALUE
int_upto(VALUE from, VALUE to)
{
- RETURN_ENUMERATOR(from, 1, &to);
+ RETURN_SIZED_ENUMERATOR(from, 1, &to, int_upto_size);
if (FIXNUM_P(from) && FIXNUM_P(to)) {
long i, end;
@@ -3090,6 +3142,20 @@ int_upto(VALUE from, VALUE to)
return from;
}
+static VALUE
+int_downto_size(VALUE from, VALUE args) {
+ VALUE to = RARRAY_PTR(args)[0];
+
+ if (FIXNUM_P(from) && FIXNUM_P(to)) {
+ long size = FIX2LONG(from) + 1 - FIX2LONG(to);
+ return LONG2NUM(size >= 0 ? size : 0);
+ }
+ else {
+ if (RTEST(rb_funcall(from, '<', 1, to))) return INT2FIX(0);
+ return rb_funcall(rb_funcall(from, '+', 1, INT2FIX(1)), '-', 1, to);
+ }
+}
+
/*
* call-seq:
* int.downto(limit) {|i| block } -> self
@@ -3111,7 +3177,7 @@ int_upto(VALUE from, VALUE to)
static VALUE
int_downto(VALUE from, VALUE to)
{
- RETURN_ENUMERATOR(from, 1, &to);
+ RETURN_SIZED_ENUMERATOR(from, 1, &to, int_downto_size);
if (FIXNUM_P(from) && FIXNUM_P(to)) {
long i, end;
@@ -3132,6 +3198,18 @@ int_downto(VALUE from, VALUE to)
return from;
}
+static VALUE
+int_dotimes_size(VALUE num)
+{
+ if (FIXNUM_P(num)) {
+ if (NUM2LONG(num) <= 0) return INT2FIX(0);
+ }
+ else {
+ if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) return INT2FIX(0);
+ }
+ return num;
+}
+
/*
* call-seq:
* int.times {|i| block } -> self
@@ -3154,7 +3232,7 @@ int_downto(VALUE from, VALUE to)
static VALUE
int_dotimes(VALUE num)
{
- RETURN_ENUMERATOR(num, 0, 0);
+ RETURN_SIZED_ENUMERATOR(num, 0, 0, int_dotimes_size);
if (FIXNUM_P(num)) {
long i, end;
@@ -3326,6 +3404,7 @@ Init_Numeric(void)
id_coerce = rb_intern("coerce");
id_to_i = rb_intern("to_i");
id_eq = rb_intern("==");
+ id_div = rb_intern("div");
rb_eZeroDivError = rb_define_class("ZeroDivisionError", rb_eStandardError);
rb_eFloatDomainError = rb_define_class("FloatDomainError", rb_eRangeError);
diff --git a/string.c b/string.c
index d18f47e..012027e 100644
--- a/string.c
+++ b/string.c
@@ -5867,6 +5867,11 @@ rb_str_each_line(int argc, VALUE *argv, VALUE str)
return orig;
}
+static VALUE
+rb_str_each_byte_size(VALUE str, VALUE args)
+{
+ return LONG2FIX(RSTRING_LEN(str));
+}
/*
* call-seq:
@@ -5891,13 +5896,27 @@ rb_str_each_byte(VALUE str)
{
long i;
- RETURN_ENUMERATOR(str, 0, 0);
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_byte_size);
for (i=0; i<RSTRING_LEN(str); i++) {
rb_yield(INT2FIX(RSTRING_PTR(str)[i] & 0xff));
}
return str;
}
+static VALUE
+rb_str_each_char_size(VALUE str)
+{
+ long len = RSTRING_LEN(str);
+ if (!single_byte_optimizable(str)) {
+ const char *ptr = RSTRING_PTR(str);
+ rb_encoding *enc = rb_enc_get(str);
+ const char *end_ptr = ptr + len;
+ for (len = 0; ptr < end_ptr; ++len) {
+ ptr += rb_enc_mbclen(ptr, end_ptr, enc);
+ }
+ }
+ return LONG2FIX(len);
+}
/*
* call-seq:
@@ -5925,7 +5944,7 @@ rb_str_each_char(VALUE str)
const char *ptr;
rb_encoding *enc;
- RETURN_ENUMERATOR(str, 0, 0);
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size);
str = rb_str_new4(str);
ptr = RSTRING_PTR(str);
len = RSTRING_LEN(str);
@@ -5979,7 +5998,7 @@ rb_str_each_codepoint(VALUE str)
rb_encoding *enc;
if (single_byte_optimizable(str)) return rb_str_each_byte(str);
- RETURN_ENUMERATOR(str, 0, 0);
+ RETURN_SIZED_ENUMERATOR(str, 0, 0, rb_str_each_char_size);
str = rb_str_new4(str);
ptr = RSTRING_PTR(str);
len = RSTRING_LEN(str);
diff --git a/struct.c b/struct.c
index f0a377c..c38ef53 100644
--- a/struct.c
+++ b/struct.c
@@ -439,6 +439,9 @@ rb_struct_new(VALUE klass, ...)
return rb_class_new_instance(size, mem, klass);
}
+static VALUE
+rb_struct_size(VALUE s);
+
/*
* call-seq:
* struct.each {|obj| block } -> struct
@@ -465,7 +468,7 @@ rb_struct_each(VALUE s)
{
long i;
- RETURN_ENUMERATOR(s, 0, 0);
+ RETURN_SIZED_ENUMERATOR(s, 0, 0, rb_struct_size);
for (i=0; i<RSTRUCT_LEN(s); i++) {
rb_yield(RSTRUCT_PTR(s)[i]);
}
@@ -499,7 +502,7 @@ rb_struct_each_pair(VALUE s)
VALUE members;
long i;
- RETURN_ENUMERATOR(s, 0, 0);
+ RETURN_SIZED_ENUMERATOR(s, 0, 0, rb_struct_size);
members = rb_struct_members(s);
for (i=0; i<RSTRUCT_LEN(s); i++) {
rb_yield_values(2, rb_ary_entry(members, i), RSTRUCT_PTR(s)[i]);
@@ -781,7 +784,7 @@ rb_struct_select(int argc, VALUE *argv, VALUE s)
if (argc > 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
}
- RETURN_ENUMERATOR(s, 0, 0);
+ RETURN_SIZED_ENUMERATOR(s, 0, 0, rb_struct_size);
result = rb_ary_new();
for (i = 0; i < RSTRUCT_LEN(s); i++) {
if (RTEST(rb_yield(RSTRUCT_PTR(s)[i]))) {
diff --git a/vm_eval.c b/vm_eval.c
index ca3fbba..6f2a892 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -791,6 +791,11 @@ loop_i(void)
return Qnil;
}
+static VALUE
+rb_f_loop_size(VALUE self, VALUE args) {
+ return DBL2NUM(INFINITY);
+}
+
/*
* call-seq:
* loop { block }
@@ -813,7 +818,7 @@ loop_i(void)
static VALUE
rb_f_loop(VALUE self)
{
- RETURN_ENUMERATOR(self, 0, 0);
+ RETURN_SIZED_ENUMERATOR(self, 0, 0, rb_f_loop_size);
rb_rescue2(loop_i, (VALUE)0, 0, 0, rb_eStopIteration, (VALUE)0);
return Qnil; /* dummy */
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.