Чтение онлайн

ЖАНРЫ

Программирование на языке Ruby
Шрифт:

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

8.3.5. Преобразование в массив или множество

Каждая перечисляемая структура теоретически может быть тривиально преобразована в массив (методом

to_a
). Например, такое преобразование для хэша дает вложенный массив пар:

hash = {1=>2, 3=>4, 5=>6}

arr = hash.to_a # [[5, 6], [1, 2], [3, 4]]

Синонимом

to_a
является метод
entries
.

Если была затребована библиотека

set
, становится доступен также метод
to_set
. Дополнительная информация о множествах приведена в разделе 9.1.

require 'set'

hash = {1=>2, 3=>4, 5=>6}

set = hash.to_set # #<Set: {[1, 2], [3, 4], [5, 6]}>

8.3.6. Энумераторы

Объект класса

Enumerator
— по существу, обертка, превращающая итераторный метод в полноценный объект
Enumerable
. Обернутый таким способом итератор приобретает все методы и свойства, присущие перечисляемым структурам.

В следующем искусственном примере в классе

Foo
есть итератор и больше ничего. Да и сам-то итератор не делает ничего полезного, только четыре раза вызывает
yield
. Чтобы подчеркнуть особенность его работы, итератор назван
every
, а не
each
.

require 'enumerator'

class Foo

 def every

yield 3

yield 2

yield 1

yield 4

 end

end

foo = Foo.new

# Передается объект и имя итератора...

enum = Enumerable::Enumerator, new(foo, :every)

enum.each {|x| p x } # Печатаются элементы

Поделиться с друзьями: