class Proc
Ein Proc-Objekt ist eine Kapselung eines Codeblocks, der in einer lokalen Variablen gespeichert, an eine Methode oder eine andere Proc übergeben und aufgerufen werden kann. Proc ist ein wesentliches Konzept in Ruby und ein Kernstück seiner funktionalen Programmierfunktionen.
square = Proc.new {|x| x**2 } square.call(3) #=> 9 # shorthands: square.(3) #=> 9 square[3] #=> 9
Proc-Objekte sind Closures, was bedeutet, dass sie den gesamten Kontext, in dem sie erstellt wurden, speichern und verwenden können.
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
Erstellung
Es gibt mehrere Methoden, um eine Proc zu erstellen
-
Verwenden Sie den Konstruktor der Klasse
Procproc1 = Proc.new {|x| x**2 }
-
Verwenden Sie die Methode
Kernel#procals Abkürzung fürProc.newproc2 = proc {|x| x**2 }
-
Empfangen eines Codeblocks als Proc-Argument (beachten Sie das
&)def make_proc(&block) block end proc3 = make_proc {|x| x**2 }
-
Erstellen Sie eine Proc mit Lambda-Semantik mit der Methode
Kernel#lambda(siehe unten für Erklärungen zu Lambdas)lambda1 = lambda {|x| x**2 }
-
Verwenden Sie die Syntax für Lambda-Proc-Literale (erstellt ebenfalls eine Proc mit Lambda-Semantik)
lambda2 = ->(x) { x**2 }
Lambda- und Nicht-Lambda-Semantik
Procs gibt es in zwei Geschmacksrichtungen: Lambda und Nicht-Lambda (reguläre Procs). Die Unterschiede sind:
-
In Lambdas bedeuten
returnundbreakdas Verlassen dieser Lambda; -
In Nicht-Lambda-Procs bedeutet
returndas Verlassen der umschließenden Methode (und löst einenLocalJumpErroraus, wenn er außerhalb der Methode aufgerufen wird); -
In Nicht-Lambda-Procs bedeutet
breakdas Verlassen der Methode, für die der Block bereitgestellt wurde (und löst einenLocalJumpErroraus, wenn er nach der Rückgabe der Methode aufgerufen wird); -
In Lambdas werden Argumente auf die gleiche Weise behandelt wie in Methoden: streng, mit
ArgumentErrorfür nicht übereinstimmende Argumentanzahl und keine zusätzliche Argumentverarbeitung; -
Reguläre Procs akzeptieren Argumente großzügiger: fehlende Argumente werden mit
nilgefüllt, einzelneArray-Argumente werden dekonstruiert, wenn die Proc mehrere Argumente hat, und es wird kein Fehler ausgelöst, wenn zusätzliche Argumente vorhanden sind.
Beispiele
# +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
Lambdas sind nützlich als eigenständige Funktionen, insbesondere als Argumente für Funktionen höherer Ordnung, und verhalten sich genau wie Ruby-Methoden.
Procs sind nützlich zur Implementierung von Iteratoren
def test [[1, 2], [3, 4], [5, 6]].map {|a, b| return a if a + b > 10 } # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ end
Innerhalb von map wird der Codeblock als reguläre (Nicht-Lambda-)Proc behandelt, was bedeutet, dass die internen Arrays in Argumentpaare dekonstruiert werden und return die Methode test verlassen würde. Dies wäre mit einer strengeren Lambda nicht möglich.
Sie können eine Lambda von einer regulären Proc durch die Instanzmethode lambda? unterscheiden.
Lambda-Semantik wird typischerweise während der Lebensdauer der Proc beibehalten, einschließlich der &-Dekonstruktion in einen Codeblock
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)
Die einzige Ausnahme ist die dynamische Methodendefinition: Selbst wenn Methoden durch Übergabe einer Nicht-Lambda-Proc definiert werden, haben sie immer noch normale Semantik für die Argumentprüfung.
class C define_method(:e, &proc {}) end C.new.e(1,2) #=> ArgumentError C.new.method(:e).to_proc.lambda? #=> true
Diese Ausnahme stellt sicher, dass Methoden niemals ungewöhnliche Argumentübergabekonventionen haben, und erleichtert Wrapper, die Methoden definieren, die sich wie üblich verhalten.
class C def self.def2(name, &body) define_method(name, &body) end def2(:f) {} end C.new.f(1,2) #=> ArgumentError
Der Wrapper def2 empfängt body als Nicht-Lambda-Proc, definiert aber dennoch eine Methode mit normaler Semantik.
Konvertierung anderer Objekte in Procs
Jedes Objekt, das die Methode to_proc implementiert, kann durch den &-Operator in eine Proc konvertiert und somit von Iteratoren verwendet werden.
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!"]
Von den Ruby-Kernklassen wird diese Methode von Symbol, Method und Hash implementiert.
: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]
Verwaiste Proc
return und break in einem Block verlassen eine Methode. Wenn ein Proc-Objekt aus dem Block generiert wird und das Proc-Objekt bis zur Rückgabe der Methode überlebt, können return und break nicht funktionieren. In diesem Fall lösen return und break einen LocalJumpError aus. Ein Proc-Objekt in einer solchen Situation wird als verwaistes Proc-Objekt bezeichnet.
Beachten Sie, dass sich die Methode zum Beenden für return und break unterscheidet. Es gibt eine Situation, in der sie für break verwaist, aber nicht für return verwaist ist.
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
Da return und break in Lambdas den Block selbst verlassen, können Lambdas nicht verwaist sein.
Anonyme Blockparameter
Um das Schreiben kurzer Blöcke zu vereinfachen, bietet Ruby zwei verschiedene Arten von anonymen Parametern: it (einzelner Parameter) und nummerierte Parameter: _1, _2 und so weiter.
# 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] # it: %w[test me please].each { puts it.upcase } # prints TEST, ME, PLEASE (1..5).map { it**2 } # => [1, 4, 9, 16, 25] # Numbered parameter: %w[test me please].each { puts _1.upcase } # prints TEST, ME, PLEASE (1..5).map { _1**2 } # => [1, 4, 9, 16, 25]
it
it ist ein Name, der innerhalb eines Blocks verfügbar ist, wenn keine expliziten Parameter definiert sind, wie oben gezeigt.
%w[test me please].each { puts it.upcase } # prints TEST, ME, PLEASE (1..5).map { it**2 } # => [1, 4, 9, 16, 25]
it ist ein "Soft Keyword": Es ist kein reservierter Name und kann als Name für Methoden und lokale Variablen verwendet werden.
it = 5 # no warnings def it(&block) # RSpec-like API, no warnings # ... end
it kann auch als lokale Variable in Blöcken verwendet werden, die es als impliziten Parameter verwenden (obwohl dieser Stil offensichtlich verwirrend ist).
[1, 2, 3].each { # takes a value of implicit parameter "it" and uses it to # define a local variable with the same name it = it**2 p it }
In einem Block mit explizit definierten Parametern löst die Verwendung von it eine Ausnahme aus.
[1, 2, 3].each { |x| p it }
# syntax error found (SyntaxError)
# [1, 2, 3].each { |x| p it }
# ^~ 'it' is not allowed when an ordinary parameter is defined
Wenn jedoch ein lokaler Name (Variable oder Methode) verfügbar ist, wird dieser verwendet.
it = 5 [1, 2, 3].each { |x| p it } # Prints 5, 5, 5
Blöcke, die it verwenden, können verschachtelt werden.
%w[test me].each { it.each_char { p it } } # Prints "t", "e", "s", "t", "m", "e"
Blöcke, die it verwenden, werden als einen Parameter betrachtend.
p = proc { it**2 } l = lambda { it**2 } p.parameters # => [[:opt]] p.arity # => 1 l.parameters # => [[:req]] l.arity # => 1
Nummerierte Parameter
Nummerierte Parameter sind eine weitere Möglichkeit, Blockparameter implizit zu benennen. Im Gegensatz zu it ermöglichen nummerierte Parameter, sich in einem Block auf mehrere Parameter zu beziehen.
%w[test me please].each { puts _1.upcase } # prints TEST, ME, PLEASE {a: 100, b: 200}.map { "#{_1} = #{_2}" } # => "a = 100", "b = 200"
Parameternamen von _1 bis _9 werden unterstützt.
[10, 20, 30].zip([40, 50, 60], [70, 80, 90]).map { _1 + _2 + _3 } # => [120, 150, 180]
Es wird jedoch empfohlen, diese mit Bedacht zu verwenden, wahrscheinlich auf _1 und _2 und Einzeilenblöcke beschränkt.
Nummerierte Parameter können nicht zusammen mit explizit benannten verwendet werden.
[10, 20, 30].map { |x| _1**2 }
# SyntaxError (ordinary parameter is defined)
Nummerierte Parameter können auch nicht mit it vermischt werden.
[10, 20, 30].map { _1 + it }
# SyntaxError: 'it' is not allowed when a numbered parameter is already used
Um Konflikte zu vermeiden, verursacht die Benennung lokaler Variablen oder Methodenargumente als _1, _2 usw. einen Fehler.
_1 = 'test' # ^~ _1 is reserved for numbered parameters (SyntaxError)
Die Verwendung von impliziten nummerierten Parametern beeinflusst die Arity des Blocks.
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
Blöcke mit nummerierten Parametern können nicht verschachtelt werden.
%w[test me].each { _1.each_char { p _1 } }
# numbered parameter is already used in outer block (SyntaxError)
# %w[test me].each { _1.each_char { p _1 } }
# ^~
Öffentliche Klassenmethoden
Source
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;
}
Erstellt ein neues Proc-Objekt, das an den aktuellen Kontext gebunden ist.
proc = Proc.new { "hello" } proc.call #=> "hello"
Löst eine ArgumentError aus, wenn sie ohne Block aufgerufen wird.
Proc.new #=> ArgumentError
Öffentliche Instanzmethoden
Source
static VALUE
proc_compose_to_left(VALUE self, VALUE g)
{
return rb_proc_compose_to_left(self, to_callable(g));
}
Gibt eine Proc zurück, die die Komposition dieser Proc und des gegebenen g ist. Die zurückgegebene Proc nimmt eine variable Anzahl von Argumenten entgegen, ruft g mit diesen auf und ruft dann diese Proc mit dem Ergebnis auf.
f = proc {|x| x * x } g = proc {|x| x + x } p (f << g).call(2) #=> 16
Siehe Proc#>> für detaillierte Erklärungen.
Source
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.code.ifunc != \
other_block->as.captured.code.ifunc) {
return Qfalse;
}
if (memcmp(
((cfunc_proc_t *)self_proc)->env,
((cfunc_proc_t *)other_proc)->env,
sizeof(((cfunc_proc_t *)self_proc)->env))) {
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;
}
Zwei Procs sind nur dann gleich, wenn sie aus demselben Codeblock erstellt wurden.
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
Source
static VALUE
proc_compose_to_right(VALUE self, VALUE g)
{
return rb_proc_compose_to_right(self, to_callable(g));
}
Gibt eine Proc zurück, die die Komposition dieser Proc und des gegebenen g ist. Die zurückgegebene Proc nimmt eine variable Anzahl von Argumenten entgegen, ruft diese Proc mit ihnen auf und ruft dann g mit dem Ergebnis auf.
f = proc {|x| x * x } g = proc {|x| x + x } p (f >> g).call(2) #=> 8
g kann eine andere Proc, eine Methode oder jedes andere Objekt sein, das auf die Methode call reagiert.
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')
Ruft den Block auf und weist die Blockparameter anhand von Argumenten zu, die den Methodenaufrufsemantiken sehr nahe kommen. Gibt den Wert des zuletzt im Block ausgewerteten Ausdrucks zurück.
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]
Beachten Sie, dass prc.() prc.call() mit den gegebenen Parametern aufruft. Dies ist eine syntaktische Zuckerung, um "call" zu verbergen.
Für mit lambda oder ->() erstellte Procs wird ein Fehler generiert, wenn die falsche Anzahl von Parametern an die Proc übergeben wird. Für mit Proc.new oder Kernel.proc erstellte Procs werden zusätzliche Parameter stillschweigend verworfen und fehlende Parameter auf nil gesetzt.
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)
Siehe auch Proc#lambda?.
Source
static VALUE
proc_arity(VALUE self)
{
int arity = rb_proc_arity(self);
return INT2FIX(arity);
}
Gibt die Anzahl der obligatorischen Argumente zurück. Wenn der Block so deklariert ist, dass er keine Argumente annimmt, wird 0 zurückgegeben. Wenn bekannt ist, dass der Block genau n Argumente annimmt, wird n zurückgegeben. Wenn der Block optionale Argumente hat, wird -n-1 zurückgegeben, wobei n die Anzahl der obligatorischen Argumente ist, mit der Ausnahme für Blöcke, die keine Lambdas sind und nur eine endliche Anzahl von optionalen Argumenten haben; in letzterem Fall wird n zurückgegeben. Schlüsselwortargumente werden als ein einzelnes zusätzliches Argument betrachtet, wobei dieses Argument obligatorisch ist, wenn ein Schlüsselwortargument obligatorisch ist. Eine proc ohne Argumentdeklarationen ist dasselbe wie ein Block, der || als seine Argumente deklariert.
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
Source
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(Qnil, 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;
}
Gibt die mit prc verbundene Bindung zurück.
def fred(param) proc {} end b = fred(99) eval("param", b.binding) #=> 99
Source
0
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
/* removed */
}
Ruft den Block auf und weist die Blockparameter anhand von Argumenten zu, die den Methodenaufrufsemantiken sehr nahe kommen. Gibt den Wert des zuletzt im Block ausgewerteten Ausdrucks zurück.
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]
Beachten Sie, dass prc.() prc.call() mit den gegebenen Parametern aufruft. Dies ist eine syntaktische Zuckerung, um "call" zu verbergen.
Für mit lambda oder ->() erstellte Procs wird ein Fehler generiert, wenn die falsche Anzahl von Parametern an die Proc übergeben wird. Für mit Proc.new oder Kernel.proc erstellte Procs werden zusätzliche Parameter stillschweigend verworfen und fehlende Parameter auf nil gesetzt.
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)
Siehe auch Proc#lambda?.
Source
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);
}
Gibt eine curried Proc zurück. Wenn das optionale Argument arity angegeben ist, bestimmt es die Anzahl der Argumente. Eine curried Proc empfängt einige Argumente. Wenn eine ausreichende Anzahl von Argumenten bereitgestellt wird, übergibt sie die bereitgestellten Argumente an die ursprüngliche Proc und gibt das Ergebnis zurück. Andernfalls gibt sie eine weitere curried Proc zurück, die den Rest der Argumente annimmt.
Das optionale Argument arity sollte beim Currying von Procs mit variablen Argumenten angegeben werden, um zu bestimmen, wie viele Argumente benötigt werden, bevor die Proc aufgerufen wird.
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
Zwei Procs sind nur dann gleich, wenn sie aus demselben Codeblock erstellt wurden.
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
Source
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);
}
Gibt einen Hash-Wert zurück, der dem Proc-Body entspricht.
Siehe auch Object#hash.
Source
VALUE
rb_proc_lambda_p(VALUE procval)
{
rb_proc_t *proc;
GetProcPtr(procval, proc);
return RBOOL(proc->is_lambda);
}
Gibt true zurück, wenn ein Proc-Objekt eine Lambda ist. false, wenn es keine Lambda ist.
Die Lambda-Eigenschaft beeinflusst die Argumentbehandlung und das Verhalten von return und break.
Ein von proc generiertes Proc-Objekt ignoriert zusätzliche Argumente.
proc {|a,b| [a,b] }.call(1,2,3) #=> [1,2]
Es stellt nil für fehlende Argumente bereit.
proc {|a,b| [a,b] }.call(1) #=> [1,nil]
Es dekonstruiert ein einzelnes Array-Argument.
proc {|a,b| [a,b] }.call([1,2]) #=> [1,2]
Ein von lambda generiertes Proc-Objekt hat solche Tricks nicht.
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? ist ein Prädikat für die Tricks. Es gibt true zurück, wenn keine Tricks angewendet werden.
lambda {}.lambda? #=> true proc {}.lambda? #=> false
Proc.new ist dasselbe wie proc.
Proc.new {}.lambda? #=> false
lambda, proc und Proc.new behalten die Tricks eines von & übergebenen Proc-Objekts bei.
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
Ein von einem &-Argument generiertes Proc-Objekt hat die Tricks.
def n(&b) b.lambda? end n {} #=> false
Das &-Argument behält die Tricks bei, wenn ein Proc-Objekt als &-Argument übergeben wird.
n(&lambda {}) #=> true n(&proc {}) #=> false n(&Proc.new {}) #=> false
Ein aus einer Methode konvertiertes Proc-Objekt hat keine Tricks.
def m() end method(:m).to_proc.lambda? #=> true n(&method(:m)) #=> true n(&method(:m).to_proc) #=> true
define_method wird wie eine Methodendefinition behandelt. Die definierte Methode hat keine Tricks.
class C define_method(:d) {} end C.new.d(1,2) #=> ArgumentError C.new.method(:d).to_proc.lambda? #=> true
define_method definiert immer eine Methode ohne die Tricks, selbst wenn ein Nicht-Lambda- Proc-Objekt übergeben wird. Dies ist die einzige Ausnahme, bei der die Tricks nicht beibehalten werden.
class C define_method(:e, &proc {}) end C.new.e(1,2) #=> ArgumentError C.new.method(:e).to_proc.lambda? #=> true
Diese Ausnahme stellt sicher, dass Methoden niemals Tricks haben, und erleichtert Wrapper, die Methoden definieren, die sich wie üblich verhalten.
class C def self.def2(name, &body) define_method(name, &body) end def2(:f) {} end C.new.f(1,2) #=> ArgumentError
Der Wrapper def2 definiert eine Methode, die keine Tricks hat.
Source
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);
}
Gibt die Parameterinformationen dieser Proc zurück. Wenn das Schlüsselwort lambda bereitgestellt und nicht nil ist, wird die Proc als Lambda behandelt, wenn true, und als Nicht-Lambda, wenn false.
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]]
Source
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_post &&
!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 post arguments 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;
}
Markiert die Proc als Übergabe von Schlüsselwörtern über ein normales Argument-Splat. Dies sollte nur auf Procs angewendet werden, die ein Argument-Splat (*args) akzeptieren, aber keine expliziten Schlüsselwörter oder einen Schlüsselwort-Splat. Es markiert die Proc so, dass, wenn die Proc mit Schlüsselwortargumenten aufgerufen wird, das finale Hash-Argument mit einem speziellen Flag markiert wird, sodass, wenn es das letzte Element eines normalen Argument-Splats an einen anderen Methodenaufruf ist und dieser Methodenaufruf keine expliziten Schlüsselwörter oder einen Schlüsselwort-Splat enthält, das letzte Element als Schlüsselwörter interpretiert wird. Mit anderen Worten, Schlüsselwörter werden über die Proc an andere Methoden weitergegeben.
Dies sollte nur für Procs verwendet werden, die Schlüsselwörter an eine andere Methode delegieren, und nur für die Abwärtskompatibilität mit Ruby-Versionen vor 2.7.
Diese Methode wird wahrscheinlich irgendwann entfernt, da sie nur für die Abwärtskompatibilität existiert. Da sie in Ruby-Versionen vor 2.7 nicht vorhanden ist, prüfen Sie, ob die Proc auf diese Methode reagiert, bevor Sie sie aufrufen. Beachten Sie auch, dass sich das Verhalten der Proc ändert, wenn diese Methode entfernt wird, sodass Schlüsselwörter nicht mehr weitergegeben werden.
module Mod foo = ->(meth, *args, &block) do send(:"do_#{meth}", *args, &block) end foo.ruby2_keywords if foo.respond_to?(:ruby2_keywords) end
Source
VALUE
rb_proc_location(VALUE self)
{
return iseq_location(rb_proc_get_iseq(self, 0));
}
Gibt den Ort zurück, an dem die Proc definiert wurde. Das zurückgegebene Array enthält
(1) the Ruby source filename (2) the line number where the definition starts (3) the column number where the definition starts (4) the line number where the definition ends (5) the column number where the definitions ends
Diese Methode gibt nil zurück, wenn die Proc nicht in Ruby (d.h. nativer) definiert wurde.
Source
static VALUE
proc_to_proc(VALUE self)
{
return self;
}
Source
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);
}
Gibt die eindeutige Kennung für diese Proc zurück, zusammen mit einer Angabe, wo die Proc definiert wurde.
Ruft den Block auf und weist die Blockparameter anhand von Argumenten zu, die den Methodenaufrufsemantiken sehr nahe kommen. Gibt den Wert des zuletzt im Block ausgewerteten Ausdrucks zurück.
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]
Beachten Sie, dass prc.() prc.call() mit den gegebenen Parametern aufruft. Dies ist eine syntaktische Zuckerung, um "call" zu verbergen.
Für mit lambda oder ->() erstellte Procs wird ein Fehler generiert, wenn die falsche Anzahl von Parametern an die Proc übergeben wird. Für mit Proc.new oder Kernel.proc erstellte Procs werden zusätzliche Parameter stillschweigend verworfen und fehlende Parameter auf nil gesetzt.
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)
Siehe auch Proc#lambda?.