Pull to refresh

Покорим Ruby вместе! Капля пятая

Reading time 4 min
Views 35K
Продолжаем (1, 2, 3, 4) штудировать Руби. На этот раз дело коснётся массивов. Но начнем с диапазонов.

Диапазоны значений


Иногда полезно иметь возможность сохранить «концепт» простого списка, причём хочется, чтобы объявить его мы могли бы максимально просто, например: список из букв от 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] - повторение


Эпилог


Капля получилась относительно «жёсткая» — да, все это нужно знать :( Будем привыкать ;) Если что непонятно, то вам дорога в комментарии — там помогут. Там же ждем отзывов и замечаний!

Дальше мы вернемся к типам данных, углубимся в работу со строками и числами. На этот раз спасибы за примеры отправляются Халу Фалтону.

Tags:
Hubs:
+42
Comments 47
Comments Comments 47

Articles