Ruby/Подробнее о массивах: различия между версиями

Содержимое удалено Содержимое добавлено
м <source> -> <syntaxhighlight> (phab:T237267)
Строка 7:
Массив создаётся как минимум тремя способами. Первый способ:
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4, 5, 6]</sourcesyntaxhighlight>
 
Вы просто перечисляете элементы массива через запятую, а границы массива обозначаете квадратными скобками. С таким методом создания массива мы уже встречались. А теперь попробуем второй способ, через вызов метода <code>.new</code> класса <code>Array</code>:
 
<sourcesyntaxhighlight lang="ruby">Array.new(6){ |index| index + 1 } #=> [1, 2, 3, 4, 5, 6]</sourcesyntaxhighlight>
 
Параметром метода <code>.new</code> является количество элементов будущего массива (в данном случае это число 6). В фигурных скобках указано, как мы будем заполнять массив. В данном случае значение элемента массива будет больше на единицу его индекса. Третий способ заключается в создании объекта типа <code>Range</code> (диапазон) и вызове метода <code>.to_a</code>:
 
<sourcesyntaxhighlight lang="ruby">(1..6).to_a #=> [1, 2, 3, 4, 5, 6]</sourcesyntaxhighlight>
 
Есть ещё много способов, но эти три используются чаще всего.
Строка 23:
Методом <code>to_a</code> очень удобно создавать из диапазона массив, содержащий упорядоченные элементы данного диапазона.
 
<sourcesyntaxhighlight lang="ruby">(1..10).to_a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
("a".."d").to_a #=> ["a", "b", "c", "d"]</sourcesyntaxhighlight>
 
Раз уж речь зашла о диапазонах, то давайте посмотрим, как они позволяют получать подмассивы. И насколько изящно у них это получается. Рассмотрим массив:
 
<sourcesyntaxhighlight lang="ruby">["a", "b", "c", "d", "e"]</sourcesyntaxhighlight>
 
Традиционно нумерация массива начинается с нуля и возрастает по одному:
Строка 40:
Плюсы расставлены лишь для красоты. Но вернёмся к отрицательной индексации. Каков её смысл? Чтобы его пояснить, давайте решим задачку: дан массив, требуется получить предпоследний элемент.
 
<sourcesyntaxhighlight lang="ruby">array = ["a", "b", "c", "d", "e"]
array[array.size - 2] #=> "d"</sourcesyntaxhighlight>
 
В данном случае мы использовали метод <code>.size</code>, который возвращает размер массива. Разработчики заметили, что вызов <code>array.size</code> приходится писать довольно часто, и решили от него избавиться. Вот что получилось:
 
<sourcesyntaxhighlight lang="ruby">array = ["a", "b", "c", "d", "e"]
array[-2] #=> "d"</sourcesyntaxhighlight>
 
Индекс <code>-2</code> значит «второй с конца элемент массива». Вот так и появилась отрицательная индексация. Теперь давайте разберёмся с диапазонами. Оказывается, в них тоже можно использовать отрицательную индексацию. Вот как можно получить все элементы массива кроме первого и последнего:
 
<sourcesyntaxhighlight lang="ruby">array = ["a", "b", "c", "d", "e"]
array[1..-2] #=> ["b", "c", "d"]</sourcesyntaxhighlight>
 
Или так:
 
<sourcesyntaxhighlight lang="ruby">array = ["a", "b", "c", "d", "e"]
array[1...-1] #=> ["b", "c", "d"]</sourcesyntaxhighlight>
 
Второй вариант с тремя точками, что автоматически приближает правую границу диапазона на одну позицию влево.
Строка 64:
Для Ruby ''двумерный массив'' — это не более чем массив, содержащий одномерные массивы. Вот несколько примеров двумерных массивов:
 
<sourcesyntaxhighlight lang="ruby">[[1], [2, 3], [4]] # разная длина элементов-массивов
[[1, 2], [3, 4]] # одинаковая длина
[["прива", "Привет"], ["пока", "Всего хорошего"]] # двумерный массив (классика)
[["прива", "Привет"], [1, ["пока", "Всего хорошего"]]] # гибрид двух-трёх-мерного массива</sourcesyntaxhighlight>
 
* Двумерность массива средствами языка не отслеживается. Вполне могут возникнуть гибриды разномерных массивов.
Строка 81:
В Ruby массивы динамические: в каждый конкретный момент времени неизвестно, сколько в нём элементов. Чтобы не плодить тайн подобного рода и был реализован метод <code>.size</code>:
 
<sourcesyntaxhighlight lang="ruby">[1, "считайте", 3, "количество", 5, 6, "запятых", 2, 5].size #=> 9</sourcesyntaxhighlight>
 
Мы явно указали массив, но на его месте могла стоять переменная:
 
<sourcesyntaxhighlight lang="ruby">array = [1, "считайте", 3, "количество", 5, 6, "запятых", 2, 5]
array.size #=> 9</sourcesyntaxhighlight>
 
Метод <code>.size</code> есть у многих классов. Например, у ассоциативных массивов и строк. И даже у целых чисел.
Строка 94:
Вспомните сколько усилий вам приходилось прилагать, чтобы найти максимальный элемент? А сколько раз вы повторяли этот кусок кода в своих программах? Ну а в Ruby поиск максимального элемента осуществляется при помощи метода <code>.max</code>, а в более сложных случаях при помощи метода <code>.max_by</code>. Вот как это выглядит:
 
<sourcesyntaxhighlight lang="ruby">["у", "попа", "была", "собака"].max #=> "у" максимальный по значению
["у", "попа", "была", "собака"].max_by{ |elem| elem.size } #=> "собака" максимальный по размеру строки</sourcesyntaxhighlight>
 
Методы <code>.min</code> и <code>.min_by</code> работают аналогично:
 
<sourcesyntaxhighlight lang="ruby">["у", "попа", "была", "собака"].min #=> "была" минимальный по значению
["у", "попа", "была", "собака"].min_by{ |elem| elem.size } #=> "у" минимальный по размеру строки</sourcesyntaxhighlight>
 
Ну как? А в Ruby эти методы уже давно.
Строка 108:
Чтобы упорядочить массив, нужно вызвать метод <code>.sort</code> или <code>.sort_by</code> (начиная с версии 1.8).
 
<sourcesyntaxhighlight lang="ruby">["у", "попа", "была", "собака"].sort
#=> ["была", "попа", "собака", "у"] сортировка по значению
["у", "попа", "была", "собака"].sort_by{ |elem| elem.size }
#=> ["у", "попа", "была", "собака"] сортировка по размеру строки</sourcesyntaxhighlight>
 
Для двумерных массивов:
<sourcesyntaxhighlight lang="ruby"> [[1,0], [16,6], [2,1], [4,5],[4,0],[5,6]].sort_by {|elem| elem[1]}
#=> [[1, 0], [4, 0], [2, 1], [4, 5], [16, 6], [5, 6]] сортировка "внешних" элементов по значению "внутренних"
[[1,0], [16,6], [2,1], [4,5],[4,0],[5,6]].sort_by {|elem| elem[0]}
#=> [[1, 0], [2, 1], [4, 0], [4, 5], [5, 6], [16, 6]]</sourcesyntaxhighlight>
Остается только добавить, что массивы упорядочиваются по возрастанию. Если вам надо по убыванию, то придётся писать собственный метод [[w:Сортировка пузырьком|сортировки пузырьком]]. Шутка! По правде же, есть много способов выстроить массив по убыванию. Пока мы будем использовать метод <code>.reverse</code>, обращающий массив.
 
Строка 126:
Для обращения массива существует метод <code>.reverse</code>. Применим его к предыдущим примерам, чтобы получить сортировку по убыванию:
 
<sourcesyntaxhighlight lang="ruby">
["у", "попа", "была", "собака"].sort.reverse #=> ["у", "собака", "попа", "была"]
["у", "попа", "была", "собака"].sort_by{ |elem| elem.size }.reverse #=> ["собака", "была", "попа", "у"]</sourcesyntaxhighlight>
 
Метод <code>.reverse</code> мы просто прицепили в конец предыдущего примера. Так можно выстроить произвольную цепочку допустимых методов; выполняться они будут по очереди, начиная с самого левого, то есть самого первого в цепочке.
Строка 136:
Для сложения массивов, строк и чисел используется метод <code>+</code>:
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4] + [5, 6, 7] + [8, 9] #=> [1, 2, 3, 4, 5, 6, 7, 8, 9]</sourcesyntaxhighlight>
 
Плюс берёт массив справа и, будто это железнодорожный состав, прицепляет его к хвосту первого массива. Это называется [[w:Конкатенация|конкатенацией]].
Строка 142:
Вычитаются массивы методом <code>-</code>, но происходит это сложнее, чем расцепление вагонов:
 
<sourcesyntaxhighlight lang="ruby">[1, 1, 2, 2, 3, 3, 3, 4, 5] - [1, 2, 4] #=> [3, 3, 3, 5]</sourcesyntaxhighlight>
 
Из первого массива удаляются все элементы, имеющиеся во втором, независимо от их количества. Остальные элементы остаются без изменений, сохраняют относительные позиции.
Строка 152:
Рассмотрим объединение множеств в действии:
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4, 5, 5, 6] | [0, 1, 2, 3, 4, 5, 7] #=> [1, 2, 3, 4, 5, 6, 0, 7]</sourcesyntaxhighlight>
 
Объединение получается вот так. Сначала массивы сцепляются:
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4, 5, 5, 6, 0, 1, 2, 3, 4, 5, 7]</sourcesyntaxhighlight>
 
Затем, начиная с первого вагона, инспектор идёт от вагона к вагону, удаляя элементы, которые уже встречались. После зачистки получается настоящее логическое объединение.
Строка 162:
На деле это выглядит так:
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4, 5, -5-, 6, 0, -1-, -2-, -3-, -4-, -5-, 7]</sourcesyntaxhighlight>
 
Зачёркнутые числа — это удаленные элементы (дубликаты). Переходим от слов к делу:
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4, 5, 5, 6] & [0, 2, 1, 3, 5, 4, 7] #=> [1, 2, 3, 4, 5]</sourcesyntaxhighlight>
 
При пересечении двух массивов, из первого удаляются все элементы, отсутствующие во втором. А из второго, отсутствующие в первом. При этом относительный порядок остающихся элементов первого массива сохраняется.
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4, 5, -5-, -6-] & [-0-, 2, 1, 3, 5, 4, -7-]</sourcesyntaxhighlight>
 
Всё просто. Важно лишь помнить, что <code>|</code> и <code>&</code> не изменяют ни первый, ни второй исходные массивы. Они через описанные процедуры создают новый массив. Чтобы тот не уехал от вас, нужно присвоить его; но не себе, а переменной, приготовленной слева от <code>=</code>.
Строка 180:
Для удаления дубликатов (повторяющихся элементов массива) в Ruby используется метод <code>.uniq</code>:
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4, 5, 5, 6, 0, 1, 2, 3, 4, 5, 7].uniq #=> [1, 2, 3, 4, 5, 6, 0, 7]</sourcesyntaxhighlight>
 
Процесс зачистки массива от дубликатов такой же, как и в объединении.
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4, 5, -5-, 6, 0, -1-, -2-, -3-, -4-, -5-, 7]</sourcesyntaxhighlight>
 
Поэтому объединение массивов можно записать как
 
<sourcesyntaxhighlight lang="ruby">(array1 + array2).uniq</sourcesyntaxhighlight>
 
Но проще, конечно, объединять палкой.
Строка 196:
Метод <code>.flatten</code> делает из многомерного массива простой, длинный одномерный массив. Он как бы расплющивает его. Например, чтобы найти в двумерном массиве наибольший элемент, мы сперва расплющим массив, а потом найдём максимум методом <code>.max</code>:
 
<sourcesyntaxhighlight lang="ruby">array = [[1, 2], [3, 4]]
array.flatten.max #=> 4</sourcesyntaxhighlight>
 
Расплющивание происходит в несколько этапов. Сначала происходит удаление всех квадратных скобок.
 
<sourcesyntaxhighlight lang="ruby">-[[- 1, 2 -]-, -[- 3, 4 -]]-</sourcesyntaxhighlight>
 
А потом, две квадратные скобки добавляются слева и справа. Но делать это надо быстро, чтобы элементы не успели разбежаться.
 
<sourcesyntaxhighlight lang="ruby">[1, 2, 3, 4]</sourcesyntaxhighlight>
 
Вот и всё! У нас они разбежаться не успели. Повторите данное упражнение на других массивах (двумерных, трёхмерных и так далее).
Строка 213:
Функцию удаления элементов <code>nil</code> массива выполняет метод <code>.compact</code> например:
 
<sourcesyntaxhighlight lang="ruby">array = [1, nil, 2, nil, 3]
array.compact #=> [1, 2, 3]</sourcesyntaxhighlight>
 
==== [[w:Транспонированная матрица|Транспонирование]] двумерного массива ====
Строка 220:
Задача: дан двумерный массив. Вывести одномерный массив с максимумами каждого из столбцов. Хм… посмотрим сперва, как эта задача решается для строчек, а не столбцов:
 
<sourcesyntaxhighlight lang="ruby">array2D = [[1, 2], [3, 4]]
array2D.map{ |array| array.max } #=> [2, 4]</sourcesyntaxhighlight>
 
Метод <code>.map </code> — это итератор, который позволяет нам делать что-нибудь с каждым объектом, на который указывает массив. Подробнее о них ниже в этой главе.
Строка 227:
Чтобы решить задачу в первоначальном варианте, нам надо лишь предварительно транспонировать массив (поменять местами строки и столбцы):
 
<sourcesyntaxhighlight lang="ruby">array2D = [[1, 2], [3, 4]]
array2D.transpose.map{ |array| array.max } #=> [3, 4]</sourcesyntaxhighlight>
 
Метод <code>.transpose</code> как раз и занимается транспонированием. Это позволяет с лёгкостью решать задачи про столбцы приёмами, схожими с задачами про строки.
Строка 236:
Речь пойдёт не о почковании, а о методе, который позволяет умножать массив на целое число. В результате такого умножения мы получим массив, состоящий из нескольких копий элементов исходного массива.
 
<sourcesyntaxhighlight lang="ruby">["много", "денег", "прячет", "тёща"] * 2
#=> ["много", "денег", "прячет", "тёща", "много", "денег", "прячет", "тёща"]</sourcesyntaxhighlight>
 
Того же самого эффекта можно добиться сцепив массив необходимое количество раз:
 
<sourcesyntaxhighlight lang="ruby">array = ["много", "денег", "прячет", "тёща"]
array + array #=> ["много", "денег", "прячет", "тёща", "много", "денег", "прячет", "тёща"]</sourcesyntaxhighlight>
 
Заметили, что есть некоторая параллель с целыми числами? Умножение можно заменить сложением и наоборот!
Строка 250:
Часто и во многих алгоритмах надо добавить элемент в конец массива:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array[array.size] = 6
array #=> [1, 2, 3, 4, 5, 6]</sourcesyntaxhighlight>
 
И если уж добавили, то надо как-то его и удалить. Делается это примерно так:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5, 6]
array[0...-1] #=> [1, 2, 3, 4, 5]</sourcesyntaxhighlight>
 
Но как всегда, эти задачи возникали слишком часто и их решили реализовать в виде методов. Методы назвали <code>.push</code> («втолкнуть» в конец массива) и <code>.pop</code> («вытолкнуть» элемент из массива):
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 3, 4, 5]
array.push(6)
Строка 267:
array << 7 #=> [1, 2, 3, 4, 5, 6, 7], другой синтаксис
array.pop #=> 7
array #=> [1, 2, 3, 4, 5, 6]</sourcesyntaxhighlight>
 
==== Функциональность очереди и списка ====
Строка 273:
Чтобы можно было использовать массив в качестве [[w:Очередь (программирование)|очереди]] и/или [[w:Линейный список|списка]], потребуется сделать всего лишь пару методов. Первый из них добавляет элемент в начало массива, а второй удаляет элемент из начала. Давайте посмотрим, как это делается универсальными методами <code>[]</code>, <code>[]=</code> и <code>+</code>:
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 3, 4, 5]
 
Строка 289:
# удалим элемент из начала массива
array = array[1..-1]
array #=> [1, 2, 3, 4, 5]</sourcesyntaxhighlight>
 
Теперь посмотрим, какие методы реализуют точно такую же функциональность:
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 3, 4, 5]
 
Строка 301:
# удаляем из начала массива
array.shift #=> 6
array #=> [1, 2, 3, 4, 5]</sourcesyntaxhighlight>
 
Удаляющий метод — <code>.shift</code> («сдвинуть»), а метод, добавляющий элемент в начало массива, называется <code>.unshift</code> (непереводимое слово, означающее нечто противоположное «сдвинуть обратно»).
Строка 309:
Мнемограмма для методов <code>.shift</code>, <code>.unshift</code>, <code>.pop</code> и <code>.push</code>:
 
<sourcesyntaxhighlight lang="ruby">.unshift(0) .push(6)
[1, 2, 3, 4, 5]
.shift .pop</sourcesyntaxhighlight>
 
Методы с параметром (сверху) добавляют элемент в массив, а методы без параметра (снизу) — удаляют. По желанию можно дорисовать стрелочки.
Строка 322:
''Замечание.'' Имена методов <code>unshift</code>/<code>shift</code> неинтуитивны. Во-первых, они не напоминают о том, что работа идёт с головой массива, а не с хвостом, во-вторых ничего не говорят о том, идёт заполнение или опустошение стека. Можно создать для этих методов псевдонимы с говорящими именами, например, <code>feed</code>/<code>spit</code> (кормить/выплевывать):<br/>
 
<sourcesyntaxhighlight lang="ruby">class Array
alias feed :unshift
alias spit :shift
end</sourcesyntaxhighlight>
 
==== Создание своих классов, работающих как массивы ====
Строка 341:
По японской традиции, имена логических методов принято заканчивать <code>?</code> (вопросительным знаком). Это позволяет также получить список логических методов, вызываемых в данном случае: просто отобрать из всех имеющихся методов те, что кончаются на <code>?</code>. Делается это при помощи небольшой вспомогательной программы:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 2, 3]
puts array.methods.grep(/\?$/)</sourcesyntaxhighlight>
 
Для удобства, можно упорядочить полученный список:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 2, 3]
puts array.methods.grep(/\?$/).sort</sourcesyntaxhighlight>
 
==== Есть ли элемент в массиве? ====
Строка 353:
Как узнать, есть ли некоторый элемент в массиве? Попробуем решить эту задачу при помощи метода <code>.size</code> и итератора <code>.find_all</code>:
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 3, 4, 5, 6, 7]
required = 5 # число, которое мы будем искать
array.find_all{ |elem| elem == required }.size != 0 #=> true
# это значит, что такое число есть</sourcesyntaxhighlight>
 
Использование связки из трёх методов (<code>!=</code>, <code>.find_all</code> и <code>.size</code>) для такой задачи — возмутительно! Разработчики не могли с этим долго мириться и реализовали метод специально для этой задачи. Имя ему — <code>.include?</code>. Перепишем нашу задачу, но на этот раз будем использовать правильный метод:
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 3, 4, 5, 6, 7]
required = 5 # число, которое мы будем искать
array.include?(required) #=> true
# что бы это значило?</sourcesyntaxhighlight>
 
Мутный горизонт скрывает берег,<br/>
Строка 372:
Методом логическим <code>.include?</code>.
 
<sourcesyntaxhighlight lang="ruby">lake = ["правый берег", "ветер", "вода", "вода", "вода", "окунь", "вода", "вода", "левый берег"]
lake.include?("акула") #=> false</sourcesyntaxhighlight>
 
Опытным путём мы доказали, что акулы в озере не водятся.
Строка 381:
Если вы хотите задать массиву вопрос «пуст ли ты?», но боитесь обидеть, то можете пойти окружным путём. Например, спросить у него: ты равен пустому массиву?
 
<sourcesyntaxhighlight lang="ruby">empty_array = []
filled_array = [1, 2, 2, 3]
 
empty_array == [] #=> true
filled_array == [] #=> false</sourcesyntaxhighlight>
 
Ещё можно задать вопрос: твой размер равен нулю?
 
<sourcesyntaxhighlight lang="ruby">empty_array = []
filled_array = [1, 2, 2, 3]
 
empty_array.size == 0 #=> true
filled_array.size == 0 #=> false</sourcesyntaxhighlight>
 
Но наш вам совет: не стоит искать обходных путей. Спросите его напрямую: <code>.empty?</code> («пуст?»):
 
<sourcesyntaxhighlight lang="ruby">empty_array = []
filled_array = [1, 2, 2, 3]
 
empty_array.empty? #=> true
filled_array.empty? #=> false</sourcesyntaxhighlight>
 
==== И наоборот ====
Строка 407:
В Ruby принято избегать отрицания условия. Например, если вам нужно сделать что-то, если массив ''не пуст'', можно воспользоваться методом, обратным <code>empty?</code>. Этот метод называется <code>any?</code>.
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 4]
array.length > 0 #=> true
array.empty? #=> false
array.any? #=> true</sourcesyntaxhighlight>
 
=== Итераторы ===
Строка 428:
Изменить все элементы массива можно по-всякому. Начнём с обнуления:
 
<sourcesyntaxhighlight lang="ruby">array = ["шифровка", "Штирлица", "в", "Центр", "секретно"]
array.map{ 0 } #=> [0, 0, 0, 0, 0]</sourcesyntaxhighlight>
 
В приведенном примере каждый элемент массива будет заменён нулем независимо от того, чем является этот элемент. Например, при попытке обнулить таким образом двумерный массив <code>[[1, 2], [3, 4]]</code>, в результате получим <code>[0, 0]</code>.
Строка 437:
Можно дать элементу <code>.map</code> иное задание. Для этого зажимаем в фигурные скобы замыкания иной код:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array.map{ |elem| elem ** 2 } #=> [1, 4, 9, 16, 25]</sourcesyntaxhighlight>
 
Прежде, чем замыканию выдать квадрат очередного элемента, ему нужно знать этот элемент. Итератор <code>.map</code> даёт ему значение элемента, словно фотографию, обрамлённую слева и справа вертикальными чертами <code>|</code>. Чтобы замыкание смогло взять эту фотографию, обязательно нужно дать ей имя. В нашем случае это <code>elem</code>, но подходят и такие названия:
Строка 456:
То имя, что показывается в раздвижных дверях, — это не сам элемент, это лишь его копия. Фотография. Голограмма. Это даже не другая переменная, это не переменная вообще. Бессмысленно присваивать новое значение фотографии:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array.map{ |elem| elem = elem**2 }
# присваивание не имеет смысла: elem несёт лишь значение элемента, не являясь им</sourcesyntaxhighlight>
 
Из итератора <code>.map</code> выезжает другой поезд, у которого вместо соответствующего элемента первого поезда сидит результат вычисления замыкания. Используйте этот массив как-нибудь, иначе поезд уедет.
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array.map{ |elem| elem**2 } #=> [1, 4, 9, 16, 25]
array #=> [1, 2, 3, 4, 5] — неизменный первый поезд</sourcesyntaxhighlight>
 
Можно присвоить его новой переменной <code>array_of_squares</code>. А можно заместить им существующую переменную <code>array</code>:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array = array.map{ |elem| elem**2 } #=> [1, 4, 9, 16, 25]
array #=> [1, 4, 9, 16, 25]</sourcesyntaxhighlight>
 
Это общее явление Ruby: методы (здесь — итераторы) не меняют объект (массив), с которым работают. Они лишь выдают результат, который потом можно использовать как аргумент или присвоить переменной.
Строка 483:
Для того, чтобы сохранить результат выполнения метода в исходную переменную, нужно добавить к названию метода восклицательный знак.
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array.map!{ |elem| elem**2 } #=> [1, 4, 9, 16, 25]
array #=> [1, 4, 9, 16, 25]</sourcesyntaxhighlight>
 
Такой приём работает и с многими другими методами в языке, которые только возвращают результат своего выполнения. Однако при использовании цепочек методов каждый должен быть с восклицательным знаком, иначе разорвётся цепочка копирований.
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 3, 4, 5, nil]
array.compact.map!{ |elem| elem**2 } #=> [1, 4, 9, 16, 25]
array #=> [1, 2, 3, 4, 5, nil]
array.compact!.map!{ |elem| elem**2 } #=> [1, 4, 9, 16, 25]
array #=> [1, 4, 9, 16, 25]</sourcesyntaxhighlight>
 
В первом случае операция .compact создала копию массива, тогда как .compact! заменила первоначальные значения результатами, полученными от .map!
Строка 502:
Вот как итератор <code>.find_all</code> выберет из массива все чётные элементы:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array.find_all{ |elem| elem % 2 == 0 } #=> [2, 4]</sourcesyntaxhighlight>
 
<code>elem % 2 == 0</code> — это вопрос «чётен ли <code>elem</code>?». Ответом, как всегда, будет <code>true</code> или <code>false</code>. Ведь «чётность» — это равенство нулю (<code>== 0</code>) остатка деления (<code>%</code>) на <code>2</code>.
Строка 509:
Кстати, равенство нулю можно проверять и при помощи метода <code>.zero?</code>. А чётность тоже можно проверить разными способами:
 
<sourcesyntaxhighlight lang="ruby">(elem % 2).zero?
(elem & 1).zero?
(elem[0]).zero? # Этот вариант круче всех</sourcesyntaxhighlight>
 
Если на вопрос, заданный в замыкании, ответ <code>true</code>, то <code>|elem|</code> (значение очередного элемента исходного массива), заносится в новый массив, который в итоге будет выводом из итератора <code>.find_all</code>.
Строка 519:
Рассмотрим феномен, который мы наблюдали уже в итераторе <code>.map</code>:
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 3, 4, 5]
array.find_all{ |elem| elem[0].zero? } #=> [2, 4]
array #=> [1, 2, 3, 4, 5]</sourcesyntaxhighlight>
 
{{info|Итератор <code>.find_all</code> лишь создаёт новый массив на базе старого. Он не изменяет исходный массив. Чтобы сохранить результат работы <code>.find_all</code>, надо его присвоить какой либо переменной.}}
Строка 528:
Вот как будет выглядеть программа без феномена:
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 3, 4, 5]
array = array.find_all{ |elem| elem[0].zero? } #=> [2, 4]
array #=> [2, 4]</sourcesyntaxhighlight>-->
 
Если нужно элементы<br/>
Строка 540:
Также есть методы <code>.odd?</code> и <code>.even?</code> для более наглядной реализации таких вещей, нечетный и четный соответственно.
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array.find_all{ |elem| elem.even? } #=> [2, 4]</sourcesyntaxhighlight>
 
==== Суммирование/произведение/агрегация элементов ====
Строка 547:
Очень часто возникает задача найти сумму/произведение всех элементов массива. Для этих целей традиционно используется итератор <code>.inject</code>. Для демонстрации его работы, давайте найдем сумму элементов массива:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array.inject(0){ |result, elem| result + elem } #=> 15</sourcesyntaxhighlight>
 
Рассмотрим все по порядку. Начнём с нуля. Его следует расшифровывать как <code>result = 0</code> перед началом работы итератора, то есть это начальное значение переменной <code>result</code> (переменной промежуточного результата).
Строка 562:
Учитывая эти два замечания, напишем код, который является неправильным:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array.inject(0){ |result, elem| result = result + elem } #=> 15</sourcesyntaxhighlight>
 
Имена переменных <code>result</code> и <code>elem</code> созданы богатым воображением автора. В ваших программах они могут называться иначе.
Строка 571:
Для полноты картины решим ещё одну задачку. На этот раз будем искать произведение всех элементов массива:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5]
array.inject(1){ |result, elem| result * elem } #=> 120</sourcesyntaxhighlight>
 
А вот так можем вычислить факториал для заданного числа (n!):
 
<sourcesyntaxhighlight lang="ruby">n = 9
(1..n).to_a.inject(){ |one, two| one * two } #=> 362880</sourcesyntaxhighlight>
 
Чтобы закрепить материал, решите задачу: найти произведение всех положительных элементов массива. Подсказка: используйте метод <code>.find_all</code>.
Строка 590:
Итератор <code>.partition</code> делит массив на две части по некоторому бинарному признаку (чётности, положительности, наличию высшего образования и тому подобным). Вот как разделить массив на две части по признаку кратности трём:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
array.partition{ |x| (x % 3).zero? } #=> [[3, 6, 9], [1, 2, 4, 5, 7, 8]]</sourcesyntaxhighlight>
 
В результате работы итератора получился массив, состоящий из двух элементов-массивов. Первый элемент-массив содержит все элементы, которые удовлетворяют условию, а второй, которые не удовлетворяют. Обратите внимание, как проверяется кратность трём. Ничего не напоминает? Например, итератор <code>.find_all</code>? Нет? Ну и ладно.
Строка 597:
Есть интересная хитрость, позволяющая разместить массив, полученный <code>.partition</code>, в две разные переменные:
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
one, two = array.partition{ |x| (x % 3).zero? }
one #=> [3, 6, 9]
two #=> [1, 2, 4, 5, 7, 8]</sourcesyntaxhighlight>
 
Этот метод называется ''множественным присваиванием'' (multiple assignment) и широко используется в ситуациях, когда из метода надо вернуть более одного значения. Но об этом позднее.
Строка 617:
В [[w:Математическая логика|математической логике]] такой «итератор» называется [[w:Квантор|квантором]] общности, обозначается символом <math>\forall</math>. На языке Ruby он называется <code>.all?</code>. По сложившейся традиции, давайте посмотрим, как решалась бы эта задача до версии 1.8, то есть до появления логических итераторов:
 
<sourcesyntaxhighlight lang="ruby">
array = [1, 2, 2, 3]
array.inject(true){ |result, elem| result && (elem > 2) } #=> false</sourcesyntaxhighlight>
 
В примере используется так называемый механизм презумпции виновности. Переменной <code>result</code> присваивается значение <code>true</code>. Логическое умножение изменяет значение переменной <code>result</code> на <code>false</code>.
Строка 627:
Ещё один вариант решения той же задачи:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 2, 3]
array.find_all{ |elem| elem <= 2 }.size.zero? #=> false</sourcesyntaxhighlight>
 
Давайте решим эту же задачу при помощи новоявленного логического итератора:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 2, 3]
array.all?{ |elem| elem > 2 } #=> false</sourcesyntaxhighlight>
 
Несмотря на то, что код получился короче, результат остался прежним: утверждение, что все элементы массива больше двух, ложно.
Строка 641:
Вслед за квантором общности (он же — логический итератор <code>.all?</code>), из математической логики был перенесён и квантор существования — <math>\exists</math>. На языке Ruby он называется <code>.any?</code>. Чтобы оценить его по достоинству, посмотрим решение задачи без его участия. Проверим, содержит ли <code>array</code> хотя бы один элемент больший двух:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 2, 3]
array.inject(false){ |result, elem| result || (elem > 2) } #=> true</sourcesyntaxhighlight>
 
В данном примере используется так называемый механизм презумпции невиновности. Переменной <code>result</code> присваивается значение <code>false</code>. В результате логического сложения, происходит изменение значения переменной <code>result</code> на <code>true</code>.
Строка 648:
Теперь тоже самое, но через логический итератор <code>.any?</code>:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 2, 3]
array.any?{ |elem| elem > 2 } #=> true</sourcesyntaxhighlight>
 
Естественно, что с появлением логических итераторов, реализация задач математической логики (в рамках языка Ruby) стала удобней.
Строка 657:
Вот так можно сгенерировать «хороший пароль» — произвольную последовательность из чисел или латинских букв, общей длиной в 8 символов.
 
<sourcesyntaxhighlight lang="ruby">symbols = ["a".."z", "A".."Z", "0".."9"].map{ |range| range.to_a }.flatten
puts (0...8).map{ symbols.sample }.join</sourcesyntaxhighlight>
 
Метод sample возвращает случайный элемент из массива.
Строка 664:
Перемешать упорядоченный массив:
 
<sourcesyntaxhighlight lang="ruby">array = [1, 2, 3, 4, 5, 6, 7]
array.sort_by{ rand } #=> перемешанный массив</sourcesyntaxhighlight>
 
Выстроить элементы массива по убыванию без использования <code>.reverse</code>:
 
<sourcesyntaxhighlight lang="ruby">array = [2, 1, 3, 5, 6, 7, 4]
array.sort{ |x, y| y <=> x } #=> [7, 6, 5, 4, 3, 2, 1]</sourcesyntaxhighlight>
 
=== Задачи про массивы ===