Андрей Васильев
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.open
block_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) # => 46
thing
метода 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
, который позволяет создавать блоки с предустановленными аргументами