mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* enumerator.c: Finalize and document Lazy.new. [Bug #7248]
Add Lazy#to_enum and simplify Lazy#size. * test/ruby/test_lazy_enumerator.rb: tests for above git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@39057 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
5af821c6e9
commit
44cd5f21e9
3 changed files with 140 additions and 90 deletions
|
@ -1,3 +1,10 @@
|
|||
Tue Feb 5 12:48:10 2013 Marc-Andre Lafortune <ruby-core@marc-andre.ca>
|
||||
|
||||
* enumerator.c: Finalize and document Lazy.new. [Bug #7248]
|
||||
Add Lazy#to_enum and simplify Lazy#size.
|
||||
|
||||
* test/ruby/test_lazy_enumerator.rb: tests for above
|
||||
|
||||
Tue Feb 5 11:35:35 2013 Eric Hodel <drbrain@segment7.net>
|
||||
|
||||
* lib/rubygems/commands/push_command.rb: Fixed credential download for
|
||||
|
|
196
enumerator.c
196
enumerator.c
|
@ -457,6 +457,9 @@ rb_enumeratorize(VALUE obj, VALUE meth, int argc, VALUE *argv)
|
|||
return rb_enumeratorize_with_size(obj, meth, argc, argv, 0);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_to_enum_i(VALUE self, VALUE meth, int argc, VALUE *argv, VALUE (*size_fn)(ANYARGS));
|
||||
|
||||
VALUE
|
||||
rb_enumeratorize_with_size(VALUE obj, VALUE meth, int argc, VALUE *argv, VALUE (*size_fn)(ANYARGS))
|
||||
{
|
||||
|
@ -1022,7 +1025,7 @@ enumerator_size(VALUE obj)
|
|||
struct enumerator *e = enumerator_ptr(obj);
|
||||
|
||||
if (e->size_fn) {
|
||||
return (*e->size_fn)(e->obj, e->args);
|
||||
return (*e->size_fn)(e->obj, e->args, obj);
|
||||
}
|
||||
if (rb_obj_is_proc(e->size)) {
|
||||
if (e->args)
|
||||
|
@ -1271,20 +1274,22 @@ generator_each(int argc, VALUE *argv, VALUE obj)
|
|||
|
||||
/* Lazy Enumerator methods */
|
||||
static VALUE
|
||||
lazy_receiver_size(VALUE self)
|
||||
enum_size(VALUE self)
|
||||
{
|
||||
VALUE r = rb_check_funcall(rb_ivar_get(self, id_receiver), id_size, 0, 0);
|
||||
VALUE r = rb_check_funcall(self, id_size, 0, 0);
|
||||
return (r == Qundef) ? Qnil : r;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_size(VALUE self)
|
||||
{
|
||||
struct enumerator *e = enumerator_ptr(self);
|
||||
if (e->size_fn) {
|
||||
return (*e->size_fn)(self);
|
||||
}
|
||||
return Qnil;
|
||||
return enum_size(rb_ivar_get(self, id_receiver));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_receiver_size(VALUE generator, VALUE args, VALUE lazy)
|
||||
{
|
||||
return lazy_size(lazy);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
@ -1313,15 +1318,6 @@ lazy_init_iterator(VALUE val, VALUE m, int argc, VALUE *argv)
|
|||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_init_yielder(VALUE val, VALUE m, int argc, VALUE *argv)
|
||||
{
|
||||
VALUE result;
|
||||
result = rb_funcall2(m, id_yield, argc, argv);
|
||||
if (result == Qundef) rb_iter_break();
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_init_block_i(VALUE val, VALUE m, int argc, VALUE *argv)
|
||||
{
|
||||
|
@ -1329,39 +1325,51 @@ lazy_init_block_i(VALUE val, VALUE m, int argc, VALUE *argv)
|
|||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_init_block(VALUE val, VALUE m, int argc, VALUE *argv)
|
||||
{
|
||||
rb_block_call(m, id_each, argc-1, argv+1, lazy_init_yielder, val);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Lazy.new(obj, size=nil) { |yielder, *values| ... }
|
||||
*
|
||||
* Creates a new Lazy enumerator. When the enumerator is actually enumerated
|
||||
* (e.g. by calling #force), +obj+ will be enumerated and each value passed
|
||||
* to the given block. The block can yield values back using +yielder+.
|
||||
* For example, to create a method +filter_map+ in both lazy and
|
||||
* non-lazy fashions:
|
||||
*
|
||||
* module Enumerable
|
||||
* def filter_map(&block)
|
||||
* map(&block).compact
|
||||
* end
|
||||
* end
|
||||
*
|
||||
* class Enumerator::Lazy
|
||||
* def filter_map
|
||||
* Lazy.new(self) do |yielder, *values|
|
||||
* result = yield *values
|
||||
* yielder << result if result
|
||||
* end
|
||||
* end
|
||||
* end
|
||||
*
|
||||
* (1..Float::INFINITY).lazy.filter_map{|i| i*i if i.even?}.first(5)
|
||||
* # => [4, 16, 36, 64, 100]
|
||||
*/
|
||||
static VALUE
|
||||
lazy_initialize(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE obj, meth;
|
||||
VALUE obj, size = Qnil;
|
||||
VALUE generator;
|
||||
int offset;
|
||||
|
||||
if (argc < 1) {
|
||||
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1..)", argc);
|
||||
rb_check_arity(argc, 1, 2);
|
||||
if (!rb_block_given_p()) {
|
||||
rb_raise(rb_eArgError, "tried to call lazy new without a block");
|
||||
}
|
||||
else {
|
||||
obj = argv[0];
|
||||
if (argc == 1) {
|
||||
meth = sym_each;
|
||||
offset = 1;
|
||||
}
|
||||
else {
|
||||
meth = argv[1];
|
||||
offset = 2;
|
||||
}
|
||||
obj = argv[0];
|
||||
if (argc > 1) {
|
||||
size = argv[1];
|
||||
}
|
||||
generator = generator_allocate(rb_cGenerator);
|
||||
rb_block_call(generator, id_initialize, 0, 0,
|
||||
(rb_block_given_p() ? lazy_init_block_i : lazy_init_block),
|
||||
obj);
|
||||
enumerator_init(self, generator, meth, argc - offset, argv + offset, lazy_receiver_size, Qnil);
|
||||
rb_block_call(generator, id_initialize, 0, 0, lazy_init_block_i, obj);
|
||||
enumerator_init(self, generator, sym_each, 0, 0, 0, size);
|
||||
rb_ivar_set(self, id_receiver, obj);
|
||||
|
||||
return self;
|
||||
|
@ -1418,14 +1426,58 @@ lazy_set_method(VALUE lazy, VALUE args, VALUE (*size_fn)(ANYARGS))
|
|||
static VALUE
|
||||
enumerable_lazy(VALUE obj)
|
||||
{
|
||||
VALUE result;
|
||||
|
||||
result = rb_class_new_instance(1, &obj, rb_cLazy);
|
||||
VALUE result = lazy_to_enum_i(obj, sym_each, 0, 0, enum_size);
|
||||
/* Qfalse indicates that the Enumerator::Lazy has no method name */
|
||||
rb_ivar_set(result, id_method, Qfalse);
|
||||
return result;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_to_enum_i(VALUE obj, VALUE meth, int argc, VALUE *argv, VALUE (*size_fn)(ANYARGS))
|
||||
{
|
||||
return enumerator_init(enumerator_allocate(rb_cLazy),
|
||||
obj, meth, argc, argv, size_fn, Qnil);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* lzy.to_enum(method = :each, *args) -> lazy_enum
|
||||
* lzy.enum_for(method = :each, *args) -> lazy_enum
|
||||
* lzy.to_enum(method = :each, *args) {|*args| block} -> lazy_enum
|
||||
* lzy.enum_for(method = :each, *args){|*args| block} -> lazy_enum
|
||||
*
|
||||
* Similar to Kernel#to_enum, except it returns a lazy enumerator.
|
||||
* This makes it easy to define Enumerable methods that will
|
||||
* naturally remain lazy if called from a lazy enumerator.
|
||||
*
|
||||
* For example, continuing from the example in Kernel#to_enum:
|
||||
*
|
||||
* # See Kernel#to_enum for the definition of repeat
|
||||
* r = 1..Float::INFINITY
|
||||
* r.repeat(2).first(5) # => [1, 1, 2, 2, 3]
|
||||
* r.repeat(2).class # => Enumerator
|
||||
* r.repeat(2).map{|n| n ** 2}.first(5) # => endless loop!
|
||||
* # works naturally on lazy enumerator:
|
||||
* r.lazy.repeat(2).class # => Enumerator::Lazy
|
||||
* r.lazy.repeat(2).map{|n| n ** 2}.first(5) # => [1, 1, 4, 4, 9]
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
lazy_to_enum(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
VALUE lazy, meth = sym_each;
|
||||
|
||||
if (argc > 0) {
|
||||
--argc;
|
||||
meth = *argv++;
|
||||
}
|
||||
lazy = lazy_to_enum_i(self, meth, argc, argv, 0);
|
||||
if (rb_block_given_p()) {
|
||||
enumerator_ptr(lazy)->size = rb_block_proc();
|
||||
}
|
||||
return lazy;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_map_func(VALUE val, VALUE m, int argc, VALUE *argv)
|
||||
{
|
||||
|
@ -1723,10 +1775,10 @@ lazy_take_func(VALUE val, VALUE args, int argc, VALUE *argv)
|
|||
}
|
||||
|
||||
static VALUE
|
||||
lazy_take_size(VALUE lazy)
|
||||
lazy_take_size(VALUE generator, VALUE args, VALUE lazy)
|
||||
{
|
||||
VALUE receiver = lazy_size(lazy);
|
||||
long len = NUM2LONG(RARRAY_PTR(rb_ivar_get(lazy, id_arguments))[0]);
|
||||
VALUE receiver = lazy_receiver_size(lazy);
|
||||
if (NIL_P(receiver) || (FIXNUM_P(receiver) && FIX2LONG(receiver) < len))
|
||||
return receiver;
|
||||
return LONG2NUM(len);
|
||||
|
@ -1736,21 +1788,20 @@ static VALUE
|
|||
lazy_take(VALUE obj, VALUE n)
|
||||
{
|
||||
long len = NUM2LONG(n);
|
||||
int argc = 1;
|
||||
VALUE argv[3];
|
||||
VALUE lazy;
|
||||
|
||||
if (len < 0) {
|
||||
rb_raise(rb_eArgError, "attempt to take negative size");
|
||||
}
|
||||
argv[0] = obj;
|
||||
if (len == 0) {
|
||||
argv[1] = sym_cycle;
|
||||
argv[2] = INT2NUM(0);
|
||||
argc = 3;
|
||||
VALUE len = INT2NUM(0);
|
||||
lazy = lazy_to_enum_i(obj, sym_cycle, 1, &len, 0);
|
||||
}
|
||||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, argc, argv,
|
||||
lazy_take_func, n),
|
||||
rb_ary_new3(1, n), lazy_take_size);
|
||||
else {
|
||||
lazy = rb_block_call(rb_cLazy, id_new, 1, &obj,
|
||||
lazy_take_func, n);
|
||||
}
|
||||
return lazy_set_method(lazy, rb_ary_new3(1, n), lazy_take_size);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
@ -1774,10 +1825,10 @@ lazy_take_while(VALUE obj)
|
|||
}
|
||||
|
||||
static VALUE
|
||||
lazy_drop_size(VALUE lazy)
|
||||
lazy_drop_size(VALUE generator, VALUE args, VALUE lazy)
|
||||
{
|
||||
long len = NUM2LONG(RARRAY_PTR(rb_ivar_get(lazy, id_arguments))[0]);
|
||||
VALUE receiver = lazy_receiver_size(lazy);
|
||||
VALUE receiver = lazy_size(lazy);
|
||||
if (NIL_P(receiver))
|
||||
return receiver;
|
||||
if (FIXNUM_P(receiver)) {
|
||||
|
@ -1841,37 +1892,13 @@ lazy_drop_while(VALUE obj)
|
|||
Qnil, 0);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_cycle_size(VALUE lazy)
|
||||
{
|
||||
return rb_enum_cycle_size(rb_ivar_get(lazy, id_receiver), rb_ivar_get(lazy, id_arguments));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_cycle_func(VALUE val, VALUE m, int argc, VALUE *argv)
|
||||
{
|
||||
return rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
lazy_cycle(int argc, VALUE *argv, VALUE obj)
|
||||
{
|
||||
VALUE args;
|
||||
int len = rb_long2int((long)argc + 2);
|
||||
|
||||
if (rb_block_given_p()) {
|
||||
return rb_call_super(argc, argv);
|
||||
}
|
||||
args = rb_ary_tmp_new(len);
|
||||
rb_ary_push(args, obj);
|
||||
rb_ary_push(args, sym_cycle);
|
||||
if (argc > 0) {
|
||||
rb_ary_cat(args, argv, argc);
|
||||
}
|
||||
return lazy_set_method(rb_block_call(rb_cLazy, id_new, len,
|
||||
RARRAY_PTR(args), lazy_cycle_func,
|
||||
args /* prevent from GC */),
|
||||
rb_ary_new4(argc, argv), lazy_cycle_size);
|
||||
return lazy_to_enum_i(obj, sym_cycle, argc, argv, rb_enum_cycle_size);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
@ -1963,12 +1990,13 @@ InitVM_Enumerator(void)
|
|||
rb_cLazy = rb_define_class_under(rb_cEnumerator, "Lazy", rb_cEnumerator);
|
||||
rb_define_method(rb_mEnumerable, "lazy", enumerable_lazy, 0);
|
||||
rb_define_method(rb_cLazy, "initialize", lazy_initialize, -1);
|
||||
rb_define_method(rb_cLazy, "to_enum", lazy_to_enum, -1);
|
||||
rb_define_method(rb_cLazy, "enum_for", lazy_to_enum, -1);
|
||||
rb_define_method(rb_cLazy, "map", lazy_map, 0);
|
||||
rb_define_method(rb_cLazy, "collect", lazy_map, 0);
|
||||
rb_define_method(rb_cLazy, "flat_map", lazy_flat_map, 0);
|
||||
rb_define_method(rb_cLazy, "collect_concat", lazy_flat_map, 0);
|
||||
rb_define_method(rb_cLazy, "select", lazy_select, 0);
|
||||
rb_define_method(rb_cLazy, "size", lazy_size, 0);
|
||||
rb_define_method(rb_cLazy, "find_all", lazy_select, 0);
|
||||
rb_define_method(rb_cLazy, "reject", lazy_reject, 0);
|
||||
rb_define_method(rb_cLazy, "grep", lazy_grep, 1);
|
||||
|
|
|
@ -20,7 +20,8 @@ class TestLazyEnumerator < Test::Unit::TestCase
|
|||
|
||||
def test_initialize
|
||||
assert_equal([1, 2, 3], [1, 2, 3].lazy.to_a)
|
||||
assert_equal([1, 2, 3], Enumerator::Lazy.new([1, 2, 3]).to_a)
|
||||
assert_equal([1, 2, 3], Enumerator::Lazy.new([1, 2, 3]){|y, v| y << v}.to_a)
|
||||
assert_raise(ArgumentError) { Enumerator::Lazy.new([1, 2, 3]) }
|
||||
end
|
||||
|
||||
def test_each_args
|
||||
|
@ -361,10 +362,6 @@ class TestLazyEnumerator < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_inspect
|
||||
assert_equal("#<Enumerator::Lazy: 1..10:each>",
|
||||
Enumerator::Lazy.new(1..10).inspect)
|
||||
assert_equal("#<Enumerator::Lazy: 1..10:cycle(2)>",
|
||||
Enumerator::Lazy.new(1..10, :cycle, 2).inspect)
|
||||
assert_equal("#<Enumerator::Lazy: 1..10>", (1..10).lazy.inspect)
|
||||
assert_equal('#<Enumerator::Lazy: #<Enumerator: "foo":each_char>>',
|
||||
"foo".each_char.lazy.inspect)
|
||||
|
@ -388,10 +385,28 @@ class TestLazyEnumerator < Test::Unit::TestCase
|
|||
EOS
|
||||
end
|
||||
|
||||
def test_lazy_to_enum
|
||||
lazy = [1, 2, 3].lazy
|
||||
def lazy.foo(*args)
|
||||
yield args
|
||||
yield args
|
||||
end
|
||||
enum = lazy.to_enum(:foo, :hello, :world)
|
||||
assert_equal Enumerator::Lazy, enum.class
|
||||
assert_equal nil, enum.size
|
||||
assert_equal [[:hello, :world], [:hello, :world]], enum.to_a
|
||||
|
||||
assert_equal [1, 2, 3], lazy.to_enum.to_a
|
||||
end
|
||||
|
||||
def test_size
|
||||
lazy = [1, 2, 3].lazy
|
||||
assert_equal 3, lazy.size
|
||||
assert_equal 42, Enumerator.new(42){}.lazy.size
|
||||
assert_equal 42, Enumerator::Lazy.new([],->{42}){}.size
|
||||
assert_equal 42, Enumerator::Lazy.new([],42){}.size
|
||||
assert_equal 42, Enumerator::Lazy.new([],42){}.lazy.size
|
||||
assert_equal 42, lazy.to_enum{ 42 }.size
|
||||
|
||||
%i[map collect].each do |m|
|
||||
assert_equal 3, lazy.send(m){}.size
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue