Продолжаем (1, 2, 3, 4) штудировать Руби. На этот раз дело коснётся массивов. Но начнем с диапазонов.
Иногда полезно иметь возможность сохранить «концепт» простого списка, причём хочется, чтобы объявить его мы могли бы максимально просто, например: список из букв от A до Z, или числа от 1 до 25. С помощью диапазонов это возможно, они в Руби максимально интуитивно понятны. Вот простые числовые диапазоны:
Оператор
Простые переменные порой не годятся для реального программирования. Каждый современный ЯП, в т.ч. и Руби, поддерживает более сложные формы структурированных данных. В Руби вы можете представлять упорядоченные наборы объектов с помощью массивов.
Массивы в Руби динамические: можно (но не обязательно) указывать размер массива при создании. После этого он может расти без участия со стороны программиста, поэтому в практике редко приходится даже узнавать его размер. Массивы в Руби гетерогенные: они могут хранить данные самых разных типов данных (если быть точным, то массив хранит только ссылки на объекты). Три одинаковых массива:
Для создания массивов используется специальный метод класса
Частая ошибка начинающих — считаем, что элементы независимы друг от друга. Однако, как было сказано раньше, в массиве лишь ссылки на один объект. Чтобы избежать такого поведения используем блок (мы его уже встречали во второй капле — это код между
Обращение к элементам и установление их значений выполняются с помощью методов
Другой способ — толкание (pushing) данных. Метод
Методы
Метод
Самый простой метод для сортировки массива — это
Как это работает?
Для выборки элементов из массива по критериям используем
Метод
В классе массивов определен итератор
Можно создать копию массива и изменить её налету с помощью итератора
Объединяем массивы:
Капля получилась относительно «жёсткая» — да, все это нужно знать :( Будем привыкать ;) Если что непонятно, то вам дорога в комментарии — там помогут. Там же ждем отзывов и замечаний!
Дальше мы вернемся к типам данных, углубимся в работу со строками и числами. На этот раз спасибы за примеры отправляются Халу Фалтону.
Диапазоны значений
Иногда полезно иметь возможность сохранить «концепт» простого списка, причём хочется, чтобы объявить его мы могли бы максимально просто, например: список из букв от A до Z, или числа от 1 до 25. С помощью диапазонов это возможно, они в Руби максимально интуитивно понятны. Вот простые числовые диапазоны:
digits = 0..9
scale1 = 0..10
scale2 = 0...10 #digits = scale2
Оператор
..
включает конечное значение, а ...
— нет (глупо — казалось, что должно быть наоборот ). Однако сами по себе диапазоны мало используются.Массивы
Простые переменные порой не годятся для реального программирования. Каждый современный ЯП, в т.ч. и Руби, поддерживает более сложные формы структурированных данных. В Руби вы можете представлять упорядоченные наборы объектов с помощью массивов.
Массивы в Руби динамические: можно (но не обязательно) указывать размер массива при создании. После этого он может расти без участия со стороны программиста, поэтому в практике редко приходится даже узнавать его размер. Массивы в Руби гетерогенные: они могут хранить данные самых разных типов данных (если быть точным, то массив хранит только ссылки на объекты). Три одинаковых массива:
a = Array.[](1,2,3,4)
b = Array[1,2,3,4]
c = [1,2,3,4]
Для создания массивов используется специальный метод класса
[]
. Также есть метод new
, который берет 0, 1 или два параметра: первый — количество элементов, второй — их значение. Смотрим пример:d = Array.new # Создаем пустой массив
e = Array.new(3) # [nil, nil, nil]
f = Array.new(3, "ruby") # ["ruby", "ruby", "ruby"]
Частая ошибка начинающих — считаем, что элементы независимы друг от друга. Однако, как было сказано раньше, в массиве лишь ссылки на один объект. Чтобы избежать такого поведения используем блок (мы его уже встречали во второй капле — это код между
{}
):f[0].capitalize! # f теперь: ["Ruby", "Ruby", "Ruby"]
g = Array.new(3) { "ruby" } # ["ruby", "ruby", "ruby"]
g[0].capitalize! # g теперь: ["Ruby", "ruby", "ruby"]
Обращение к элементам и установление их значений выполняются с помощью методов
[]
и []=
соответственно. Каждый может принимать один или два (начало и длина) параметра или диапазон. Обратный отсчет с конца массива начинается с -1
. Считывать значения можно и специальным простым методом at
— он принимает только один параметр и поэтому работает немного быстрее, delete_at
удалит элемент. Смотрим:a = [1, 2, 3, 4, 5, 6]
b = a[0] # 1
c = a.at(0) # 1
d = a[-2] # 5
e = a.at(-2) # 5
f = a[9] # nil
g = a.at(9) # nil
h = a[3,3] # [4, 5, 6]
i = a[2..4] # [3, 4, 5]
j = a[2...4] # [3, 4]
a[1] = 8 # [1, 8, 3, 4, 5, 6]
a[1,3] = [10, 20, 30] # [1, 10, 20, 30, 5, 6]
a[0..3] = [2, 4, 6, 8] # [2, 4, 6, 8, 5, 6]
a[-1] = 12 # [2, 4, 6, 8, 5, 12]
a.delete_at(3) # [1, 2, 4, 5, 6]
a.delete_at(9) # nil
Другой способ — толкание (pushing) данных. Метод
join
«сливает» элементы массива в одну переменную, в качестве параметра указываем разделитель:x = []
x << "Word"
x << "Play" << "Fun"
puts x.join(', ') # Word, Play, Fun
Методы
first
и last
возвращают первый и последний элемент массива соответственно, а values_at
— массив, содержащий только выбранные элементы:x = ["alpha", "beta", "gamma", "delta", "epsilon"]
a = x.first # alpha
b = x.values_at(0..2,4) # [alpha, beta, gamma, epsilon]
Метод
length
и его алиас size
возвратят количество элементов в массиве, а nitems
не будет считать пустые элементы (nil
), compact
уберёт nil
из массива вообще:y = [1, 2, nil, nil, 3, 4]
c = y.size # 6
e = y.nitems # 4
d = y.compact # [1, 2, 3, 4]
Самый простой метод для сортировки массива — это
sort
(попробуйте сами). Сортировка работает только в массивах, содержащих элементы, которые поддаются сравнению — с массивами со смешанными типами данных метод возвратит ошибку. Отсортируем смешанный массив, налету преобразовывая его элементы в строки (преобразовываем методом to_s
):a = [1, 2, "three", "four", 5, 6]
b = a.sort {|x,y| x.to_s <=> y.to_s}
# b теперь [1, 2, 5, 6, "four", "three"]
Как это работает?
<=>
— один из методов сравнения (см. третью каплю). Блок возвращает -1
, если первый их сравниваемой пары элемент меньше (тогда метод меняет элементы местами), 0
если элементы равны, 1
— если больше (в последних двух случаях оставляем элементы на местах). Поэтому для убывающего порядка надо просто поменять местами сравниваемые элементы ({|x,y| y.to_s <=> x.to_s}
). Думаю, что мы всё поняли.Для выборки элементов из массива по критериям используем
detect
(find
— его синоним) и find_all
(select
— то же самое), выражение критерия засовываем в блок:x = [5, 8, 12, 9, 4, 30]
# find покажет нам только первый элемент, кратный шести
x.find {|e| e % 6 == 0 } # 12
# А select покажет все подходящие элементы
x.select {|e| e % 6 == 0} # [12, 30]
Метод
reject
обратен select
— он удалит каждый элемент, удовлетворяющий блоку:x = [5, 8, 12, 9, 4, 30]
d = c.reject {|e| e % 2 == 0} # [5, 9]
delete
удалит все элементы, содержащие определенные данные:c = ["alpha", "beta", "gamma", "delta"]
c.delete("delta")
# Теперь с = ["alpha", "beta", "gamma"]
В классе массивов определен итератор
each
— работает он просто, догадайтесь, что делает этот код:[1, "test", 2, 3, 4].each { |element| puts element.to_s + "X" }
Можно создать копию массива и изменить её налету с помощью итератора
collect
:[1, 2, 3, 4].collect { |element| element * 2 } #[2, 4, 6, 8]
Объединяем массивы:
x = [1, 2, 3]
y = ["a", "b", "c"]
z = x + y #[1, 2, 3, "a", "b", "c"]
a = [1, 2, 3, 4]
b = [3, 4, 5, 6]
a — b # [1, 2] — разность массивов
a & b # [3, 4] — пересечение массивов
a | b # [1, 2, 3, 4, 5, 6] — объединение с удалением дупликатов
a*2 # [1, 2, 3, 4, 1, 2, 3, 4] - повторение
Эпилог
Капля получилась относительно «жёсткая» — да, все это нужно знать :( Будем привыкать ;) Если что непонятно, то вам дорога в комментарии — там помогут. Там же ждем отзывов и замечаний!
Дальше мы вернемся к типам данных, углубимся в работу со строками и числами. На этот раз спасибы за примеры отправляются Халу Фалтону.