proc.c: Implement Proc#* for Proc composition
* proc.c (proc_compose): Implement Proc#* for Proc composition, enabling composition of Procs and Methods. [Feature #6284] * test/ruby/test_proc.rb: Add test cases for Proc composition. From: Paul Mucur <mudge@mudge.name> git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65911 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
655257273a
commit
a43e967b8d
54
proc.c
54
proc.c
@ -3046,6 +3046,59 @@ rb_method_curry(int argc, const VALUE *argv, VALUE self)
|
|||||||
return proc_curry(argc, argv, proc);
|
return proc_curry(argc, argv, proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
compose(VALUE dummy, VALUE args, int argc, VALUE *argv, VALUE passed_proc)
|
||||||
|
{
|
||||||
|
VALUE f, g, fargs;
|
||||||
|
f = RARRAY_AREF(args, 0);
|
||||||
|
g = RARRAY_AREF(args, 1);
|
||||||
|
fargs = rb_ary_new3(1, rb_proc_call_with_block(g, argc, argv, passed_proc));
|
||||||
|
|
||||||
|
return rb_proc_call(f, fargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* prc * g -> a_proc
|
||||||
|
*
|
||||||
|
* Returns a proc that is the composition of this proc and the given proc <i>g</i>.
|
||||||
|
* The returned proc takes a variable number of arguments, calls <i>g</i> with them
|
||||||
|
* then calls this proc with the result.
|
||||||
|
*
|
||||||
|
* f = proc {|x| x * 2 }
|
||||||
|
* g = proc {|x, y| x + y }
|
||||||
|
* h = f * g
|
||||||
|
* p h.call(1, 2) #=> 6
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
proc_compose(VALUE self, VALUE g)
|
||||||
|
{
|
||||||
|
VALUE proc, args;
|
||||||
|
rb_proc_t *procp;
|
||||||
|
int is_lambda;
|
||||||
|
|
||||||
|
if (!rb_obj_is_method(g) && !rb_obj_is_proc(g)) {
|
||||||
|
rb_raise(rb_eTypeError,
|
||||||
|
"wrong argument type %s (expected Proc/Method)",
|
||||||
|
rb_obj_classname(g));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rb_obj_is_method(g)) {
|
||||||
|
g = method_to_proc(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
args = rb_ary_new3(2, self, g);
|
||||||
|
|
||||||
|
GetProcPtr(self, procp);
|
||||||
|
is_lambda = procp->is_lambda;
|
||||||
|
|
||||||
|
proc = rb_proc_new(compose, args);
|
||||||
|
GetProcPtr(proc, procp);
|
||||||
|
procp->is_lambda = is_lambda;
|
||||||
|
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Document-class: LocalJumpError
|
* Document-class: LocalJumpError
|
||||||
*
|
*
|
||||||
@ -3142,6 +3195,7 @@ Init_Proc(void)
|
|||||||
rb_define_method(rb_cProc, "lambda?", rb_proc_lambda_p, 0);
|
rb_define_method(rb_cProc, "lambda?", rb_proc_lambda_p, 0);
|
||||||
rb_define_method(rb_cProc, "binding", proc_binding, 0);
|
rb_define_method(rb_cProc, "binding", proc_binding, 0);
|
||||||
rb_define_method(rb_cProc, "curry", proc_curry, -1);
|
rb_define_method(rb_cProc, "curry", proc_curry, -1);
|
||||||
|
rb_define_method(rb_cProc, "*", proc_compose, 1);
|
||||||
rb_define_method(rb_cProc, "source_location", rb_proc_location, 0);
|
rb_define_method(rb_cProc, "source_location", rb_proc_location, 0);
|
||||||
rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0);
|
rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0);
|
||||||
|
|
||||||
|
@ -1416,4 +1416,55 @@ class TestProc < Test::Unit::TestCase
|
|||||||
def test_proc_without_block_for_symbol
|
def test_proc_without_block_for_symbol
|
||||||
assert_equal('1', method_for_test_proc_without_block_for_symbol(&:to_s).call(1), '[Bug #14782]')
|
assert_equal('1', method_for_test_proc_without_block_for_symbol(&:to_s).call(1), '[Bug #14782]')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_compose
|
||||||
|
f = proc {|x| x * 2}
|
||||||
|
g = proc {|x| x + 1}
|
||||||
|
h = f * g
|
||||||
|
|
||||||
|
assert_equal(6, h.call(2))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_compose_with_multiple_args
|
||||||
|
f = proc {|x| x * 2}
|
||||||
|
g = proc {|x, y| x + y}
|
||||||
|
h = f * g
|
||||||
|
|
||||||
|
assert_equal(6, h.call(1, 2))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_compose_with_block
|
||||||
|
f = proc {|x| x * 2}
|
||||||
|
g = proc {|&blk| blk.call(1) }
|
||||||
|
h = f * g
|
||||||
|
|
||||||
|
assert_equal(8, h.call { |x| x + 3 })
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_compose_with_lambda
|
||||||
|
f = lambda {|x| x * 2}
|
||||||
|
g = lambda {|x| x}
|
||||||
|
h = f * g
|
||||||
|
|
||||||
|
assert_predicate(h, :lambda?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_compose_with_method
|
||||||
|
f = proc {|x| x * 2}
|
||||||
|
c = Class.new {
|
||||||
|
def g(x) x + 1 end
|
||||||
|
}
|
||||||
|
g = c.new.method(:g)
|
||||||
|
h = f * g
|
||||||
|
|
||||||
|
assert_equal(6, h.call(2))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_compose_with_nonproc_or_method
|
||||||
|
f = proc {|x| x * 2}
|
||||||
|
|
||||||
|
assert_raise(TypeError) {
|
||||||
|
f * 5
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user