Перечислимые объекты - это модули в Ruby, которые предоставляют очень разнообразный набор методов, которые в основном используются для просмотра, сортировки или управления коллекциями. Перечисления возможны благодаря концепции миксинов в Ruby, которая позволяет включать модули в класс, а класс имеет доступ к методам модуля. Это мощная концепция, которая позволяет «унаследовать» функциональные возможности из нескольких мест и разделить их между несвязанными классами, уменьшая необходимость переписывать код между классами.

Можно узнать, какие классы используют Enumerable с Object#included_modules.

>> Array.included_modules
=> [Enumerable, Kernel]
>> String.included_modules
=> [Comparable, Kernel]

Обратите внимание, что класс String не включает модуль Enumerable. Это связано с тем, что для того, чтобы класс мог использовать модуль Enumerable, он должен предоставить свой собственный метод #each, чтобы преобразовать последовательные элементы в коллекции в блок.

Метод #each - это простой и отличный метод для итерации по коллекции, передавая каждый элемент объекта в указанный блок.

> ['cat', 'dog', 'bird'].each {|el| puts el.upcase}
CAT
DOG
BIRD
=> ["cat", "dog", "bird"]

Однако, поскольку возвращаемое значение метода #each - это исходная коллекция, для возврата измененной коллекции потребуются дополнительные строки кода. К счастью для нас, существует множество других перечислимых методов, которые обеспечивают дополнительную функциональность и могут сохранять наш код кратким и чистым!

1. #map

#map возвращает новый массив, заполненный результатами однократного запуска блока для каждого элемента в коллекции. Это уменьшает необходимость вручную создавать массив результатов, как это было бы, если бы мы использовали #each enumerable.

>['cat', 'dog', 'bird'].map {|el| el.upcase}
=>["CAT", "DOG", "BIRD"]

2. #select or #reject

#select возвращает новую коллекцию, содержащую все элементы в своем приемнике, для которых данный блок возвращает истинное значение.

И наоборот, #reject возвращает новую коллекцию, содержащую все элементы в своем приемнике, для которых данный блок возвращает ложное значение.

>[1,2,3,4].select {|x| x.odd?}
=>[1, 3]
>[1,2,3,4].reject {|x| x.odd?}
=>[2,4]

3. #count

Метод #count можно использовать тремя способами.

Во-первых, без аргументов его можно использовать как синоним длины.

>[1,2,3,4,5].count
=> 5

Во-вторых, с одним аргументом вернет количество элементов, равное данному аргументу.

>[1,2,3,4,5].count(2)
=> 1

И в-третьих, для данного блока будет возвращено количество элементов, которые при передаче в данный блок вернут истинное значение.

>[1,2,3,4,5].count {|int| int.odd?}
=> 3

4. #reduce или #inject

Метод #reduce или #inject работает как агрегатор, объединяя все элементы своего получателя, применяя бинарную операцию, и может быть вызван тремя способами.

Во-первых, с одним аргументом:

>[1,2,3].reduce(:+)
=> 6

#reduce принимает символ, который называет двоичный метод или оператор

Во-вторых, с блоком и без аргумента - у блока два параметра: аккумулятор и текущий элемент:

>[1,2,3].reduce {|acc, el| acc + el}
=> 6

Функционально это эквивалентно предыдущему примеру. Первый элемент в массиве используется как аккумулятор, который затем переназначается после каждой итерации. Однако использование этого блока дает больший контроль над тем, как уменьшить приемник - вы не ограничены двоичными методами / операциями, как в предыдущем примере.

И, наконец, #reduce может вызываться как с блоком, так и с аргументом:

>[12,2,2].reduce(48) {|acc, el| acc/el}
=> 1

Интерпретатор сначала назначает аккумулятор данному аргументу, а затем выполняет итерацию по всему получателю, не пропуская первый элемент. В приведенном выше примере шаги оцениваются следующим образом:

48/12 = 4

4/2 = 2

2/2 = 1

В заключении…

Перечисления - это мощный инструмент, который может выполнять сложные операции с относительно небольшим количеством строк кода. Отчасти радость и волнение, которые я испытал с тех пор, как я начал свое путешествие по изучению программирования на Ruby, связан с изучением множества различных перечислимых методов и поиском новых способов подхода и решения проблем.