Метод inspect во многом похож на to_s за исключением того, что возвращаемая им строка – это попытка показать код на Ruby для создания объекта, который вы ему передали. Здесь он показывает нам весь массив, возвращённый при нашем первом вызове метода doUntilFalse. Вы, должно быть, также заметили, что мы сами никогда не возводили в квадрат этот 0 в конце массива, но поскольку 0 в квадрате всегда равен 0, нам это и не нужно было делать. А так как alwaysFalse, как вы знаете, возвращает всегда false, метод doUntilFalse ничего не делал, когда мы вызвали его во второй раз; он просто вернул то, что ему было передано.
Методы, возвращающие процедурные объекты
Ещё одна из крутых возможностей, которые можно делать с процедурными объектами, это то, что их можно создавать в методах, а затем возвращать их. Это делает возможным разнообразные сумасшедшие, но мощные программистские штучки (с впечатляющими названиями наподобие ленивое вычисление, бесконечные структуры данных и карринг). Но дело в том, что я почти никогда не использовал это на практике, а также не припомню, чтобы видел, как кто-либо применял это в своём коде. Думаю, это не такого рода вещи, которые обычно нужно делать на 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
'Мммм! Я так люблю пирожки с '+fruit+', а вы?' end
# Помните, что мы берём элементы массива с чётными номерами,
# все из которых оказываются нечётными числами; это
# просто потому, что мне захотелось создать подобные трудности.
[1, 2, 3, 4, 5].eachEven do |oddBall|
puts oddBall.to_s+' – НЕ чётное число!' end
Мммм!
Я так люблю пирожи
с яблоками,
а вы?
Мммм!
Я так люблю пирожи
с вишней, а
вы?
–Н И
чётное число!
ЕН – 3
чётное число!
5 – НЕ
чётное число!