класс Proc
Proc объекты — это блоки кода, которые связаны с набором локальных переменных. После связывания код может вызываться в разных контекстах и по-прежнему обращаться к этим переменным.
def gen_times(factor)
return Proc.new {|n| n*factor }
end
times3 = gen_times(3)
times5 = gen_times(5)
times3.call(12) #=> 36
times5.call(5) #=> 25
times3.call(times5.call(4)) #=> 60
Публичные методы класса
static VALUE
rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
{
VALUE block = proc_new(klass, FALSE);
rb_obj_call_init(block, argc, argv);
return block;
} Создаёт новый Proc объект, связанный с текущим контекстом. Proc::new может вызываться без блока только внутри метода с прикреплённым блоком, в этом случае этот блок преобразуется в объект Proc.
def proc_from
Proc.new
end
proc = proc_from { "hello" }
proc.call #=> "hello"
Методы экземпляра публичного интерфейса
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
VALUE vret;
const rb_block_t *blockptr = 0;
const rb_iseq_t *iseq;
rb_proc_t *proc;
VALUE passed_procval;
GetProcPtr(procval, proc);
iseq = proc->block.iseq;
if (RUBY_VM_IFUNC_P(iseq) || iseq->body->param.flags.has_block) {
if (rb_block_given_p()) {
rb_proc_t *passed_proc;
passed_procval = rb_block_proc();
GetProcPtr(passed_procval, passed_proc);
blockptr = &passed_proc->block;
}
}
vret = rb_vm_invoke_proc(GET_THREAD(), proc, argc, argv, blockptr);
RB_GC_GUARD(procval);
RB_GC_GUARD(passed_procval);
return vret;
} Вызывает блок с obj в качестве параметра proc, как в #call. Это позволяет использовать объект proc в качестве целевого объекта для when в операторе case.
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
VALUE vret;
const rb_block_t *blockptr = 0;
const rb_iseq_t *iseq;
rb_proc_t *proc;
VALUE passed_procval;
GetProcPtr(procval, proc);
iseq = proc->block.iseq;
if (RUBY_VM_IFUNC_P(iseq) || iseq->body->param.flags.has_block) {
if (rb_block_given_p()) {
rb_proc_t *passed_proc;
passed_procval = rb_block_proc();
GetProcPtr(passed_procval, passed_proc);
blockptr = &passed_proc->block;
}
}
vret = rb_vm_invoke_proc(GET_THREAD(), proc, argc, argv, blockptr);
RB_GC_GUARD(procval);
RB_GC_GUARD(passed_procval);
return vret;
} Вызывает блок, устанавливая параметры блока на значения в 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».
Для proc, созданных с использованием lambda или ->(), генерируется ошибка, если передано неверное количество параметров для proc. Для proc, созданных с использованием Proc.new или Kernel.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)
См. также #lambda?.
static VALUE
proc_arity(VALUE self)
{
int arity = rb_proc_arity(self);
return INT2FIX(arity);
} Возвращает количество обязательных аргументов. Если блок объявлен без аргументов, возвращает 0. Если блок известен, что принимает ровно n аргументов, возвращает n. Если блок имеет необязательные аргументы, возвращает -n-1, где n — количество обязательных аргументов, за исключением блоков, которые не являются лямбдами и имеют только конечное количество необязательных аргументов; в последнем случае возвращает n. Аргументы ключевых слов будут рассматриваться как один дополнительный аргумент, который является обязательным, если какой-либо аргумент ключевого слова является обязательным. A 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 { |x=0| }.arity #=> 0
lambda { |x=0| }.arity #=> -1
proc { |x=0, y| }.arity #=> 1
lambda { |x=0, y| }.arity #=> -2
proc { |x=0, y=0| }.arity #=> 0
lambda { |x=0, y=0| }.arity #=> -1
proc { |x, y=0| }.arity #=> 1
lambda { |x, y=0| }.arity #=> -2
proc { |(x, y), z=0| }.arity #=> 1
lambda { |(x, y), z=0| }.arity #=> -2
proc { |a, x:0, y:0| }.arity #=> 1
lambda { |a, x:0, y:0| }.arity #=> -2
static VALUE
proc_binding(VALUE self)
{
VALUE bindval, envval;
const rb_proc_t *proc;
const rb_iseq_t *iseq;
rb_binding_t *bind;
GetProcPtr(self, proc);
envval = rb_vm_proc_envval(proc);
iseq = proc->block.iseq;
if (SYMBOL_P(iseq)) goto error;
if (RUBY_VM_IFUNC_P(iseq)) {
struct vm_ifunc *ifunc = (struct vm_ifunc *)iseq;
if (IS_METHOD_PROC_IFUNC(ifunc)) {
VALUE method = (VALUE)ifunc->data;
envval = env_clone(envval, method_receiver(method), method_cref(method));
iseq = rb_method_iseq(method);
}
else {
error:
rb_raise(rb_eArgError, "Can't create Binding from C level Proc");
}
}
bindval = rb_binding_alloc(rb_cBinding);
GetBindingPtr(bindval, bind);
bind->env = envval;
if (iseq) {
rb_iseq_check(iseq);
bind->path = iseq->body->location.path;
bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq));
}
else {
bind->path = Qnil;
bind->first_lineno = 0;
}
return bindval;
} Возвращает привязку, связанную с prc. Обратите внимание, что Kernel#eval принимает в качестве второго параметра либо Proc, либо объект Binding.
def fred(param)
proc {}
end
b = fred(99)
eval("param", b.binding) #=> 99
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
VALUE vret;
const rb_block_t *blockptr = 0;
const rb_iseq_t *iseq;
rb_proc_t *proc;
VALUE passed_procval;
GetProcPtr(procval, proc);
iseq = proc->block.iseq;
if (RUBY_VM_IFUNC_P(iseq) || iseq->body->param.flags.has_block) {
if (rb_block_given_p()) {
rb_proc_t *passed_proc;
passed_procval = rb_block_proc();
GetProcPtr(passed_procval, passed_proc);
blockptr = &passed_proc->block;
}
}
vret = rb_vm_invoke_proc(GET_THREAD(), proc, argc, argv, blockptr);
RB_GC_GUARD(procval);
RB_GC_GUARD(passed_procval);
return vret;
} Вызывает блок, устанавливая параметры блока на значения в 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».
Для proc, созданных с использованием lambda или ->(), генерируется ошибка, если передано неверное количество параметров для proc. Для proc, созданных с использованием Proc.new или Kernel.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)
См. также #lambda?.
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;
rb_scan_args(argc, argv, "01", &arity);
if (NIL_P(arity)) {
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);
} Возвращает замыкание proc. Если необязательный аргумент arity задан, он определяет количество аргументов. Замыкание proc принимает некоторые аргументы. Если предоставлено достаточное количество аргументов, оно передает эти аргументы исходному proc и возвращает результат. В противном случае возвращает другое замыкание 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_hash(VALUE self)
{
st_index_t hash;
hash = rb_hash_start(0);
hash = rb_hash_proc(hash, self);
hash = rb_hash_end(hash);
return LONG2FIX(hash);
} Возвращает значение хэша, соответствующее телу proc.
См. также Object#hash.
VALUE
rb_proc_lambda_p(VALUE procval)
{
rb_proc_t *proc;
GetProcPtr(procval, proc);
return proc->is_lambda ? Qtrue : Qfalse;
} Возвращает true для объекта Proc, для которого обработка аргументов жёсткая. Такие proc обычно генерируются lambda.
Объект 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]
Объект Proc, сгенерированный аргументом lambda не имеет таких уловок.
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
#lambda? — это предикат для этих уловок. Возвращает true если никакие уловки не применимы.
lambda {}.lambda? #=> true
proc {}.lambda? #=> false
::new такое же, как proc.
Proc.new {}.lambda? #=> false
lambda, proc и ::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 всегда определяет метод без уловок, даже если задан объект не-лямбда 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 определяет метод, который не имеет уловок.
static VALUE
rb_proc_parameters(VALUE self)
{
int is_proc;
const rb_iseq_t *iseq = rb_proc_get_iseq(self, &is_proc);
if (!iseq) {
return unnamed_parameters(rb_proc_arity(self));
}
return rb_iseq_parameters(iseq, is_proc);
} Возвращает информацию о параметрах этого proc.
prc = lambda{|x, y=42, *other|}
prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]]
VALUE
rb_proc_location(VALUE self)
{
return iseq_location(rb_proc_get_iseq(self, 0));
} Возвращает имя файла и номер строки Ruby, содержащие этот proc, или nil, если этот proc не был определён в Ruby (т.е. нативный).
static VALUE
proc_to_proc(VALUE self)
{
return self;
} Часть протокола для преобразования объектов в объекты Proc. Экземпляры класса Proc просто возвращают себя.
static VALUE
proc_to_s(VALUE self)
{
VALUE str = 0;
rb_proc_t *proc;
const char *cname = rb_obj_classname(self);
const rb_iseq_t *iseq;
const char *is_lambda;
GetProcPtr(self, proc);
iseq = proc->block.iseq;
is_lambda = proc->is_lambda ? " (lambda)" : "";
if (RUBY_VM_NORMAL_ISEQ_P(iseq) && rb_iseq_check(iseq)) {
int first_lineno = 0;
if (iseq->body->line_info_table) {
first_lineno = FIX2INT(rb_iseq_first_lineno(iseq));
}
str = rb_sprintf("#<%s:%p@%"PRIsVALUE":%d%s>", cname, (void *)self,
iseq->body->location.path, first_lineno, is_lambda);
}
else if (SYMBOL_P(iseq)) {
str = rb_sprintf("#<%s:%p(&%+"PRIsVALUE")%s>", cname, (void *)self,
(VALUE)iseq, is_lambda);
}
else {
str = rb_sprintf("#<%s:%p%s>", cname, (void *)proc->block.iseq,
is_lambda);
}
if (OBJ_TAINTED(self)) {
OBJ_TAINT(str);
}
return str;
} Возвращает уникальный идентификатор этого proc вместе с указанием места определения proc.
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
VALUE vret;
const rb_block_t *blockptr = 0;
const rb_iseq_t *iseq;
rb_proc_t *proc;
VALUE passed_procval;
GetProcPtr(procval, proc);
iseq = proc->block.iseq;
if (RUBY_VM_IFUNC_P(iseq) || iseq->body->param.flags.has_block) {
if (rb_block_given_p()) {
rb_proc_t *passed_proc;
passed_procval = rb_block_proc();
GetProcPtr(passed_procval, passed_proc);
blockptr = &passed_proc->block;
}
}
vret = rb_vm_invoke_proc(GET_THREAD(), proc, argc, argv, blockptr);
RB_GC_GUARD(procval);
RB_GC_GUARD(passed_procval);
return vret;
} Вызывает блок, устанавливая параметры блока на значения в 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».
Для proc, созданных с использованием lambda или ->(), генерируется ошибка, если передано неверное количество параметров для proc. Для proc, созданных с использованием Proc.new или Kernel.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)
См. также #lambda?.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.