Spec-Zone.ru › Ruby 3.4

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

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

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

Семантика Lambda и не-lambda

Proc бывают двух типов: lambda и не-lambda (обычные proc). Различия:

  • В lambda, return и break означает выход из этой lambda;

  • В не-lambda proc, return означает выход из охватывающего метода (и вызовет LocalJumpError, если вызван за пределами метода);

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

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

  • Обычные proc принимают аргументы более снисходительно: отсутствующие аргументы заполняются nil, одиночные аргументы-массивы Array деконструируются, если у proc несколько аргументов, и не возникает ошибок при наличии дополнительных аргументов.

Примеры:

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

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

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

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

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

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

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

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

test_return # => 4, return from proc

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

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

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

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

Вы можете отличить lambda от обычного proc, используя экземплярный метод lambda?.

Семантика lambda обычно сохраняется в течение жизни proc, включая &-декомпозицию в блок кода:

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

Единственным исключением является динамическое определение метода: даже если он определен путём передачи не-lambda proc, методы по-прежнему имеют обычную семантику проверки аргументов.

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

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

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

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

Обёртка def2 получает body как не-lambda proc, но определяет метод с нормальной семантикой.

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

Любой объект, реализующий метод to_proc, может быть преобразован в proc с помощью оператора &, и поэтому может быть использован итераторами.

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

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

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

Из основных классов Ruby, этот метод реализован классами 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 выходят из блока в lambda, lambda не могут быть осиротевшими.

Анонимные параметры блока

Для упрощения написания коротких блоков, Ruby предоставляет два разных типа анонимных параметров: it (один параметр) и пронумерованные: _1, _2 и так далее.

# 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 - это имя, доступное внутри блока, когда явных параметров не определено, как показано выше.

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

it - это «мягкий ключевой» элемент: это не зарезервированное имя, и его можно использовать в качестве имени для методов и локальных переменных:

it = 5 # no warnings
def it(&block) # RSpec-like API, no warnings
   # ...
end

it можно использовать как локальную переменную даже в блоках, использующих его как неявный параметр (хотя такой стиль очевидно вызывает путаницу):

[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
}

В блоке с явными параметрами использование it вызывает исключение:

[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

Но если локальное имя (переменная или метод) доступно, оно будет использоваться:

it = 5
[1, 2, 3].each { |x| p it }
# Prints 5, 5, 5

Блоки, использующие it могут быть вложены:

%w[test me].each { it.each_char { p it } }
# Prints "t", "e", "s", "t", "m", "e"

Блоки, использующие it считаются имеющими один параметр:

p = proc { it**2 }
l = lambda { it**2 }
p.parameters     # => [[:opt, nil]]
p.arity          # => 1
l.parameters     # => [[:req]]
l.arity          # => 1

Пронумерованные параметры

Пронумерованные параметры – это ещё один способ неявно назвать параметры блока. В отличие от it, пронумерованные параметры позволяют ссылаться на несколько параметров в одном блоке.

%w[test me please].each { puts _1.upcase } # prints TEST, ME, PLEASE
{a: 100, b: 200}.map { "#{_1} = #{_2}" } # => "a = 100", "b = 200"

Поддерживаются имена параметров от _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)

Пронумерованные параметры не могут быть смешаны с it:

[10, 20, 30].map { _1 + it }
# SyntaxError: `it` is not allowed when a numbered parameter is already used

Чтобы избежать конфликтов, присвоение локальных переменных или имен аргументов _1, _2 и так далее, вызовет ошибку.

  _1 = 'test'
# ^~ _1 is reserved for numbered parameters (SyntaxError)

Использование неявных пронумерованных параметров влияет на арность блока:

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 } }
# numbered parameter is already used in outer block (SyntaxError)
# %w[test me].each { _1.each_char { p _1 } }
#                    ^~

Методы класса

new {|...| block } → a_proc
Исходный код
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;
}

Создаёт новый объект Proc, привязанный к текущему контексту.

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

Вызывает ArgumentError, если вызывается без блока.

Proc.new    #=> ArgumentError

Методы экземпляра Public Instance

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#>> для подробных объяснений.

prc == other → true или 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.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;
}

Две процедуры одинаковы тогда и только тогда, когда они были созданы из одного блока кода.

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
Также алиасируется как: eql?
(params,...) → obj

Вызывает блок, устанавливая параметры блока на значения в params, используя что-то близкое к семантике вызова метода. Возвращает значение последнего выражения, вычисленного в блоке.

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

Обратите внимание, что prc.() вызывает prc.call() с заданными параметрами. Это синтаксический сахар для скрытия «call».

Для процедур, созданных с помощью lambda или ->(), генерируется ошибка, если передано неправильное количество параметров в процедуру. Для процедур, созданных с помощью Proc.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?.

Псевдоним для: call
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#<<.

(params,...) → obj

Вызывает блок, устанавливая параметры блока на значения в params, используя что-то близкое к семантике вызова метода. Возвращает значение последнего выражения, вычисленного в блоке.

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

Обратите внимание, что prc.() вызывает prc.call() с заданными параметрами. Это синтаксический сахар для скрытия «call».

Для процедур, созданных с помощью lambda или ->(), генерируется ошибка, если передано неправильное количество параметров в процедуру. Для процедур, созданных с помощью Proc.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?.

Псевдоним для: call
arity → целое число
Исходный код
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;

    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;
}

Возвращает binding, связанный с prc.

def fred(param)
  proc {}
end

b = fred(99)
eval("param", b.binding)   #=> 99
call(params,...) → obj
(params,...) → obj
Исходный код
0
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() с заданными параметрами. Это синтаксический сахар для скрытия «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?.

Также алиасируется как: [], ===, yield
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 proc). Если задан необязательный аргумент arity, он определяет количество аргументов. Curried процедура получает некоторые аргументы. Если предоставлено достаточное количество аргументов, она передает их исходной процедуре и возвращает результат. В противном случае возвращает другую 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
eql?(other) → true или false

Две процедуры одинаковы тогда и только тогда, когда они были созданы из одного блока кода.

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 → целое число
Исходный код
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
END_OF_DOCUMENT_MARKER
lambda? → true или false
Исходный код
VALUE
rb_proc_lambda_p(VALUE procval)
{
    rb_proc_t *proc;
    GetProcPtr(procval, proc);

    return RBOOL(proc->is_lambda);
}

Возвращает значение 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(lambda: nil) → массив
Исходный код
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);
}

Возвращает информацию о параметрах этого прога. Если ключевое слово lambda указано и не равно nil, рассматривает прог как лямбду, если true, и как не-лямбду, если 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]]
ruby2_keywords → прог
Исходный код
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;
}

Помечает прог как пропускающий ключевые слова через обычный аргумент 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 → [строка, целое число]
Исходный код
VALUE
rb_proc_location(VALUE self)
{
    return iseq_location(rb_proc_get_iseq(self, 0));
}

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

to_proc → прог
Исходный код
static VALUE
proc_to_proc(VALUE self)
{
    return self;
}

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

to_s → строка
Исходный код
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(параметры,...) → объект

Вызывает блок, устанавливая параметры блока в значения в параметрах, используя что-то близкое к семантике вызова метода. Возвращает значение последнего выражения, вычисленного в блоке.

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

Обратите внимание, что prc.() вызывает prc.call() с заданными параметрами. Это синтаксический сахар для сокрытия «call».

Для прогов, созданных с помощью lambda или ->(), генерируется ошибка, если передано неверное количество параметров в прог. Для прогов, созданных с помощью Proc.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?.

Псевдоним для: call

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

Spec-Zone.ru

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