Ранее мы уже говорили о "простейших" объектах Ruby - числах и строках. К их числу относятся и массивы - экземпляры класса Array. Массив есть просто набор (коллекция) элементов, в котором доступ к каждому элементу возможен по его номеру (целому числу).
Массив может состоять из элементов, принадлежащих различным классам. Обратите внимание на то, что нумерация элементов массива начинается с нуля.
Чтобы создать экземпляр класса Array, его элементы, разделенные запятыми, заключают в квадратные скобки, например, [1, 2, 3].
Для задания массива строк можно использовать более удобную форму с использованием выражения %w: запись %w(раз два три) эквивалентна записи ["раз", "два", "три"]. Между символом w и открывающей скобкой не должно быть пробела. Если несколько слов должны стать одним элементом массива, то для их разделения перед пробелом добавляется символ \ (backslash):
%w(раз\ два три\ четыре) # ["раз два", "три четыре"]
Иногда требуется только создать массив, но не заполнять его элементами. В этом случае используется одна из следующих конструкций:
a = [ ] b = Array.new
Если требуется создать "пустой" массив заданного размера, то он указывается в качестве аргумента метода new, например,
myArray = Array.new(10) # когда у функции только один аргумент, # скобки можно опустить myArray = Array.new 10
Необязательный второй аргумент метода new задает класс, экземпляры которого предполагается размещать в массиве:
timeArray = Array.new(3, Time) p timeArray #[Time, Time, Time]
Одним из несомненных достоинств Ruby является наличие в нем большого числа методов для работы с массивами. В таблице ниже перечислены наиболее употребимые из них, иллюстрирующие возможность работы с массивом как с множеством, стеком и иными структурами данных.
Назначение и пример использования метода | Результат | |
---|---|---|
[ ] at |
Получение элемента с указанным индексом; если аргумент является отрицательным числом, то индекс отсчитывается с конца a = ["a", "b", "c", "d"]; a[0]; a.at(0); a[0 .. 2] a[-2]; a.at(-2); a[2, 2] |
"a"; ["a", "b", "c"] "c"; ["c", "d"] |
+ | Добавление одного массива к другому [1, 2, 3] + [4, 5] |
[1, 2, 3, 4, 5] |
* | Повторение [1, 2] * 2 |
[1, 2, 1, 2] |
| | Объединение множеств ["a", "b", "c"] | ["c", "d", "a"] |
["a", "b", "c", "d"] |
& | Пересечение множеств [1, 1, 3, 5] & [1, 2, 3] |
[1, 3] |
- | Разность множеств [1, 2, 2, 3, 3, 3, 4, 5] - [1, 2, 4] |
[3, 5] |
<< push |
Добавление в конец массива [1, 2] << "c" << [3, 4] a = ["a", "b"]; a.push("c", "d") |
[1, 2, "c", [3, 4]] ["a", "b", "c", "d"] |
unshift | Добавление элемента в начало массива со сдвигом остальных a = ["b", "c"]; a.unshift("a") |
["a", "b", "c"] |
clear | Удаление всех элементов массива a = ["a", "b", "c", "d"]; a.clear |
[] |
collect | Выполнение блока операторов, заключенных в фигурные скобки, по разу для каждого элемента массива a = ["a", "b", "c", "d"] a.collect {|i| i+"!"} |
["a!", "b!", "c!", "d!"] |
compact | Удаление всех объектов nil из массива ["a", "b", nil, "c", nil].compact |
["a", "b", "c"] |
empty? | Проверка на отсутствие элементов массива [].empty? |
true |
length size |
Определение количества элементов массива [ 1, 2, 3, 4, 5 ].length |
5 |
pop | Удаление последнего элемента массива a = [ "a", "m", "z" ]; a.pop a |
"z" ["a", "m"] |
each | Вызов блока операторов по одному разу для каждого элемента массива a = ["a", "b", "c"] a.each {|x| print x, "--" } |
a--b--c-- |
flatten | "Разглаживание" массива - получение одномерного массива из массива массивов s = [ 1, 2, 3 ] t = [ 4, 5, 6, [7, 8] ] a = [ s, t, 9, 10 ]; a.flatten |
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
index | Получение номера первого вхождения аргумента (nil если не найден) a = [ "a", "b", "c" ]; a.index("b") a.index("z") |
1 nil |
join | Создание единой строки из массива строк [ "a", "b", "c" ].join [ "a", "b", "c" ].join("-") |
"abc" "a-b-c" |
nitems | Определение количества элементов массива, отличных от nil [ 1, nil, 3, nil, 5 ].nitems |
3 |
reverse | Инвертирование массива [ "a", "b", "c" ].reverse [ 1 ].reverse |
["c", "b", "a"] [1] |
sort | Сортировка массива a = ["d", "a", "c"] a.sort a.sort {|x,y| y <=> x } |
["a", "c", "d"] ["d", "c", "a"] |
Будьте внимательны при сортировке строк, содержащих буквы русского алфавита. Упорядочивание строк происходит по ASCII-кодам их символов, а таблица кодировки koi8-r, принятая в ОС Linux, размещает русские буквы не в алфавитном порядке.
a2 = %w(собака волк лошадь альбатрос) puts a2.sort # альбатрос лошадь собака волк
Пример
Создайте файл, в который поместите следующую программу.
a = [1, "cat", 3.14] # создание массива из трех элементов puts a puts a[0] # печать первого элемента массива puts a.type print a[0].type, "\t", a[1].type, "\t", a[2].type, "\n" a[2] = "dog" # изменение значения третьего элемента puts a print a[0].type, "\t", a[1].type, "\t", a[2].type, "\n"
Проследите за изменениями типа элементов массива a.
Будьте внимательны при присваивании значений переменным, если они есть ссылки на массивы. Рассмотрим следующий пример, комментируя действия с переменными в терминах "наклеек":
# "упаковываем" массив Ruby puts "\n\t Массивы " magic = [1, 2] # обе метки наклеены на одну и ту же упаковку hocusPocus = magic # их значения совпадают print "magic = "; p magic print "hocusPocus = "; p hocusPocus puts "изменяем содержимое массива hocusPocus" # изменим содержимое упаковки с именем hocusPocus hocusPocus[0] = 6 print "magic = "; p magic # посмотрим... print "hocusPocus = "; p hocusPocus puts "Обе переменные изменились"
Так же, как массивы, ведут себя в языке Ruby почти все объекты. Исключением являются числа (Fixnum), логические величины true и false, а также специальная величина nil (ноль, ничего), используемая для ссылки "в никуда". Когда вы присваиваете значение переменной, указывающей на объект одного из вышеперечисленных типов, другой переменной, эти две переменные начинают ссылаться на два различных объекта, имеющих одинаковое значение, а не на один и тот же объект. Например,
puts"===============" puts "\n \t Числа " # "упаковали" объект типа FixNum magic = 42 # теперь имеем две упаковки, каждая со своей меткой hocusPocus = magic # обе упаковки имеют одинаковое содержимое puts "magic = #{magic}" puts "hocusPocus = #{hocusPocus}" puts "изменяем содержимое hocusPocus" # изменим содержимое упаковки с именем hocusPocus hocusPocus = 0 # посмотрим... puts "magic = #{magic}" # и заметим, что они различны puts "hocusPocus = #{hocusPocus}" puts "Вторая переменная не изменилась"
Значения, присваиваемые переменным, могут являться выражениями или массивами. Если число элементов в правой части больше числа переменных в левой, то лишние значения игнорируются. Если число переменных слева превышает число значений справа, то оставшиеся переменные принимают значение nil. Символ * перед именем последней переменной в списке указывает, что она является массивом, в который помещаются оставшиеся значения.
a, b = [1, 2] # a = 1; b = 2 a, b = 1, 2 # a = 1; b = 2 a, b, c = 1, 2 # a = 1; b = 2; c = nil a, b = 1, 2, 3 # a = 1; b = 2 a, *b = 1, 2, 3 # a = 1; b = [2, 3]
В Ruby, как и в большинстве других современных языков, нет многомерных массивов, но они легко моделируются созданием массива массивов, т. е. массива, элементами которого являются также массивы. Как следствие, такие массивы не обязаны быть прямоугольными (т. е. число элементов в разных строках может отличаться), что позволяет значительно экономить память. Например, двумерный массив размером 3x3 можно задать так
a = [ [11, 12, 13], [21, 22, 23], [31, 32, 33] ]
Аналогично можно создать массив любой требуемой размерности, хотя массивы размерности, большей чем два, встречаются достаточно редко. К элементам таких массивов можно добраться, последовательно указывая индексы требуемых элементов:
p a[1] # [21, 22, 23], p a[1][1] # 22
Если требуется только создать многомерный массив, не заполняя его данными, то следует использовать следующую конструкцию:
# Пустой массив: # создали три разных массива и поместили их в четвертый a = Array.new(3) a[0] = Array.new(3) a[1] = Array.new(3) a[2] = Array.new(3) a[1][1] = 123 p a # [[nil, nil, nil], [nil, 123, nil], [nil, nil, nil]]
Метод map, который выполняет блок операторов, следующий за ним, для каждого элемента объекта, к которому он применяется, позволяет более компактно реализовать рассмотренную выше конструкцию:
a = (0..2).map{ Array.new(3)} # ПРАВИЛЬНО a[1][1] = 123 p a # [[nil, nil, nil], [nil, 123, nil], [nil, nil, nil]]
Попытка же создать такой массив, аналогично одномерному, приводит к неожиданным на первый взгляд результатам:
b = Array.new(3, Array.new(3)) # НЕПРАВИЛЬНО # создали массив, в который поместили # три ссылки на один и тот же объект b[1][1] = 123 p b # [[nil, 123, nil], [nil, 123, nil], [nil, 123, nil]]
Пример
Рассмотрим создание массива размера 3x3, который должен быть заполнен по следующей схеме: в первой строке массива - текущие год, номер месяца и дня месяца, во второй - название месяца, день и наименование дня недели, в третьей строке - текущие час, минута и секунда.
myArray = (0..2).map{ Array.new 3 } mouth = %w(январь февраль март апрель май июнь июль август сентябрь октябрь ноябрь декабрь) dayOfWeek = %w(воскресенье понедельник вторник среда четверг пятница суббота) t = Time.now myArray[0][0] = t.year myArray[0][1] = t.month myArray[0][2] = t.day myArray[1][0] = mouth[t.month] myArray[1][1] = t.day myArray[1][2] = dayOfWeek[t.wday] myArray[2][0] = t.hour myArray[2][1] = t.min myArray[2][2] = t.sec print "Сегодня #{myArray[0][2]}.", "#{myArray[0][1]}.#{myArray[0][0]}\n" print "Число: #{myArray[1][1]}, ", "месяц: #{myArray[1][0]},", " день недели: #{myArray[1][2]}\n" print "Сейчас #{myArray[2][0]} час, ", "#{myArray[2][1]} мин,", " #{myArray[2][2]}сек\n"
Пример
Зачастую в задачах требуется подсчитать некоторую характеристику массива, последовательно обрабатывая все его элементы. В таких ситуациях удобно использовать встроенный метод each. Продемонстрируем его использование на задаче нахождения суммы всех элементов массива.
b = [23, -14, 45, 78, -2.5] s = 0 b.each {|i| s +=i} puts s
Задания
- Напишите программу, печатающую среднее арифметическое первого и последнего элементов массива.
- Напишите программу, находящую пересечение и объединение двух множеств A={1,2,3} и B={2,4,5}, используя методы работы с массивами.