Читать «Учись программировать (на Ruby)» онлайн - страница 41
Крис Пайн
Методы, возвращающие процедурные объекты
Ещё одна из крутых возможностей, которые можно делать с процедурными объектами, это то, что их можно создавать в методах, а затем возвращать их. Это делает возможным разнообразные сумасшедшие, но мощные программистские штучки (с впечатляющими названиями наподобие ленивое вычисление, бесконечные структуры данных и карринг). Но дело в том, что я почти никогда не использовал это на практике, а также не припомню, чтобы видел, как кто-либо применял это в своём коде. Думаю, это не такого рода вещи, которые обычно нужно делать на Ruby, а, может быть, Ruby просто подталкивает вас находить другие решения – не знаю. В любом случае, я только кратко коснусь этого.
В этом примере метод compose принимает два процедурных объекта и возвращает новый процедурный объект, который, будучи вызван, вызывает первый процедурный объект и передаёт его результат во второй.
def compose proc1, proc2
Proc.new do |x|
proc2.call(proc1.call(x))
end
end
squareIt = Proc.new do |x|
x * x
end
doublelt = Proc.new do |x|
x + x
end
doubleThenSquare = compose doublelt,
squareIt
squareThenDouble = compose squareIt,
doublelt
puts doubleThenSquare.call(5)
puts squareThenDouble.call(5)
>
100
50
Обратите внимание, что вызов proel должен быть внутри скобок при вызове proc2, чтобы он был выполнен первым.
Передача блоков (не proc–объектов) в методы
Ну, хорошо, этот подход представляет чисто академический интерес, к тому же применять его несколько затруднительно. В основном трудность состоит в том, что здесь вам приходится выполнить три шага (определить метод, создать процедурный объект и вызвать метод с процедурным объектом); тогда как есть ощущение, что должно быть только два (определить метод и передать блок непосредственно в этот метод, совсем не используя процедурный объект), поскольку в большинстве случаев вы не хотите использовать процедурный объект / блок после того, как вы передали его в метод. Что ж, да будет вам известно, что в Ruby всё это уже сделано за нас! Фактически, вы уже делали это каждый раз, когда использовали итераторы.
Сначала я быстро покажу вам пример, а затем мы обсудим его.
class Array
def eachEven(&wasABlock_nowAProc)
isEven = true # Мы начинаем с «true», т.к. массив начинается с 0, а он чётный.
self.each do |object| if isEven
wasABlock_nowAProc.call object end
isEven = (not isEven) # Переключиться с чётного на нечётное или наоборот.
end
end
end
['яблоками', 'гнилыми яблоками', 'вишней', 'дурианом'].eachEven do |fruit| puts