Андрей Васильев
2019
Enumerator реализует «внешние итераторы»to_enum на коллекции или enum_for с указанием названия итератораa = [1, 3, "cat"]
enum_a = a.each # Созадаём нумератор
enum_a.next # => 1
enum_b = a.to_enum # Создаём нумератор
enum_c = a.enum_for(:each) # Создаём нумераторНумераторы являются объектами класса Enumerator, который включает в себя модуль Enumerable, что делает доступным для нумераторов всех «классных» методов
loopЗадачей данного метода является бесконечный вызов блока. Если внутри блока используются нумераторы, то выход будет осуществлён, когда закончатся значения в нумераторе
Метод each_with_index определён в модуле Enumerable
result = []
['a', 'b', 'c'].each_with_index do |item, index|
result << [item, index]
end
result # => [["a", 0], ["b", 1], ["c", 2]]Метод with_index определён в классе Enumerator
enum_forМетоду enum_for можно передать название метода-итератора, который будет предоставлять значения последовательности
Если итератор ожидает аргументов, то их следует передать после имени метода
Нумераторы могут быть созданы на основе обычного блока, который предоставляет очередные значения
Если генерирующий блок способен предоставлять бесконечное число значений, то его надо указать “ленивым”
Хорошей практикой при создании собственного итератора является возвращение нумератора в случае, когда блок не ассоциирован с данным методом
В результате ваш собственный итератор можно будет использовать как встроенные итераторы: либо в форме ассоциации с болоком, либо в форме получения нумератора
Зачастую необходимо выполнять связные действия, например открытый файл обязательно должне быть закрыт
self, относятся к классу, а не к объекту класса (“статические”)*args в аргументах метода open_and_process собирает все аргументы в массив args*args в вызове метода open раскрывает содержимое массива и записывает их как аргументы метода**opts для обработки именованных аргументовFile.openblock_given? проверяет наличие блока и позволяет реализовать альтернативное поведениеДанная техника применяется в итераторах Array, Hash, Enumerable и т.д. для обработки ситуации работы метода с блоком и без него. Если вы не ассоциировали блок с итератором, то он вернёт нумератор
Блоки похожи на анонимные методы, однако с ними можно общаться как с объектами: сохранять в переменные…
Блоки представлены классом Proc
reach = Proc.new do |param|
puts "You called #{param}"
end
reach.call(42) # => You called 42
reach.call('scar') => You called scarОбъекты можно вернуть из методов
Блоки можно создавать с помощью метода lambda
Или использовать краткий синтаксис ->
nilвывод Используйте лямбды, т.к. они предоставляют простую модель использования и гарантии
return выходит из лямбды, а не из метода, её вызвавшую.return приведёт к выбросу исключения, если будет вызвано вне связанного методавывод Не используйте ключевое слово return внутри блоков
Лямбды и блоки определены в едином классе Proc
Для вызова блока он предоставляет следующие методы:
#call(params): action.call(42)#.(params): action.(42)#[params]: action.[42]#yield(params): action.yield(42)Все формы равносильны между собой. Рекомендуется использовать #call()
Замыкание - возможность доступа к переменным, объявлённых вне блока
def n_times(thing)
lambda {|n| thing * n }
end
p1 = n_times(23)
p1.call(3) # => 69
p1.call(2) # => 46thing метода n_times находится в области видимости выражений блокаthingМожно реализовать простой генератор следующим образом
Современным способом описания коротких лямбд является
lambda длиннее ->Любой класс, реализующий метод #to_proc может быть преобразован в блок с помощью оператора &
Создаёт блок, который будет вызывать метод с именем символа
Создаёт блок, который связывает ключи с их значением
Метод Object#method(symbol) позволяет создать объект класса Method, который позволяет вызывать данный метод в стиле лямбды
Интерфейс класса Method повторяет в ключевых моментах интерфейс класса Proc и де-факто может использоваться как альтернатива блокам и лямбдам
Классы Proc и Method предоставляют методы << и >>, которые позволяют создать цепочки обработки данных
multiplication = lambda {|x| x * x }
addition = lambda {|x| x + x }
(multiplication << addition).call(2) #=> 16
(multiplication >> addition).call(2) #=> 8Ограничением такого подхода является то, чтобы формат передаваемых данных был в точности тем, что ожидают блоки
Объекты классов Proc и Method поддерживают метод curry, который позволяет создавать блоки с предустановленными аргументами