Блоки, процы и лямбды (в компьютерных науках называются замыканиями) являются одним из самых мощных аспектов Ruby, а также одним из самых неправильно понятых. Вероятно, это потому, что Ruby обрабатывает замыкания довольно уникальным способом. Ситуация усложняется тем, что в Ruby есть четыре различных способа использования замыканий, каждый из которых немного отличается, а иногда и бессмысленен. Существует довольно много сайтов с очень хорошей информацией о том, как замыкания работают в Ruby. Но я еще не нашел хорошего, окончательного руководства там.
class Array
def iterate!(&code)
self.each_with_index do |n, i|
self[i] = code.call(n)
end
end
end
array = [1, 2, 3, 4]
array.iterate! do |n|
n ** 2
end
Процедуры, AKA, Procs
Блоки очень удобны и синтаксически просты, однако мы можем захотеть иметь в своем распоряжении много разных блоков и использовать их несколько раз. Таким образом, пропуск одного и того же блока снова и снова потребует от нас повторения. Однако, поскольку Ruby полностью объектно-ориентирован, это можно сделать довольно аккуратно, сохранив повторно используемый код как сам объект. Этот повторно используемый код называется Proc (сокращение от процедуры). Единственная разница между блоками и процессами заключается в том, что блок - это процесс, который не может быть сохранен и, как таковой, является решением для одноразового использования. Работая с Procs, мы можем начать делать следующее:
class Array
def iterate!(code)
self.each_with_index do |n, i|
self[i] = code.call(n)
end
end
end
array_1 = [1, 2, 3, 4]
array_2 = [2, 3, 4, 5]
square = Proc.new do |n|
n ** 2
end
Лямбда
До сих пор вы использовали Procs двумя способами, передавая их непосредственно как атрибут и сохраняя их как переменную. Эти Procs действуют очень похоже на то, что другие языки называют анонимными функциями или лямбдами. Чтобы сделать вещи более интересными, лямбды также доступны в Ruby. Взгляните:
class Array
def iterate!(code)
self.each_with_index do |n, i|
self[i] = code.call(n)
end
end
end
array = [1, 2, 3, 4]
array.iterate!(lambda { |n| n ** 2 })
puts array.inspect
Блоки
Самый распространенный, самый простой и, пожалуй, самый «похожий на Ruby» способ использования замыканий в Ruby - это блоки. Они имеют следующий знакомый синтаксис:
array = [1, 2, 3, 4]
array.collect! do |n|
n ** 2
end
puts array.inspect
# => [1, 4, 9, 16]