[前][次][番号順一覧][スレッド一覧]

ruby-changes:53695

From: nobu <ko1@a...>
Date: Thu, 22 Nov 2018 14:51:45 +0900 (JST)
Subject: [ruby-changes:53695] nobu:r65911 (trunk): proc.c: Implement Proc#* for Proc composition

nobu	2018-11-22 14:51:40 +0900 (Thu, 22 Nov 2018)

  New Revision: 65911

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=65911

  Log:
    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@m...>

  Modified files:
    trunk/proc.c
    trunk/test/ruby/test_proc.rb
Index: test/ruby/test_proc.rb
===================================================================
--- test/ruby/test_proc.rb	(revision 65910)
+++ test/ruby/test_proc.rb	(revision 65911)
@@ -1416,4 +1416,55 @@ class TestProc < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_proc.rb#L1416
   def test_proc_without_block_for_symbol
     assert_equal('1', method_for_test_proc_without_block_for_symbol(&:to_s).call(1), '[Bug #14782]')
   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
Index: proc.c
===================================================================
--- proc.c	(revision 65910)
+++ proc.c	(revision 65911)
@@ -3046,6 +3046,59 @@ rb_method_curry(int argc, const VALUE *a https://github.com/ruby/ruby/blob/trunk/proc.c#L3046
     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
  *
@@ -3142,6 +3195,7 @@ Init_Proc(void) https://github.com/ruby/ruby/blob/trunk/proc.c#L3195
     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, "source_location", rb_proc_location, 0);
     rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0);
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]