Spec-Zone.ru › Ruby 2.7

класс 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 }
    
  • Создайте proc с семантикой лямбда-функции, используя метод Kernel#lambda (см. ниже объяснения о лямбда-функциях):

    lambda1 = lambda {|x| x**2 }
    
  • Используйте синтаксис литерала лямбда-функции (также создаёт proc с семантикой лямбда-функции):

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

Семантика лямбда-функций и обычных процедур

Proc бывают двух типов: лямбда-функции и обычные процедуры. Различия заключаются в следующем:

  • В лямбда-функциях return и break означают выход из этой лямбда-функции;

  • В обычных процедурах return означает выход из содержащего метода (и вызовет LocalJumpError при вызове вне метода);

  • В обычных процедурах break означает выход из метода, которому был передан блок. (и вызовет LocalJumpError при вызове после возвращения метода);

  • В лямбда-функциях аргументы обрабатываются так же, как и в методах: строго, с ArgumentError для несовпадения количества аргументов и без дополнительной обработки аргументов;

  • Обычные процедуры принимают аргументы более гибко: отсутствующие аргументы заполняются nil, одиночные аргументы типа 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

Лямбда-функции полезны как автономные функции, особенно полезны в качестве аргументов для функций высшего порядка, ведя себя точно так же, как методы Ruby.

Процедуры полезны для реализации итераторов:

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

Внутри map, блок кода обрабатывается как обычная (не лямбда-функция) процедура, что означает, что внутренние массивы будут деконструированы в пары аргументов, и return выйдет из метода test. Это было бы невозможно с более жёсткой лямбда-функцией.

Вы можете определить лямбда-функцию от обычной процедуры, используя метод lambda?.

Семантика лямбда-функций обычно сохраняется на протяжении всего срока службы процедуры, включая & деконструкцию в блок кода:

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

Исключением является динамическое определение метода: даже если он определён путём передачи обычной процедуры, методы всё ещё имеют нормальную семантику проверки аргументов.

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 в качестве обычной процедуры, но определяет метод, который имеет нормальную семантику.

Преобразование других объектов в процедуры

Любой объект, который реализует метод to_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, этот метод реализован Symbol, Method и Hash.

: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

return и break в блоке выходят из метода. Если объект Proc генерируется из блока, а объект Proc существует до возвращения метода, return и break не могут работать. В этом случае, return и break возбуждают LocalJumpError. Объект Proc в такой ситуации называется осиротевшим объектом Proc.

Обратите внимание, что метод выхода отличается для return и break. Существует ситуация, когда осиротевший для 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

Так как return и break выходят из самого блока в лямбда-функциях, лямбда-функции не могут быть осиротевшими.

Параметры с номерами

Параметры с номерами — это неявные параметры блока, предназначенные для упрощения написания коротких блоков:

# 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 Показать исходный код
new → a_proc
static VALUE
rb_proc_s_new(int argc, VALUE *argv, VALUE klass)
{
    VALUE block = proc_new(klass, FALSE, FALSE);

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

Создаёт новый объект Proc, привязанный к текущему контексту. Proc::new может быть вызван без блока только внутри метода с прикреплённым блоком, в этом случае этот блок преобразуется в объект Proc.

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

Публичные методы экземпляров

prc << g → a_proc Показать исходный код
static VALUE
proc_compose_to_left(VALUE self, VALUE g)
{
    return rb_proc_compose_to_left(self, to_callable(g));
}

Возвращает процедуру, которая является композицией этой процедуры и заданной g. Возвращаемая процедура принимает переменное количество аргументов, вызывает g с ними, а затем вызывает эту процедуру с результатом.

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

См. Proc#>> для подробных объяснений.

proc === obj → result_of_proc Показать исходный код
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    /* removed */
}

Вызывает блок с obj в качестве параметра процедуры, как в Proc#call. Это позволяет объекту процедуры быть объектом назначения в when разделе в инструкции case.

prc >> g → a_proc Показать исходный код
static VALUE
proc_compose_to_right(VALUE self, VALUE g)
{
    return rb_proc_compose_to_right(self, to_callable(g));
}

Возвращает процедуру, которая является композицией этой процедуры и заданной g. Возвращаемая процедура принимает переменное количество аргументов, вызывает эту процедуру с ними, а затем вызывает 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#<<.

prc[params,...] → obj Показать исходный код
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    /* removed */
}

Вызывает блок, устанавливая параметры блока на значения в 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.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)

См. также Proc#lambda?.

arity → integer Показать исходный код
static VALUE
proc_arity(VALUE self)
{
    int arity = rb_proc_arity(self);
    return INT2FIX(arity);
}

Возвращает количество обязательных аргументов. Если блок объявлен без аргументов, возвращает 0. Если блок известен как принимающий ровно n аргументов, возвращает n. Если блок имеет необязательные аргументы, возвращает -n-1, где n — количество обязательных аргументов, за исключением блоков, которые не являются лямбдами и имеют только конечное число необязательных аргументов; в последнем случае возвращает 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
binding → binding Показать исходный код
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;

  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_symbol:
        goto error;
      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;
            }
            else {
              error:
                rb_raise(rb_eArgError, "Can't create Binding from C level Proc");
                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->location.pathobj);
        bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq));
    }
    else {
        RB_OBJ_WRITE(bindval, &bind->pathobj,
                     rb_iseq_pathobj_new(rb_fstring_lit("(binding)"), Qnil));
        bind->first_lineno = 1;
    }

    return bindval;
}

Возвращает привязку, связанную с prc.

def fred(param)
  proc {}
end

b = fred(99)
eval("param", b.binding)   #=> 99
call(params,...) → obj Показать исходный код
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    /* removed */
}

Вызывает блок, устанавливая параметры блока на значения в 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.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)

См. также Proc#lambda?.

curry → a_proc Показать исходный код
curry(arity) → a_proc
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);
}

Возвращает процедуру с частичным применением аргументов (curried). Если необязательный аргумент arity задан, он определяет количество аргументов. Процедура с частичным применением получает некоторые аргументы. Если предоставлено достаточное количество аргументов, она передает их исходной процедуре и возвращает результат. В противном случае возвращает другую процедуру с частичным применением, которая принимает оставшиеся аргументы.

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
hash → integer Показать исходный код
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);
}

Возвращает значение хэша, соответствующее телу процедуры.

См. также Object#hash.

inspect()
Псевдоним для: to_s
lambda? → true или false Показать исходный код
VALUE
rb_proc_lambda_p(VALUE procval)
{
    rb_proc_t *proc;
    GetProcPtr(procval, proc);

    return proc->is_lambda ? Qtrue : Qfalse;
}

Возвращает true, если объект Proc является лямбдой. false, если не лямбда.

Характер лямбды влияет на обработку аргументов и поведение return и break.

Объект 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

Proc#lambda? — предикат для этих ухищрений. Возвращает true, если ухищрения не применяются.

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

Proc.new эквивалентно proc.

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

lambda, proc и 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 определяет метод, не имеющий ухищрений.

parameters → array Показать исходный код
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 rb_unnamed_parameters(rb_proc_arity(self));
    }
    return rb_iseq_parameters(iseq, is_proc);
}

Возвращает информацию о параметрах этой процедуры.

prc = lambda{|x, y=42, *other|}
prc.parameters  #=> [[:req, :x], [:opt, :y], [:rest, :other]]
ruby2_keywords → proc Показать исходный код
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 (proc->block.as.captured.code.iseq->body->param.flags.has_rest &&
                !proc->block.as.captured.code.iseq->body->param.flags.has_kw &&
                !proc->block.as.captured.code.iseq->body->param.flags.has_kwrest) {
            proc->block.as.captured.code.iseq->body->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;
}

Помечает процедуру как передающую ключевые слова через обычный аргумент splat. Это следует применять только к процедурам, принимающим аргумент splat (*args), но не явные ключевые слова или ключевой splat. Она помечает процедуру таким образом, что если процедура вызывается с ключевыми аргументами, то последний аргумент-хеш помечается специальным флагом. Если он является последним элементом в обычном аргументе splat для другого вызова метода, и этот вызов метода не содержит явных ключевых слов или ключевого splat, то последний элемент интерпретируется как ключевые слова. Другими словами, ключевые слова будут переданы через процедуру в другие методы.

Это следует использовать только для процедур, делегирующих ключевые слова другому методу, и только для обратной совместимости с версиями Ruby до 2.7.

Этот метод, вероятно, будет удалён в какой-то момент, так как он существует только для обратной совместимости. Поскольку он отсутствует в версиях Ruby до 2.7, перед его вызовом проверьте, что процедура отвечает на этот метод. Также имейте в виду, что если этот метод будет удалён, поведение процедуры изменится так, что она не будет передавать ключевые слова.

module Mod
  foo = ->(meth, *args, &block) do
    send(:"do_#{meth}", *args, &block)
  end
  foo.ruby2_keywords if foo.respond_to?(:ruby2_keywords)
end
source_location → [String, Integer] Показать исходный код
VALUE
rb_proc_location(VALUE self)
{
    return iseq_location(rb_proc_get_iseq(self, 0));
}

Возвращает имя файла и номер строки Ruby, содержащие эту процедуру или nil если эта процедура не была определена в Ruby (т. е. нативная).

to_proc → proc Показать исходный код
static VALUE
proc_to_proc(VALUE self)
{
    return self;
}

Часть протокола для преобразования объектов в Proc объекты. Экземпляры класса Proc просто возвращают сами себя.

to_s → string Показать исходный код
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
yield(params,...) → obj Показать исходный код
static VALUE
proc_call(int argc, VALUE *argv, VALUE procval)
{
    /* removed */
}

Вызывает блок, устанавливая параметры блока на значения в 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.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)

См. также Proc#lambda?.

Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API