Читать «Учись программировать (на 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