Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на языке Ruby.docx
Скачиваний:
16
Добавлен:
06.09.2019
Размер:
1.74 Mб
Скачать

8.3.2. Кванторы

Кванторы any? и all? появились в версии Ruby 1.8, чтобы было проще проверять некоторые свойства набора. Оба квантора принимают в качестве параметр блок (который должен возвращать значение true или false).

Nums = [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 относится не к людям (хотя мы можем представить их в Ruby как объекты), но тоже делит набор на две части.

Если при вызове partition задан блок, то он вычисляется для каждого элемента набора. В результате создаются два массива: в первый попадают элементы, для которых блок вернул значениеtrue, во второй — все остальные. Метод возвращает массив, двумя элементами которого являются эти массивы.

nums = [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]]

Если нужно разбить набор больше чем на две группы, придется написать собственный метод. Я назвал его classify по аналогии с методом из класса Set.

module Enumerable

 def classify(&block)

  hash = {}

  self.each do |x|

   result = block.call(x)

   (hashfresult] ||= []) << x

  end

  hash

 end

end

nums = [1,2,3,4,5,6,7,8,9]

mod3 = nums.classify {|x| x % 3 }

# { 0=>[3,6,9], 1=>[1,4,7], 2=>[2,5,8] }

words = %w( area arboreal brick estrous clear donor ether filial

patina ]

vowels = words.classify {|x| x.count("aeiou") }

# {1=>["brick"], 2=>["clear", "donor", "ether"],

#  3=>["area", "estrous", "filial", "patina"], 4=>["arboreal"]}

initials = words.classify {|x| x[0..0] }

# {"a"=>["area", "arboreal"], "b"=>["brick"], "c"=>["clear"],

#  "d"=>["donor"], "p"=>["patina"], "e"=>["estrous", "ether"],

#  "f"=>["filial"]}

8.3.4. Обход с группировкой

До сих пор мы обходили список по одному элементу за раз. Но иногда желательно на каждой итерации анализировать по два, три или более элементов.

Итератор each_slice принимает в качестве параметра число n, равное числу просматриваемых на каждой итерации элементов. (Для работы с ним нужна библиотека enumerator.) Если не осталось достаточного количества элементов, размер последнего фрагмента будет меньше.

require 'enumerator'

arr = [1,2,3,4,5,6,7,8,9,10]

arr.each_slice(3) do |triple|

 puts triple.join(",")

end

# Выводится:

# 1,2,3

# 4,5,6

# 7,8,9

# 10

Имеется также итератор each_cons, который позволяет обходить набор методом «скользящего окна» заданного размера. (Если название кажется вам странным, знайте, что это наследие языка Lisp.) В таком случае фрагменты всегда будут иметь одинаковый размер.

require 'enumerator'

arr = [1,2,3,4,5,6,7,8,9,10]

arr.each_cons(3) do |triple|

 puts triple.join(",")

end

# Выводится:

# 1,2,3

# 2,3,4

# 3,4,5

# 4,5,6

# 5,6,7

# 6,7,8

# 7,8,9

# 8,9,10