Программирование на языке Ruby
Программирование на языке Ruby читать книгу онлайн
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
Кроме того, если предполагается пользоваться методами
minmaxsort<=>Итак, перечисляемая структура представляет собой набор, в котором можно производить поиск, который можно обойти и, быть может, отсортировать. В любой определенный пользователем набор, не являющийся подклассом существующего системного класса, имеет смысл подмешивать модуль
EnumerableИмейте в виду — все сказанное о какой-то одной перечисляемой структуре относится ко всем. В качестве примеров таких структур можно назвать массив, хэш, дерево и т.д.
Конечно, у каждой структуры есть свои нюансы. Массив — это упорядоченный набор отдельных элементов, а хэш — неупорядоченный набор пар ключ-значение. Понятно, что в каких-то отношениях они будут вести себя по-разному.
Многие методы, с которыми мы познакомились при изучении массивов и хэшей (например,
mapfindEnumerableМассив — наиболее часто употребляемый набор, подмешивающий этот модуль. Поэтому по умолчанию я буду пользоваться в примерах именно массивами.
8.3.1. Метод inject
Метод
injectОн отражает тот факт, что мы часто хотим обойти список и по ходу «аккумулировать» некоторый результат. Конечно, самый естественный пример — суммирование чисел в списке. Но и для других операций обычно есть некий «аккумулятор» (которому присваивается начальное значение) и применяемая функция (в Ruby она представлена блоком).
В качестве тривиального примера рассмотрим массив чисел, которые нужно просуммировать:
nums = [3,5,7,9,11,13]sum = nums.inject(0) {|x,n| x+n }Обратите внимание, что начальное значение аккумулятора равно 0 («нейтральный элемент» для операции сложения). Затем блок получает текущее значение аккумулятора и значение текущего элемента списка. Действие блока заключается в прибавлении нового значения к текущей сумме.
Ясно, что этот код эквивалентен следующему:
sum = 0nums.each {|n| sum += n }В данном случае уровень абстракции лишь немногим выше. Если идея метода
injectНачальное значение аккумулятора задавать необязательно. Если оно опущено, то в качестве такового используется значение первого элемента, который при последующих итерациях пропускается,
sum = nums.inject {|x,n| x+n }# To же самое, что:sum = nums[0]nums[1..-1].each {|n| sum + = n }Другой похожий пример — вычисление произведения чисел. В данном случае аккумулятору следует присвоить начальное значение 1 (нейтральный элемент для операции умножения).
prod = nums.inject(1) {|x,n| x*n }# илиprod = nums.inject {|x,n| x*n }В следующем немного более сложном примере мы находим самое длинное слово в списке:
words = %w[ alpha beta gamma delta epsilon eta theta ]longest_word = words.inject do |best,w| w.length > best.length ? w : bestend# Возвращается значение "epsilon".8.3.2. Кванторы
Кванторы
any?all?truefalseNums = [1,3,5,8,9]# Есть ли среди чисел четные?flag1 = nums.any? {|x| x % 2 == 0 } # true# Все ли числа четные?flag2 = nums.all? {|x| x % 2 == 0 } # falseЕсли блок не задан, то просто проверяется значение истинности каждого элемента. Иными словами, неявно добавляется блок
{|x| x }flag1 = list.all? # list не содержит ни одного false или nil.flag1 = list.any? # list содержит хотя бы одно истинное значение # не nil и не false).8.3.3. Метод partition
Как говорится, «в мире есть два сорта людей: те, что делят людей по сортам, и те, что не делят». Метод
partitionЕсли при вызове
partitiontruenums = [1, 2, 3, 4, 5, 6, 7, 8, 9]odd_even = nums.partition {|x| x % 2 == 1 }# [[1,3,5,7,9],[2,3,4,6,8]]under5 = nums.partition {|x| x < 5 }# [[1,2,3,4],[5,6,7,8,9]]squares = nums.partition {|x| Math.sqrt(x).to_i**2 == x }# [[1,4,9], [2,3,5,6,7,8]]Если нужно разбить набор больше чем на две группы, придется написать собственный метод. Я назвал его
classifySetmodule Enumerable def classify(&block) hash = {} self.each do |x| result = block.call(x)
