Blocks
Блоки — это небольшие анонимные функции, которые можно передавать в методы.
Блоки заключаются в оператор do / end или в фигурные скобки {}, и у них может быть несколько аргументов. Имена аргументов определяются между двумя вертикальными | | знаками.
Блок можно написать в одну строку
[1, 2, 3].each { |num| puts num }
^^^^^ ^^^^^^^
аргумент(ы) тело блока
Code language: Ruby (ruby)
Или в несколько строк
[1, 2, 3].each do |num|
puts num
end
Code language: Ruby (ruby)
Блоки полезны, так как записанную в них логику (код), можно использовать много раз
Ключевое слово Yield
Yield — вызывает блок, он вам нужен. Это как методы используют блоки. Когда вы используете ключевое слово yield, код внутри блока запускается и выполняет свою работу. Так же, как когда вы вызываете обычный метод Ruby.
def print_once
yield
end
print_once { puts "Block is being run" }
Code language: Ruby (ruby)
Это запускает любой блок, переданный в print_once, в результате на экране будет напечатано «Block is being run». yield — можно использовать несколько раз? Каждый раз, когда вы вызываете yield, блок запускается, так что это похоже на повторный вызов того же метода.
def print_twice
yield
yield
end
print_twice { puts "Hello" }
# "Hello"
# "Hello"
Code language: Ruby (ruby)
Вы можете передать любое количество аргументов yield.
def one_two_three
yield 1
yield 2
yield 3
end
one_two_three { |number| puts number * 10 }
# 10, 20, 30
Code language: Ruby (ruby)
Эти аргументы затем становятся аргументами блока.
Неявные и явные блоки
Блоки могут быть «явными» или «неявными». Явный означает, что вы даете ему имя в своем списке параметров. Вы можете передать явный блок другому методу или сохранить его в переменной для дальнейшего использования.
def explicit_block(&block)
block.call # same as yield
end
explicit_block { puts "Explicit block called" }
Code language: Ruby (ruby)
Обратите внимание на параметр & block …
Вот как вы определяете имя блока!
Как проверить, был ли дан блок?
Если вы попытаетесь уступить без блока, вы получите ошибку не задан блок (yield). Вы можете проверить, был ли передан блок с помощью block_given? метод.
def do_something_with_block
return "No block given" unless block_given?
yield
end
Code language: Ruby (ruby)
Это предотвращает ошибку, если кто-то вызывает ваш метод без блокировки.
Lambda
Лямбда — это способ определить блок и его параметры с помощью специального синтаксиса. Вы можете сохранить эту лямбду в переменной для дальнейшего использования. Синтаксис определения лямбда-выражения Ruby выглядит так:
say_something = -> { puts "This is a lambda" }
Code language: JavaScript (javascript)
Вы также можете использовать альтернативный синтаксис: лямбда вместо ->.
Определение лямбда-выражения не приведет к запуску кода внутри него, так же как определение метода не приведет к запуску метода, для этого вам нужно использовать метод call.
say_something = -> { puts "This is a lambda" }
say_something.call
# "This is a lambda"
Code language: PHP (php)
Есть и другие способы вызова lambda, но лучше придерживаться .call для ясности. Список способов вызова
my_lambda = -> { puts "Lambda called" }
my_lambda.call
my_lambda.()
my_lambda[]
my_lambda.===
Code language: Ruby (ruby)
Лямбды также могут принимать аргументы
times_two = ->(x) { x * 2 }
times_two.call(10)
# 20
Code language: PHP (php)
Если вы передадите лямбда-выражение неправильное количество аргументов, оно вызовет исключение, как и обычный метод.
Procs
my_proc = Proc.new { |x| puts x }
Code language: PHP (php)
t = Proc.new { |x,y| puts "I don't care about arguments!" }
t.call
# "I don't care about arguments!"
Code language: Ruby (ruby)
Разница между «proc» и «lambda» так же заключается в возвращении значения ( «return» )
lambda — делает возврат как обычный метод
proc — пытается ввернуть из текущего контекста
Пример ниже показывает как proc вызовет ошибку LocalJumpError. Причина в том, что мы не можем вызвать из более высокого уровня
# Работает
my_lambda = -> { return 1 }
puts "Lambda result: #{my_lambda.call}"
# Вызовет ошибку
my_proc = Proc.new { return 1 }
puts "Proc result: #{my_proc.call}"
Code language: Ruby (ruby)
Если «proc» внутри метода, то вызов ‘return’ был бы эквивалентен возврату из этого метода.
def call_proc
puts "Before proc"
my_proc = Proc.new { return 2 }
my_proc.call
puts "After proc"
end
p call_proc
# Prints "Before proc" but not "After proc"
Code language: Ruby (ruby)
lambda — определяется с помощью -> {}, а proc с помощью Proc.new {}.
proc — возвращаются из текущего метода, lambda возвращаются из самой лямбды.
proc — не заботятся о правильном количестве аргументов, а lambda вызовут исключение.
Различия между proc и lambda
Это еще раз доказывает, что lambda ближе к обычным методам, чем proc
Закрытие
У proc и lambda есть еще один специальный атрибут.
Когда вы создаете proc, он захватывает текущую область выполнения. Эта концепция, которую иногда называют закрытием, означает, что
proc будет иметь такие значения, как локальные переменные и методы, из контекста, в котором она была определена
Они не содержат фактических значений, но являются ссылкой на них, поэтому, если переменные изменятся после создания proc, proc всегда будет иметь последнюю версию.
def call_proc(my_proc)
count = 500
my_proc.call
end
count = 1
my_proc = Proc.new { puts count }
p call_proc(my_proc) # What does this print?
Code language: Ruby (ruby)
В этом примере у нас есть локальная переменная count, для которой установлено значение 1.
У нас также есть proc с именем my_proc и метод call_proc, который запускает (через .call) любую proc или lambda, переданную в качестве аргумента. Из-за эффекта «закрытия» будет напечатано 1. Это происходит потому, что proc использует значение count из того места, где была определена процедура, а это вне метода. определение.
Связывающий класс
Где proc и lambda хранят эту информацию об области видимости? Когда создается объект Binding с помощью метода binding, вы создаете «привязку» к этой точке кода. Каждая переменная, метод и класс, определенные на этом этапе, будут доступны позже через этот объект, даже если вы находитесь в совершенно другой области.
def return_binding
foo = 100
binding
end
# Foo доступен, спасибо binding,
# хотя мы вне метода
# где это было определено?
puts return_binding.class
puts return_binding.eval('foo')
# Если вы попытаетесь напечатать foo напрямую, вы получите сообщение об ошибке.
# Причина в том, что foo никогда не определялся вне метода.
puts foo
Code language: Ruby (ruby)
Другими словами, выполнение чего-либо в контексте объекта binding аналогично тому, как если бы этот код находился в том же месте, где была определена этот binding (помните метафору «привязки»).
Не нужно напрямую использовать объекты привязки, но все же хорошо знать это
curry method
Этот метод позволяет передавать некоторые или все необходимые аргументы. Если вы передадите только частичное количество аргументов, вы получите новую proc с уже «предварительно загруженными» аргументами. Когда все аргументы будут предоставлены, proc будет выполнена.
Информация была взята с rubyguides.com