Статьи

Руководство по коллекциям Ruby IV: советы и хитрости

collections_tips

Это последняя статья в Руководстве по коллекциям Ruby. Я хочу закончить, предоставив несколько разных советов, которые, надеюсь, окажутся полезными во время ваших приключений с Ruby.

Более подробное создание массива

Во введении к массивам я показал вам, что вы можете заполнить массив значением по умолчанию, передав второй аргумент в Array.new

 >> Array.new(3, 1)
=> [1,1,1]

Так что, если бы мы хотели инициализировать массив со случайными числами, мы могли бы просто передать вместо этого rand(x)

 >> Array.new(3, rand(100))
=> [28,28,28]

Хм. Это было не совсем случайно. Похоже, что rand(100) К счастью, Array#new

 >> Array.new(3) { rand(100) }
=> [10,53,27]

Массовое задание

Подобно Python, Ruby позволяет присваивать более одной переменной или нескольким индексам в массиве в одном и том же операторе.

 >> letter1, letter2 = ["a", "b"]
=> ["a", "b"]
>> letter1
=> "a"
>> letter2
=> "b"

>> transportation = []
>> transportation[0..1] = ["trains", "planes"]
>> transportation[0]
=> "trains"
>> transportation[1]
=> "planes"

Проверка границ

По умолчанию Ruby Arrays возвращает nil при отправке неверного индекса.

 >> [1,2,3][99]
=> nil

Если вам не нравится это поведение (возможно, в ваших массивах много ноля), вы можете переопределить #[]

 >> class Array
>>   def [](idx)
>>     self.fetch(idx)
>>   end
>> end

>> [1,2,3][99]
=> IndexError: index 99 outside of array bounds: -3...3

!!ПРЕДУПРЕЖДЕНИЕ!! Обращение с основными библиотеками Ruby может привести к ошибкам, которые очень трудно отследить. Фактически, даже предыдущий пример убивает способность Array принимать более одного аргумента и, по-видимому, вызывает ошибки в irb. Если вы не хотите исследовать, как переопределить методы Array, не вызывая проблем, более безопасной альтернативой будет создание подкласса Array.

 >> class BoundsCheckedArray < Array
>>   def [](idx)
>>     self.fetch(idx)
>>   end
>> end

>> arr = BoundsCheckedArray.new([1,2,3])
>> arr[99]
=> IndexError: index 99 outside of array bounds: -3...3

Случайные элементы массива

Мы видели, как создавать случайные числа в Ruby, но что еще? Возможно, ваш значимый друг недавно реализовал такой метод:

 >> def random_restaurant
>>   "I don't care. Where do you want to go?"
>> end

>> 3.times.map { random_restaurant }
=> ["I don't care. Where do you want to go", "I don't care. Where do you want to go", "I don't care. Where do you want to go"]

Задушивая слезы разочарования, вы можете поместить в список несколько ресторанов поблизости и использовать метод #sample для случайного выбора .

 >> def random_restaurant
>>   ["Process: The Forkeria", "Shenanigans", "Grease Factory"].sample
>> end

>> 3.times.map { random_restaurant }
=> ["Process: The Forkeria", "Grease Factory", "Process: The Forkeria"]

Если вы дважды #sample Если вам нужно более одного уникального случайного элемента, вы можете передать количество элементов в #sample.

 >> ["a","b","c"].sample(2)
=> ["c", "a"]

Многомерные и высокопроизводительные массивы

В отличие от своего двоюродного брата Python, Ruby довольно хорошо позволяет вам объявлять многомерные массивы интуитивно понятным способом.

 >> two_d = [[1,2,3],[4,5,6],[7,8,9]] 
>> two_d[1][1]
=> 5

К сожалению, люди обычно не обращают внимания на Ruby при выполнении дорогостоящих математических операций. В этом случае часто намного легче покинуть корабль и изучить NumPy

Тем не менее, японский исследователь создал гем NArray, который предоставляет быстрые векторы и матрицы. К сожалению, цена скорости заключается в том, что вы ограничены однородными, числовыми коллекциями.

 $ gem install narray --version 0.6.0.8

Создать n-мерные массивы с помощью NArray очень просто. Каждый аргумент конструктора NArray действует как размер для измерения.

 >> require 'narray'
>> NArray.int(2)
=> NArray.int(2): 
   [ 0, 0 ] 

>> NArray.int(2,2)
=> NArray.int(2,2): 
   [ [ 0, 0 ], 
     [ 0, 0 ] ] 

>> NArray.int(2,3)
=> NArray.int(2,3): 
   [ [ 0, 0 ], 
     [ 0, 0 ], 
     [ 0, 0 ] ] 

>> NArray.int(1,2)
=> NArray.int(1,2): 
   [ [ 0 ], 
     [ 0 ] ]

NArray использует обозначение [x, y] со вкусом математики вместо обозначения [x] [y] для указания индексов.

 >> arr = NArray.int(3,3)
>> arr[1,1] = 7
>> arr
=> NArray.int(3,3): 
   [ [ 0, 0, 0 ], 
     [ 0, 7, 0 ], 
     [ 0, 0, 0 ] ]

Это не совместимо с массивом. arr[x][y]

 >> arr[1][1]
=> 0
>> arr[1,1]
=> 7

Gem NArray не ограничивается целыми числами или объектами NArray. Английскую документацию по NArray можно найти здесь

Бенчмаркинг

С кажущимся бесконечным количеством способов доступа к коллекциям Ruby и манипулирования ими вам может быть интересно узнать, насколько эффективны используемые вами методы. Например, действительно ли быстрее выполнить тест членства с помощью Set, чем с Array?

Ruby предоставляет Benchmark для временного кода.

 >> require "benchmark"
>> require "set"

>> array = ["a", -3.14, 0, []]
>> set = array.to_set

>> Benchmark.bm do |bench|
>>   bench.report("array:") do
>>     1000.times { array.include? -3.14 }
>>   end
>>   bench.report("set:") do
>>     1000.times { set.include? -3.14 }
>>   end
>> end

      user     system      total        real
array:  0.000000   0.000000   0.000000 (  0.000200)
set:    0.000000   0.000000   0.000000 (  0.000212)

Хотя это вряд ли можно назвать полным сравнением, разница для моей реализации Ruby кажется незначительной (1.9.3-p448).

Вывод

Ну вот и закончился мой сериал по коллекциям Ruby. Вот некоторые дополнительные ресурсы, если вы хотите продолжить свое образование: