Skip to content

Commit

Permalink
Proc#<< and Proc#>>
Browse files Browse the repository at this point in the history
  • Loading branch information
nobu committed Nov 12, 2018
1 parent cd2d9ca commit 1c271e9
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 33 deletions.
87 changes: 76 additions & 11 deletions proc.c
Expand Up @@ -3052,26 +3052,33 @@ 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_funcall_with_block(g, idCall, argc, argv, passed_proc));

return rb_proc_call(f, fargs);
if (rb_obj_is_proc(g))

This comment has been minimized.

Copy link
@Itay289

Itay289 Nov 17, 2018

fargs = if (rb_obj_is_proc(g))

This comment has been minimized.

Copy link
@nobu

nobu Nov 21, 2018

Author Owner

What do you mean?

fargs = rb_proc_call_with_block(g, argc, argv, passed_proc);
else
fargs = rb_funcall_with_block(g, idCall, argc, argv, passed_proc);

if (rb_obj_is_proc(f))
return rb_proc_call(f, rb_ary_new3(1, fargs));
else
return rb_funcallv(f, idCall, 1, &fargs);
}

/*
* call-seq:
* prc * g -> a_proc
* prc << g -> a_proc
*
* Returns a proc that is the composition of this proc and the given <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
* h = f << g
* p h.call(1, 2) #=> 6
*/
static VALUE
proc_compose(VALUE self, VALUE g)
proc_compose_to_left(VALUE self, VALUE g)
{
VALUE proc, args;
rb_proc_t *procp;
Expand All @@ -3091,7 +3098,39 @@ proc_compose(VALUE self, VALUE g)

/*
* call-seq:
* meth * g -> a_proc
* prc << g -> a_proc

This comment has been minimized.

Copy link
@asterite

asterite Nov 12, 2018

Should be prc >> g -> a_proc

This comment has been minimized.

Copy link
@nobu

nobu Nov 13, 2018

Author Owner

Thank you, I think it had been fixed already.

*
* Returns a proc that is the composition of this proc and the given <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, y| x + y }
* g = proc {|x| x * 2 }
* h = f >> g
* p h.call(1, 2) #=> 6
*/
static VALUE
proc_compose_to_right(VALUE self, VALUE g)
{
VALUE proc, args;
rb_proc_t *procp;
int is_lambda;

args = rb_ary_new3(2, g, self);

GetProcPtr(self, procp);
is_lambda = procp->is_lambda;

proc = rb_proc_new(compose, args);
GetProcPtr(proc, procp);
procp->is_lambda = is_lambda;

return proc;
}

/*
* call-seq:
* meth << g -> a_proc
*
* Returns a proc that is the composition of this method and the given <i>g</i>.
* The returned proc takes a variable number of arguments, calls <i>g</i> with them
Expand All @@ -3103,14 +3142,38 @@ proc_compose(VALUE self, VALUE g)
*
* f = self.method(:f)
* g = proc {|x, y| x + y }
* h = f * g
* h = f << g
* p h.call(1, 2) #=> 6
*/
static VALUE
rb_method_compose_to_left(VALUE self, VALUE g)
{
VALUE proc = method_to_proc(self);
return proc_compose_to_left(proc, g);
}

/*
* call-seq:
* meth >> g -> a_proc
*
* Returns a proc that is the composition of this method and the given <i>g</i>.
* The returned proc takes a variable number of arguments, calls <i>g</i> with them
* then calls this method with the result.
*
* def f(x, y)
* x + y
* end
*
* f = self.method(:f)
* g = proc {|x| x * 2 }
* h = f >> g
* p h.call(1, 2) #=> 6
*/
static VALUE
rb_method_compose(VALUE self, VALUE g)
rb_method_compose_to_right(VALUE self, VALUE g)
{
VALUE proc = method_to_proc(self);
return proc_compose(proc, g);
return proc_compose_to_right(proc, g);
}

/*
Expand Down Expand Up @@ -3209,7 +3272,8 @@ Init_Proc(void)
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, "curry", proc_curry, -1);
rb_define_method(rb_cProc, "*", proc_compose, 1);
rb_define_method(rb_cProc, "<<", proc_compose_to_left, 1);
rb_define_method(rb_cProc, ">>", proc_compose_to_right, 1);
rb_define_method(rb_cProc, "source_location", rb_proc_location, 0);
rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0);

Expand All @@ -3236,7 +3300,8 @@ Init_Proc(void)
rb_define_method(rb_cMethod, "call", rb_method_call, -1);
rb_define_method(rb_cMethod, "===", rb_method_call, -1);
rb_define_method(rb_cMethod, "curry", rb_method_curry, -1);
rb_define_method(rb_cMethod, "*", rb_method_compose, 1);
rb_define_method(rb_cMethod, "<<", rb_method_compose_to_left, 1);
rb_define_method(rb_cMethod, ">>", rb_method_compose_to_right, 1);
rb_define_method(rb_cMethod, "[]", rb_method_call, -1);
rb_define_method(rb_cMethod, "arity", method_arity_m, 0);
rb_define_method(rb_cMethod, "inspect", method_inspect, 0);
Expand Down
19 changes: 11 additions & 8 deletions test/ruby/test_method.rb
Expand Up @@ -1048,9 +1048,9 @@ def g(x) x + 1 end
}
f = c.new.method(:f)
g = c.new.method(:g)
h = f * g

assert_equal(6, h.call(2))
assert_equal(6, (f << g).call(2))
assert_equal(6, (g >> f).call(2))
end

def test_compose_with_proc
Expand All @@ -1059,9 +1059,9 @@ def f(x) x * 2 end
}
f = c.new.method(:f)
g = proc {|x| x + 1}
h = f * g

assert_equal(6, h.call(2))
assert_equal(6, (f << g).call(2))
assert_equal(6, (g >> f).call(2))
end

def test_compose_with_callable
Expand All @@ -1072,20 +1072,23 @@ def f(x) x * 2 end
def call(x) x + 1 end
}
f = c.new.method(:f)
g = f * c2.new
g = c2.new

assert_equal(6, g.call(2))
assert_equal(6, (f << g).call(2))
assert_equal(5, (f >> g).call(2))
end

def test_compose_with_noncallable
c = Class.new {
def f(x) x * 2 end
}
f = c.new.method(:f)
g = f * 5

assert_raise(NoMethodError) {
g.call(2)
(f << 5).call(2)
}
assert_raise(NoMethodError) {
(f >> 5).call(2)
}
end
end
31 changes: 17 additions & 14 deletions test/ruby/test_proc.rb
Expand Up @@ -1420,33 +1420,33 @@ def test_proc_without_block_for_symbol
def test_compose
f = proc {|x| x * 2}
g = proc {|x| x + 1}
h = f * g

assert_equal(6, h.call(2))
assert_equal(6, (f << g).call(2))
assert_equal(6, (g >> f).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))
assert_equal(6, (f << g).call(1, 2))
assert_equal(6, (g >> f).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 })
assert_equal(8, (f << g).call { |x| x + 3 })
assert_equal(8, (g >> f).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?)
assert_predicate((f << g), :lambda?)
assert_predicate((g >> f), :lambda?)
end

def test_compose_with_method
Expand All @@ -1455,27 +1455,30 @@ def test_compose_with_method
def g(x) x + 1 end
}
g = c.new.method(:g)
h = f * g

assert_equal(6, h.call(2))
assert_equal(6, (f << g).call(2))
assert_equal(5, (f >> g).call(2))
end

def test_compose_with_callable
f = proc {|x| x * 2}
c = Class.new {
def call(x) x + 1 end
}
g = f * c.new
g = c.new

assert_equal(6, g.call(2))
assert_equal(6, (f << g).call(2))
assert_equal(5, (f >> g).call(2))
end

def test_compose_with_noncallable
f = proc {|x| x * 2}
g = f * 5

assert_raise(NoMethodError) {
g.call(2)
(f << 5).call(2)
}
assert_raise(NoMethodError) {
(f >> 5).call(2)
}
end
end

0 comments on commit 1c271e9

Please sign in to comment.