类 Proc

一个 Proc 对象是代码块的封装,可以存储在局部变量中,传递给方法或另一个 Proc,并且可以调用。 Proc 是 Ruby 中的一个重要概念,也是其函数式编程特性的核心。

square = Proc.new {|x| x**2 }

square.call(3)  #=> 9
# shorthands:
square.(3)      #=> 9
square[3]       #=> 9

Proc 对象是闭包,这意味着它们会记住并可以使用创建它们的整个上下文。

def gen_times(factor)
  Proc.new {|n| n*factor } # remembers the value of factor at the moment of creation
end

times3 = gen_times(3)
times5 = gen_times(5)

times3.call(12)               #=> 36
times5.call(5)                #=> 25
times3.call(times5.call(4))   #=> 60

创建

有几种方法可以创建 Proc

  • 使用 Proc 类构造函数

    proc1 = Proc.new {|x| x**2 }
    
  • 使用 Kernel#proc 方法作为 Proc.new 的简写

    proc2 = proc {|x| x**2 }
    
  • 将代码块接收为 proc 参数(注意 &

    def make_proc(&block)
      block
    end
    
    proc3 = make_proc {|x| x**2 }
    
  • 使用 Kernel#lambda 方法构造具有 lambda 语义的 proc(有关 lambda 的解释,请参见下文)

    lambda1 = lambda {|x| x**2 }
    
  • 使用 Lambda proc 字面量 语法(也构造具有 lambda 语义的 proc)

    lambda2 = ->(x) { x**2 }
    

Lambda 和非 Lambda 语义

Proc 有两种类型:lambda 和非 lambda(普通 proc)。区别在于

  • 在 lambda 中,returnbreak 表示退出此 lambda;

  • 在非 lambda proc 中,return 表示退出包含方法(如果在方法外部调用,则会抛出 LocalJumpError);

  • 在非 lambda proc 中,break 表示退出传递给该块的方法。(如果在方法返回后调用,则会抛出 LocalJumpError);

  • 在 lambda 中,参数的处理方式与方法相同:严格,对于不匹配的参数数量会抛出 ArgumentError,并且没有额外的参数处理;

  • 普通 proc 更宽松地接受参数:缺少的参数将用 nil 填充,如果 proc 有多个参数,则单个 Array 参数会被解构,并且不会因额外的参数而引发错误。

示例

# +return+ in non-lambda proc, +b+, exits +m2+.
# (The block +{ return }+ is given for +m1+ and embraced by +m2+.)
$a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1 { return }; $a << :m2 end; m2; p $a
#=> []

# +break+ in non-lambda proc, +b+, exits +m1+.
# (The block +{ break }+ is given for +m1+ and embraced by +m2+.)
$a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1 { break }; $a << :m2 end; m2; p $a
#=> [:m2]

# +next+ in non-lambda proc, +b+, exits the block.
# (The block +{ next }+ is given for +m1+ and embraced by +m2+.)
$a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1 { next }; $a << :m2 end; m2; p $a
#=> [:m1, :m2]

# Using +proc+ method changes the behavior as follows because
# The block is given for +proc+ method and embraced by +m2+.
$a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&proc { return }); $a << :m2 end; m2; p $a
#=> []
$a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&proc { break }); $a << :m2 end; m2; p $a
# break from proc-closure (LocalJumpError)
$a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&proc { next }); $a << :m2 end; m2; p $a
#=> [:m1, :m2]

# +return+, +break+ and +next+ in the stubby lambda exits the block.
# (+lambda+ method behaves same.)
# (The block is given for stubby lambda syntax and embraced by +m2+.)
$a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&-> { return }); $a << :m2 end; m2; p $a
#=> [:m1, :m2]
$a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&-> { break }); $a << :m2 end; m2; p $a
#=> [:m1, :m2]
$a = []; def m1(&b) b.call; $a << :m1 end; def m2() m1(&-> { next }); $a << :m2 end; m2; p $a
#=> [:m1, :m2]

p = proc {|x, y| "x=#{x}, y=#{y}" }
p.call(1, 2)      #=> "x=1, y=2"
p.call([1, 2])    #=> "x=1, y=2", array deconstructed
p.call(1, 2, 8)   #=> "x=1, y=2", extra argument discarded
p.call(1)         #=> "x=1, y=", nil substituted instead of error

l = lambda {|x, y| "x=#{x}, y=#{y}" }
l.call(1, 2)      #=> "x=1, y=2"
l.call([1, 2])    # ArgumentError: wrong number of arguments (given 1, expected 2)
l.call(1, 2, 8)   # ArgumentError: wrong number of arguments (given 3, expected 2)
l.call(1)         # ArgumentError: wrong number of arguments (given 1, expected 2)

def test_return
  -> { return 3 }.call      # just returns from lambda into method body
  proc { return 4 }.call    # returns from method
  return 5
end

test_return # => 4, return from proc

Lambda 函数作为自包含的函数非常有用,特别是在作为高阶函数的参数时,其行为与 Ruby 方法完全一致。

Proc 用于实现迭代器。

def test
  [[1, 2], [3, 4], [5, 6]].map {|a, b| return a if a + b > 10 }
                            #  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
end

map 内部,代码块被视为常规(非 lambda)proc,这意味着内部数组将被解构为成对的参数,并且 return 将退出方法 test。对于更严格的 lambda,这将是不可能的。

可以使用 lambda? 实例方法区分 lambda 和常规 proc。

Lambda 语义通常在 proc 的生命周期内保留,包括 & 解构为代码块。

p = proc {|x, y| x }
l = lambda {|x, y| x }
[[1, 2], [3, 4]].map(&p) #=> [1, 3]
[[1, 2], [3, 4]].map(&l) # ArgumentError: wrong number of arguments (given 1, expected 2)

唯一的例外是动态方法定义:即使通过传递非 lambda proc 定义,方法仍然具有正常的参数检查语义。

class C
  define_method(:e, &proc {})
end
C.new.e(1,2)       #=> ArgumentError
C.new.method(:e).to_proc.lambda?   #=> true

此例外确保方法永远不会有异常的参数传递约定,并且可以轻松地创建定义行为如常的方法的包装器。

class C
  def self.def2(name, &body)
    define_method(name, &body)
  end

  def2(:f) {}
end
C.new.f(1,2)       #=> ArgumentError

包装器 def2 接收 body 作为非 lambda proc,但定义了一个具有正常语义的方法。

将其他对象转换为 proc

任何实现了 to_proc 方法的对象都可以通过 & 运算符转换为 proc,因此可以被迭代器使用。

class Greeter
  def initialize(greeting)
    @greeting = greeting
  end

  def to_proc
    proc {|name| "#{@greeting}, #{name}!" }
  end
end

hi = Greeter.new("Hi")
hey = Greeter.new("Hey")
["Bob", "Jane"].map(&hi)    #=> ["Hi, Bob!", "Hi, Jane!"]
["Bob", "Jane"].map(&hey)   #=> ["Hey, Bob!", "Hey, Jane!"]

在 Ruby 核心类中,SymbolMethodHash 实现了此方法。

:to_s.to_proc.call(1)           #=> "1"
[1, 2].map(&:to_s)              #=> ["1", "2"]

method(:puts).to_proc.call(1)   # prints 1
[1, 2].each(&method(:puts))     # prints 1, 2

{test: 1}.to_proc.call(:test)       #=> 1
%i[test many keys].map(&{test: 1})  #=> [1, nil, nil]

孤立的 Proc

块中的 returnbreak 会退出方法。如果从块中生成 Proc 对象,并且 Proc 对象在方法返回之前一直存在,则 returnbreak 无法工作。在这种情况下,returnbreak 会引发 LocalJumpError。在这种情况下,Proc 对象被称为孤立的 Proc 对象。

请注意,returnbreak 的退出方法不同。存在对 break 而言是孤立的,但对 return 而言不是孤立的情况。

def m1(&b) b.call end; def m2(); m1 { return } end; m2 # ok
def m1(&b) b.call end; def m2(); m1 { break } end; m2 # ok

def m1(&b) b end; def m2(); m1 { return }.call end; m2 # ok
def m1(&b) b end; def m2(); m1 { break }.call end; m2 # LocalJumpError

def m1(&b) b end; def m2(); m1 { return } end; m2.call # LocalJumpError
def m1(&b) b end; def m2(); m1 { break } end; m2.call # LocalJumpError

由于 returnbreak 在 lambda 中退出块本身,因此 lambda 不会成为孤立的。

编号参数

编号参数是隐式定义的块参数,旨在简化编写简短的块。

# Explicit parameter:
%w[test me please].each { |str| puts str.upcase } # prints TEST, ME, PLEASE
(1..5).map { |i| i**2 } # => [1, 4, 9, 16, 25]

# Implicit parameter:
%w[test me please].each { puts _1.upcase } # prints TEST, ME, PLEASE
(1..5).map { _1**2 } # => [1, 4, 9, 16, 25]

支持从 _1_9 的参数名称。

[10, 20, 30].zip([40, 50, 60], [70, 80, 90]).map { _1 + _2 + _3 }
# => [120, 150, 180]

虽然建议明智地使用它们,可能将自己限制在_1_2,以及单行块。

编号参数不能与显式命名的参数一起使用。

[10, 20, 30].map { |x| _1**2 }
# SyntaxError (ordinary parameter is defined)

为了避免冲突,将局部变量或方法参数命名为_1_2等会引发警告。

_1 = 'test'
# warning: `_1' is reserved as numbered parameter

使用隐式编号参数会影响块的元数。

p = proc { _1 + _2 }
l = lambda { _1 + _2 }
p.parameters     # => [[:opt, :_1], [:opt, :_2]]
p.arity          # => 2
l.parameters     # => [[:req, :_1], [:req, :_2]]
l.arity          # => 2

带有编号参数的块不能嵌套。

%w[test me].each { _1.each_char { p _1 } }
# SyntaxError (numbered parameter is already used in outer block here)
# %w[test me].each { _1.each_char { p _1 } }
#                    ^~

编号参数是在 Ruby 2.7 中引入的。

公共类方法

new {|...| block } → a_proc 点击切换源代码

创建一个新的Proc对象,绑定到当前上下文。

proc = Proc.new { "hello" }
proc.call   #=> "hello"

如果在没有块的情况下调用,则会引发ArgumentError

Proc.new    #=> ArgumentError
static VALUE
rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
{
    VALUE block = proc_new(klass, FALSE);

    rb_obj_call_init_kw(block, argc, argv, RB_PASS_CALLED_KEYWORDS);
    return block;
}

公共实例方法

prc << g → a_proc 点击切换源代码

返回一个 proc,它是此 proc 和给定 g 的组合。返回的 proc 接受可变数量的参数,用它们调用 g,然后用结果调用此 proc。

f = proc {|x| x * x }
g = proc {|x| x + x }
p (f << g).call(2) #=> 16

有关详细说明,请参见Proc#>>

static VALUE
proc_compose_to_left(VALUE self, VALUE g)
{
    return rb_proc_compose_to_left(self, to_callable(g));
}
prc == other → true or false 点击切换源代码

如果且仅当两个 proc 是从同一个代码块创建的,它们才相同。

def return_block(&block)
  block
end

def pass_block_twice(&block)
  [return_block(&block), return_block(&block)]
end

block1, block2 = pass_block_twice { puts 'test' }
# Blocks might be instantiated into Proc's lazily, so they may, or may not,
# be the same object.
# But they are produced from the same code block, so they are equal
block1 == block2
#=> true

# Another Proc will never be equal, even if the code is the "same"
block1 == proc { puts 'test' }
#=> false
static VALUE
proc_eq(VALUE self, VALUE other)
{
    const rb_proc_t *self_proc, *other_proc;
    const struct rb_block *self_block, *other_block;

    if (rb_obj_class(self) !=  rb_obj_class(other)) {
        return Qfalse;
    }

    GetProcPtr(self, self_proc);
    GetProcPtr(other, other_proc);

    if (self_proc->is_from_method != other_proc->is_from_method ||
            self_proc->is_lambda != other_proc->is_lambda) {
        return Qfalse;
    }

    self_block = &self_proc->block;
    other_block = &other_proc->block;

    if (vm_block_type(self_block) != vm_block_type(other_block)) {
        return Qfalse;
    }

    switch (vm_block_type(self_block)) {
      case block_type_iseq:
        if (self_block->as.captured.ep != \
                other_block->as.captured.ep ||
                self_block->as.captured.code.iseq != \
                other_block->as.captured.code.iseq) {
            return Qfalse;
        }
        break;
      case block_type_ifunc:
        if (self_block->as.captured.ep != \
                other_block->as.captured.ep ||
                self_block->as.captured.code.ifunc != \
                other_block->as.captured.code.ifunc) {
            return Qfalse;
        }
        break;
      case block_type_proc:
        if (self_block->as.proc != other_block->as.proc) {
            return Qfalse;
        }
        break;
      case block_type_symbol:
        if (self_block->as.symbol != other_block->as.symbol) {
            return Qfalse;
        }
        break;
    }

    return Qtrue;
}
也称为:eql?
(params,...) → obj

调用块,使用类似于方法调用语义将块的参数设置为 params 中的值。返回块中最后评估的表达式的值。

a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
a_proc.call(9, 1, 2, 3)    #=> [9, 18, 27]
a_proc[9, 1, 2, 3]         #=> [9, 18, 27]
a_proc.(9, 1, 2, 3)        #=> [9, 18, 27]
a_proc.yield(9, 1, 2, 3)   #=> [9, 18, 27]

请注意,prc.() 使用给定的参数调用 prc.call()。它是隐藏“call”的语法糖。

对于使用lambda->()创建的 proc,如果传递给 proc 的参数数量错误,则会生成错误。对于使用Proc.newKernel.proc创建的 proc,多余的参数会被静默丢弃,缺少的参数会被设置为nil

a_proc = proc {|a,b| [a,b] }
a_proc.call(1)   #=> [1, nil]

a_proc = lambda {|a,b| [a,b] }
a_proc.call(1)   # ArgumentError: wrong number of arguments (given 1, expected 2)

另请参见Proc#lambda?

别名:call
prc >> g → a_proc 点击切换源代码

返回一个 proc,它是此 proc 和给定 g 的组合。返回的 proc 接收可变数量的参数,用它们调用此 proc,然后用结果调用 g

f = proc {|x| x * x }
g = proc {|x| x + x }
p (f >> g).call(2) #=> 8

g 可以是其他 Proc,或 Method,或任何其他响应 call 方法的对象

class Parser
  def self.call(text)
     # ...some complicated parsing logic...
  end
end

pipeline = File.method(:read) >> Parser >> proc { |data| puts "data size: #{data.count}" }
pipeline.call('data.json')

另请参见 Method#>>Method#<<

static VALUE
proc_compose_to_right(VALUE self, VALUE g)
{
    return rb_proc_compose_to_right(self, to_callable(g));
}
prc[params,...] → obj
(params,...) → obj

调用块,使用类似于方法调用语义将块的参数设置为 params 中的值。返回块中最后评估的表达式的值。

a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
a_proc.call(9, 1, 2, 3)    #=> [9, 18, 27]
a_proc[9, 1, 2, 3]         #=> [9, 18, 27]
a_proc.(9, 1, 2, 3)        #=> [9, 18, 27]
a_proc.yield(9, 1, 2, 3)   #=> [9, 18, 27]

请注意,prc.() 使用给定的参数调用 prc.call()。它是隐藏“call”的语法糖。

对于使用lambda->()创建的 proc,如果传递给 proc 的参数数量错误,则会生成错误。对于使用Proc.newKernel.proc创建的 proc,多余的参数会被静默丢弃,缺少的参数会被设置为nil

a_proc = proc {|a,b| [a,b] }
a_proc.call(1)   #=> [1, nil]

a_proc = lambda {|a,b| [a,b] }
a_proc.call(1)   # ArgumentError: wrong number of arguments (given 1, expected 2)

另请参见Proc#lambda?

别名:call
arity → integer 点击切换源代码

返回强制参数的数量。如果块声明为不接受任何参数,则返回 0。如果块已知恰好接受 n 个参数,则返回 n。如果块具有可选参数,则返回 -n-1,其中 n 是强制参数的数量,但对于不是 lambda 且只有有限数量的可选参数的块除外;在这种情况下,返回 n。关键字参数将被视为一个额外的强制参数,如果任何关键字参数是强制的,则该参数也是强制的。一个没有参数声明的 proc 与声明 || 作为其参数的块相同。

proc {}.arity                  #=>  0
proc { || }.arity              #=>  0
proc { |a| }.arity             #=>  1
proc { |a, b| }.arity          #=>  2
proc { |a, b, c| }.arity       #=>  3
proc { |*a| }.arity            #=> -1
proc { |a, *b| }.arity         #=> -2
proc { |a, *b, c| }.arity      #=> -3
proc { |x:, y:, z:0| }.arity   #=>  1
proc { |*a, x:, y:0| }.arity   #=> -2

proc   { |a=0| }.arity         #=>  0
lambda { |a=0| }.arity         #=> -1
proc   { |a=0, b| }.arity      #=>  1
lambda { |a=0, b| }.arity      #=> -2
proc   { |a=0, b=0| }.arity    #=>  0
lambda { |a=0, b=0| }.arity    #=> -1
proc   { |a, b=0| }.arity      #=>  1
lambda { |a, b=0| }.arity      #=> -2
proc   { |(a, b), c=0| }.arity #=>  1
lambda { |(a, b), c=0| }.arity #=> -2
proc   { |a, x:0, y:0| }.arity #=>  1
lambda { |a, x:0, y:0| }.arity #=> -2
static VALUE
proc_arity(VALUE self)
{
    int arity = rb_proc_arity(self);
    return INT2FIX(arity);
}
binding → binding 点击切换源代码

返回与 prc 关联的绑定。

def fred(param)
  proc {}
end

b = fred(99)
eval("param", b.binding)   #=> 99
static VALUE
proc_binding(VALUE self)
{
    VALUE bindval, binding_self = Qundef;
    rb_binding_t *bind;
    const rb_proc_t *proc;
    const rb_iseq_t *iseq = NULL;
    const struct rb_block *block;
    const rb_env_t *env = NULL;

    GetProcPtr(self, proc);
    block = &proc->block;

    if (proc->is_isolated) rb_raise(rb_eArgError, "Can't create Binding from isolated Proc");

  again:
    switch (vm_block_type(block)) {
      case block_type_iseq:
        iseq = block->as.captured.code.iseq;
        binding_self = block->as.captured.self;
        env = VM_ENV_ENVVAL_PTR(block->as.captured.ep);
        break;
      case block_type_proc:
        GetProcPtr(block->as.proc, proc);
        block = &proc->block;
        goto again;
      case block_type_ifunc:
        {
            const struct vm_ifunc *ifunc = block->as.captured.code.ifunc;
            if (IS_METHOD_PROC_IFUNC(ifunc)) {
                VALUE method = (VALUE)ifunc->data;
                VALUE name = rb_fstring_lit("<empty_iseq>");
                rb_iseq_t *empty;
                binding_self = method_receiver(method);
                iseq = rb_method_iseq(method);
                env = VM_ENV_ENVVAL_PTR(block->as.captured.ep);
                env = env_clone(env, method_cref(method));
                /* set empty iseq */
                empty = rb_iseq_new(NULL, name, name, Qnil, 0, ISEQ_TYPE_TOP);
                RB_OBJ_WRITE(env, &env->iseq, empty);
                break;
            }
        }
        /* FALLTHROUGH */
      case block_type_symbol:
        rb_raise(rb_eArgError, "Can't create Binding from C level Proc");
        UNREACHABLE_RETURN(Qnil);
    }

    bindval = rb_binding_alloc(rb_cBinding);
    GetBindingPtr(bindval, bind);
    RB_OBJ_WRITE(bindval, &bind->block.as.captured.self, binding_self);
    RB_OBJ_WRITE(bindval, &bind->block.as.captured.code.iseq, env->iseq);
    rb_vm_block_ep_update(bindval, &bind->block, env->ep);
    RB_OBJ_WRITTEN(bindval, Qundef, VM_ENV_ENVVAL(env->ep));

    if (iseq) {
        rb_iseq_check(iseq);
        RB_OBJ_WRITE(bindval, &bind->pathobj, ISEQ_BODY(iseq)->location.pathobj);
        bind->first_lineno = ISEQ_BODY(iseq)->location.first_lineno;
    }
    else {
        RB_OBJ_WRITE(bindval, &bind->pathobj,
                     rb_iseq_pathobj_new(rb_fstring_lit("(binding)"), Qnil));
        bind->first_lineno = 1;
    }

    return bindval;
}
call(params,...) → obj 点击切换源代码
(params,...) → obj

调用块,使用类似于方法调用语义将块的参数设置为 params 中的值。返回块中最后评估的表达式的值。

a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
a_proc.call(9, 1, 2, 3)    #=> [9, 18, 27]
a_proc[9, 1, 2, 3]         #=> [9, 18, 27]
a_proc.(9, 1, 2, 3)        #=> [9, 18, 27]
a_proc.yield(9, 1, 2, 3)   #=> [9, 18, 27]

请注意,prc.() 使用给定的参数调用 prc.call()。它是隐藏“call”的语法糖。

对于使用lambda->()创建的 proc,如果传递给 proc 的参数数量错误,则会生成错误。对于使用Proc.newKernel.proc创建的 proc,多余的参数会被静默丢弃,缺少的参数会被设置为nil

a_proc = proc {|a,b| [a,b] }
a_proc.call(1)   #=> [1, nil]

a_proc = lambda {|a,b| [a,b] }
a_proc.call(1)   # ArgumentError: wrong number of arguments (given 1, expected 2)

另请参见Proc#lambda?

static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    /* removed */
}
也称为:[]===yield
curry → a_proc 点击切换源代码
curry(arity) → a_proc

返回一个柯里化的 proc。如果给出了可选的 arity 参数,它将确定参数的数量。一个柯里化的 proc 接收一些参数。如果提供了足够数量的参数,它将把提供的参数传递给原始 proc 并返回结果。否则,返回另一个柯里化的 proc,它接收其余参数。

当柯里化具有可变参数的 proc 时,应该提供可选的 arity 参数来确定在调用 proc 之前需要多少个参数。

b = proc {|x, y, z| (x||0) + (y||0) + (z||0) }
p b.curry[1][2][3]           #=> 6
p b.curry[1, 2][3, 4]        #=> 6
p b.curry(5)[1][2][3][4][5]  #=> 6
p b.curry(5)[1, 2][3, 4][5]  #=> 6
p b.curry(1)[1]              #=> 1

b = proc {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
p b.curry[1][2][3]           #=> 6
p b.curry[1, 2][3, 4]        #=> 10
p b.curry(5)[1][2][3][4][5]  #=> 15
p b.curry(5)[1, 2][3, 4][5]  #=> 15
p b.curry(1)[1]              #=> 1

b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) }
p b.curry[1][2][3]           #=> 6
p b.curry[1, 2][3, 4]        #=> wrong number of arguments (given 4, expected 3)
p b.curry(5)                 #=> wrong number of arguments (given 5, expected 3)
p b.curry(1)                 #=> wrong number of arguments (given 1, expected 3)

b = lambda {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
p b.curry[1][2][3]           #=> 6
p b.curry[1, 2][3, 4]        #=> 10
p b.curry(5)[1][2][3][4][5]  #=> 15
p b.curry(5)[1, 2][3, 4][5]  #=> 15
p b.curry(1)                 #=> wrong number of arguments (given 1, expected 3)

b = proc { :foo }
p b.curry[]                  #=> :foo
static VALUE
proc_curry(int argc, const VALUE *argv, VALUE self)
{
    int sarity, max_arity, min_arity = rb_proc_min_max_arity(self, &max_arity);
    VALUE arity;

    if (rb_check_arity(argc, 0, 1) == 0 || NIL_P(arity = argv[0])) {
        arity = INT2FIX(min_arity);
    }
    else {
        sarity = FIX2INT(arity);
        if (rb_proc_lambda_p(self)) {
            rb_check_arity(sarity, min_arity, max_arity);
        }
    }

    return make_curry_proc(self, rb_ary_new(), arity);
}
eql?(other) → true 或 false

如果且仅当两个 proc 是从同一个代码块创建的,它们才相同。

def return_block(&block)
  block
end

def pass_block_twice(&block)
  [return_block(&block), return_block(&block)]
end

block1, block2 = pass_block_twice { puts 'test' }
# Blocks might be instantiated into Proc's lazily, so they may, or may not,
# be the same object.
# But they are produced from the same code block, so they are equal
block1 == block2
#=> true

# Another Proc will never be equal, even if the code is the "same"
block1 == proc { puts 'test' }
#=> false
别名:==
hash → 整数 点击切换源代码

返回与 proc 代码块对应的哈希值。

另请参见 Object#hash.

static VALUE
proc_hash(VALUE self)
{
    st_index_t hash;
    hash = rb_hash_start(0);
    hash = rb_hash_proc(hash, self);
    hash = rb_hash_end(hash);
    return ST2FIX(hash);
}
inspect()

返回此 proc 的唯一标识符,以及 proc 定义位置的指示。

别名:to_s
lambda? → true 或 false 点击切换源代码

如果 Proc 对象是 lambda,则返回 true。如果是非 lambda,则返回 false

lambda 性质会影响参数处理以及 returnbreak 的行为。

proc 生成的 Proc 对象会忽略额外的参数。

proc {|a,b| [a,b] }.call(1,2,3)    #=> [1,2]

它会为缺失的参数提供 nil

proc {|a,b| [a,b] }.call(1)        #=> [1,nil]

它会扩展单个数组参数。

proc {|a,b| [a,b] }.call([1,2])    #=> [1,2]

lambda 生成的 Proc 对象没有这些技巧。

lambda {|a,b| [a,b] }.call(1,2,3)  #=> ArgumentError
lambda {|a,b| [a,b] }.call(1)      #=> ArgumentError
lambda {|a,b| [a,b] }.call([1,2])  #=> ArgumentError

Proc#lambda? 是这些技巧的谓词。如果没有任何技巧适用,它将返回 true

lambda {}.lambda?            #=> true
proc {}.lambda?              #=> false

Proc.newproc 相同。

Proc.new {}.lambda?          #=> false

lambdaprocProc.new 会保留由 & 参数给出的 Proc 对象的技巧。

lambda(&lambda {}).lambda?   #=> true
proc(&lambda {}).lambda?     #=> true
Proc.new(&lambda {}).lambda? #=> true

lambda(&proc {}).lambda?     #=> false
proc(&proc {}).lambda?       #=> false
Proc.new(&proc {}).lambda?   #=> false

& 参数生成的 Proc 对象具有这些技巧

def n(&b) b.lambda? end
n {}                         #=> false

如果由 & 参数给出了 Proc 对象,则 & 参数会保留这些技巧。

n(&lambda {})                #=> true
n(&proc {})                  #=> false
n(&Proc.new {})              #=> false

从方法转换而来的 Proc 对象没有技巧。

def m() end
method(:m).to_proc.lambda?   #=> true

n(&method(:m))               #=> true
n(&method(:m).to_proc)       #=> true

define_method 的处理方式与方法定义相同。定义的方法没有技巧。

class C
  define_method(:d) {}
end
C.new.d(1,2)       #=> ArgumentError
C.new.method(:d).to_proc.lambda?   #=> true

define_method 始终定义一个没有技巧的方法,即使给出了非 lambda Proc 对象。这是唯一一个不保留技巧的例外。

class C
  define_method(:e, &proc {})
end
C.new.e(1,2)       #=> ArgumentError
C.new.method(:e).to_proc.lambda?   #=> true

此例外确保方法永远不会有技巧,并使创建包装器以定义行为与通常相同的简单方法变得容易。

class C
  def self.def2(name, &body)
    define_method(name, &body)
  end

  def2(:f) {}
end
C.new.f(1,2)       #=> ArgumentError

包装器 def2 定义了一个没有技巧的方法。

VALUE
rb_proc_lambda_p(VALUE procval)
{
    rb_proc_t *proc;
    GetProcPtr(procval, proc);

    return RBOOL(proc->is_lambda);
}
parameters(lambda: nil) → 数组 点击切换源代码

返回此 proc 的参数信息。如果提供了 lambda 关键字且不为 nil,则如果为 true,则将 proc 视为 lambda,如果为 false,则视为非 lambda。

prc = proc{|x, y=42, *other|}
prc.parameters  #=> [[:opt, :x], [:opt, :y], [:rest, :other]]
prc = lambda{|x, y=42, *other|}
prc.parameters  #=> [[:req, :x], [:opt, :y], [:rest, :other]]
prc = proc{|x, y=42, *other|}
prc.parameters(lambda: true)  #=> [[:req, :x], [:opt, :y], [:rest, :other]]
prc = lambda{|x, y=42, *other|}
prc.parameters(lambda: false) #=> [[:opt, :x], [:opt, :y], [:rest, :other]]
static VALUE
rb_proc_parameters(int argc, VALUE *argv, VALUE self)
{
    static ID keyword_ids[1];
    VALUE opt, lambda;
    VALUE kwargs[1];
    int is_proc ;
    const rb_iseq_t *iseq;

    iseq = rb_proc_get_iseq(self, &is_proc);

    if (!keyword_ids[0]) {
        CONST_ID(keyword_ids[0], "lambda");
    }

    rb_scan_args(argc, argv, "0:", &opt);
    if (!NIL_P(opt)) {
        rb_get_kwargs(opt, keyword_ids, 0, 1, kwargs);
        lambda = kwargs[0];
        if (!NIL_P(lambda)) {
            is_proc = !RTEST(lambda);
        }
    }

    if (!iseq) {
        return rb_unnamed_parameters(rb_proc_arity(self));
    }
    return rb_iseq_parameters(iseq, is_proc);
}
ruby2_keywords → proc 点击切换源代码

将关键字通过普通参数 splat 传递给 proc。这应该只在接受参数 splat (*args) 但不接受显式关键字或关键字 splat 的 proc 上调用。它标记 proc,以便如果 proc 使用关键字参数调用,则最终的哈希参数将被标记为特殊标志,这样如果它是另一个方法调用的普通参数 splat 的最后一个元素,并且该方法调用不包含显式关键字或关键字 splat,则最后一个元素将被解释为关键字。换句话说,关键字将通过 proc 传递给其他方法。

这应该只用于将关键字委托给另一个方法的 proc,并且仅用于与 2.7 之前的 Ruby 版本向后兼容。

此方法可能会在某个时候被删除,因为它仅用于向后兼容。由于它在 2.7 之前的 Ruby 版本中不存在,因此在调用它之前检查 proc 是否响应此方法。此外,请注意,如果此方法被删除,则 proc 的行为将发生变化,因此它不会传递关键字。

module Mod
  foo = ->(meth, *args, &block) do
    send(:"do_#{meth}", *args, &block)
  end
  foo.ruby2_keywords if foo.respond_to?(:ruby2_keywords)
end
static VALUE
proc_ruby2_keywords(VALUE procval)
{
    rb_proc_t *proc;
    GetProcPtr(procval, proc);

    rb_check_frozen(procval);

    if (proc->is_from_method) {
            rb_warn("Skipping set of ruby2_keywords flag for proc (proc created from method)");
            return procval;
    }

    switch (proc->block.type) {
      case block_type_iseq:
        if (ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_rest &&
                !ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kw &&
                !ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kwrest) {
            ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.ruby2_keywords = 1;
        }
        else {
            rb_warn("Skipping set of ruby2_keywords flag for proc (proc accepts keywords or proc does not accept argument splat)");
        }
        break;
      default:
        rb_warn("Skipping set of ruby2_keywords flag for proc (proc not defined in Ruby)");
        break;
    }

    return procval;
}
source_location → [String, Integer] 点击切换源代码

返回包含此 proc 的 Ruby 源文件名和行号,或者如果此 proc 不是在 Ruby 中定义的(即本机),则返回 nil

VALUE
rb_proc_location(VALUE self)
{
    return iseq_location(rb_proc_get_iseq(self, 0));
}
to_proc → proc 点击切换源代码

将对象转换为 Proc 对象的协议的一部分。Proc 类的实例只返回自身。

static VALUE
proc_to_proc(VALUE self)
{
    return self;
}
to_s → string 点击切换源代码

返回此 proc 的唯一标识符,以及 proc 定义位置的指示。

static VALUE
proc_to_s(VALUE self)
{
    const rb_proc_t *proc;
    GetProcPtr(self, proc);
    return rb_block_to_s(self, &proc->block, proc->is_lambda ? " (lambda)" : NULL);
}
也称为:inspect
(params,...) → obj
yield(params,...) → obj

调用块,使用类似于方法调用语义将块的参数设置为 params 中的值。返回块中最后评估的表达式的值。

a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
a_proc.call(9, 1, 2, 3)    #=> [9, 18, 27]
a_proc[9, 1, 2, 3]         #=> [9, 18, 27]
a_proc.(9, 1, 2, 3)        #=> [9, 18, 27]
a_proc.yield(9, 1, 2, 3)   #=> [9, 18, 27]

请注意,prc.() 使用给定的参数调用 prc.call()。它是隐藏“call”的语法糖。

对于使用lambda->()创建的 proc,如果传递给 proc 的参数数量错误,则会生成错误。对于使用Proc.newKernel.proc创建的 proc,多余的参数会被静默丢弃,缺少的参数会被设置为nil

a_proc = proc {|a,b| [a,b] }
a_proc.call(1)   #=> [1, nil]

a_proc = lambda {|a,b| [a,b] }
a_proc.call(1)   # ArgumentError: wrong number of arguments (given 1, expected 2)

另请参见Proc#lambda?

别名:call