Предыдущий раздел Уровень выше Следующий раздел

Массивы

Ранее мы уже говорили о "простейших" объектах 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

(Загрузить файл с примером)

Задания
  1. Напишите программу, печатающую среднее арифметическое первого и последнего элементов массива.
  2. Напишите программу, находящую пересечение и объединение двух множеств A={1,2,3} и B={2,4,5}, используя методы работы с массивами.

Предыдущий раздел Уровень выше Следующий раздел