類別 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

Lambda 和非 lambda 語意

程序有兩種形式:lambda 和非 lambda(一般程序)。差異如下

範例

# +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 方法完全相同。

Procs 可用於實作迭代器

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

包裝函式 def2body 接收為非 lambda proc,但定義的方法具有正常的語意。

將其他物件轉換為 procs

任何實作 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

由於 lambda 中的 returnbreak 會退出區塊本身,因此 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 或 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()。這是隱藏「呼叫」的語法糖。

對於使用 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 可以是其他 ProcMethod,或任何其他回應 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));
}
(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()。這是隱藏「呼叫」的語法糖。

對於使用 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()。這是隱藏「呼叫」的語法糖。

對於使用 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?

0
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 → integer 按一下以切換來源

傳回與 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
別名為: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) → array 按一下切換來源

傳回此程序的參數資訊。如果提供了 lambda 關鍵字且不為 nil,則在為 true 時將程序視為 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 按一下切換來源

標記程序為透過一般引數散佈傳遞關鍵字。這只應呼叫在接受引數散佈 (*args) 但不接受明確關鍵字或關鍵字散佈的程序上。它會標記程序,以便如果程序以關鍵字引數呼叫,則最後一個雜湊引數會標記一個特殊旗標,以便如果它是另一個方法呼叫的最後一個一般引數散佈元素,且該方法呼叫不包含明確關鍵字或關鍵字散佈,則最後一個元素會被解釋為關鍵字。換句話說,關鍵字會透過程序傳遞到其他方法。

這只應使用在將關鍵字委派給另一個方法的程序,而且只應使用在與 Ruby 2.7 之前的版本向後相容時。

此方法可能在某個時間點移除,因為它僅存在於向後相容性中。由於它在 Ruby 2.7 之前的版本中不存在,因此在呼叫此方法之前,請檢查 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 → [字串, 整數] 按一下以切換原始碼

傳回包含此 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 → 字串 按一下以切換原始碼

傳回此 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()。這是隱藏「呼叫」的語法糖。

對於使用 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