
Когда я впервые изучал Ruby, ничто не вызывало у меня большего беспокойства, чем необходимость различать, какое перечисление использовать. Как раз тогда, когда я думал, что справился с этим, бесчисленные боги дали мне пощечину и рассмеялись мне в лицо.
Пытаясь подавить свое беспокойство и добраться до сути этой неуловимой темы, я решил разложить все по полочкам, начав с самого слова. По данным Мерриам-Вебстер:
Перечислить: (1) установить количество или подсчитать; (2) указать один за другим или перечислить
В простейшем случае перечисляемые элементы проверяют каждый элемент в массиве или хеше, чтобы определитьсоответствует ли он критериям, указанным в блоке кода. Если элемент соответствует критериям, элемент предназначен для вывода. Однако вывод определяется конкретным используемым перечислимым, поскольку каждое из них ведет себя по-разному.
Далее следует попытка новичка объяснить, как некоторые распространенные методы — each, map, find и select — работают как перечисления для анализа и вывода элементов из массивов (для простоты в этой статье я сосредоточусь на массивах).
#каждый
По сути, #each — это универсальный метод, который применяет блок кода к каждому элементу массива. Давайте посмотрим на пример:
numbers = [100, 200, 300]
numbers.each do |n|
text = "I want #{n} pieces of chocolate!"
puts text
end
Здесь у нас есть массив чисел: [100, 200, 300]. В следующей строке я использую #each для последовательного перебора всех трех элементов массива. Я использую «n» в качестве переменной блока кода, через которую интерполируется каждое число, передаваемое в выводящееся предложение. Результат:
I want 100 pieces of chocolate! I want 200 pieces of chocolate! I want 300 pieces of chocolate!
В отличие от других перечисляемых, #each не собирает результаты прохождения элементов массива через блок кода. В приведенном выше примере, кроме включения интерполированного числа в выходное предложение, нет никаких указаний на то, каковы результаты работы перечислимого.
Опять же, #each — самый простой из этих методов. Хотите что-то более захватывающее? Читать дальше…
#карта
Мне потребовалось некоторое время, чтобы полностью понять это, потому что название, по крайней мере, на мой взгляд, не отражает его функции. При применении к массиву #map возвращает новый массив, содержащий то же количество элементов, что и в исходном массиве, но исходные элементы были каким-то образом изменены после прохождения через блок кода в качестве аргументов. . Другими словами, #map изменяет исходный массив путем однозначного преобразования — массив имеет ту же длину, но преобразуется каждый элемент.
numbers = [10, 20, 30] numbers.map do |n| n * 10 end
Начнем с массива чисел. Используя #map для перебора массива, каждый элемент массива передается через блок кода в качестве аргумента. Таким образом, на каждой итерации этот код сначала выполняет 10 * 10, затем 20 * 10, а затем 30 * 10. #map собирает выходные данные каждой итерации и помещает их в новый массив:
=> [100, 200, 300]
В исходном массиве было 3 числа, как и в выходном массиве. Давайте попробуем другой пример.
fruits = ["apple", "banana", "strawberry"] fruits.map do |fruit| fruit.upcase end
Здесь я хочу, чтобы #map возвращал массив тех же элементов в исходном массиве, но возвращал их все с заглавной буквы.
=> ["APPLE", "BANANA", "STRAWBERRY"]
#найти
Для меня #find гораздо проще понять, основываясь только на его названии. В документах Ruby указано, что этот метод оценивает исходный массив, чтобы… дождаться его… найти самый первый экземпляр, для которого блок кода не является ложным. Другими словами (и чтобы избавиться от двойного отрицательного значения), он возвращает первый элемент массива, который оказывается истинным после прохождения блока кода. Вот пример:
numbers = [1, 2, 3, 4, 5] numbers.find do |n| n.even? end => 2
Вот что происходит: начиная с массива чисел, я хочу найти первое четное число. #find просматривает массив чисел по одному элементу за раз, чтобы определить, какой элемент является первым, который делает блок кода истинным. Он возвращает «2» и останавливается на этом, потому что #find возвращает только самое первое вхождение того, что мы попросили его найти.
Другой пример:
fruits = ["apple", "banana", "orange", "raspberry", "strawberry"]
fruits.find do |fruit|
fruit.include?("berry")
end
Здесь я хочу, чтобы #find возвращал первый элемент, в имени которого есть «ягода» (обратите внимание, что массив содержит два элемента). Оно начинается с «яблока» и продолжается, поскольку оно ложно. «Банан» тоже выбрасывается, потому что в нем нет «ягоды». Однако, как только он достигает «малины», кодовый блок оказывается верным. Итератор выбирает этот элемент массива, и мы получаем следующее:
=> "raspberry"
Поскольку #find возвращает только первый экземпляр, наш результат имеет смысл.
#выбрать
Наконец, в этом кратком, но, надеюсь, осмысленном туре по перечислимым числам у нас есть #select. Для новичка (также известного как я) #select и #find были еще одним набором перечислимых величин, которые я не мог удержать в чистоте. В конце концов, имена звучали очень похоже. Момент озарения наступил, когда я узнал, что #select и #find_all являются синонимами.
Я только что обсудил #find, который находит и возвращает только первый единственный экземпляр, удовлетворяющий блоку кода. #select возвращает все экземпляры, соответствующие блоку кода. Используя приведенный выше пример, но заменив #select:
numbers = [1, 2, 3, 4, 5] numbers.select do |n| n.even? end => [2, 4]
И, используя второй пример сверху:
fruits = ["apple", "banana", "orange", "raspberry", "strawberry"]
fruits.select do |fruit|
fruit.include?("berry")
end
Я хочу получить список фруктов, в названиях которых есть слово «ягода». Как и выше с #find, метод #select выполняет итерацию по массиву фруктов, чтобы увидеть, содержит ли какой-либо из элементов массива «ягоду». На этот раз, поскольку #select выбирает все элементы массива, для которых блок кода имеет значение true, результирующий массив включает как «малиновый», так и «клубничный»:
=> ["raspberry", "strawberry"]
Я мог бы использовать #find_all вместо #select и получил бы тот же результат.
В заключении
Я никогда не думал, что скажу это, но оказалось, что перечисления действительно интересны и могут делать довольно крутые вещи. Хотя в этой статье были рассмотрены только некоторые основные перечисляемые, существует множество других, которые нужно изучить и освоить.