Каков "правильный" способ итерации через массив в Ruby?
PHP, при всех его бородавках, довольно хорош в этом отношении. Нет никакой разницы между массивом и хэшем (может быть, я наивен, но это кажется мне очевидным), и для итерации либо вы просто делаете
foreach (array/hash as $key => $value)
в Ruby есть куча способов сделать такие вещи:
array.length.times do |i|
end
array.each
array.each_index
for i in array
хэши имеют больше смысла, так как я просто всегда использую
hash.each do |key, value|
почему я не могу сделать это для массивов? Если я хочу вспомнить только один метод, я думаю, я могу используйте each_index (так как это делает и индекс и значение доступными), но это раздражает, чтобы сделать array[index] вместо value.
Ах да, я забыл про array.each_with_index. Однако, это один отстой, потому что он идет |value, key| и hash.each идет |key, value|! Разве это не безумие?
11 ответов:
это будет повторяться через все элементы:
array = [1, 2, 3, 4, 5, 6] array.each { |x| puts x }принты:
1 2 3 4 5 6это будет повторяться через все элементы, дающие вам значение и индекс:
array = ["A", "B", "C"] array.each_with_index {|val, index| puts "#{val} => #{index}" }принты:
A => 0 B => 1 C => 2Я не совсем уверен в вашем вопросе, который вы ищете.
Я думаю, что нет никого право путь. Есть много различных способов итерации, и каждый из них имеет свою собственную нишу.
eachдостаточно для многих применений, так как я не часто забочусь об индексах.each_ with _indexдействует как хэш#each-вы получаете значение и индекс.each_index- только индексы. Я не часто этим пользуюсь. Эквивалентно " длина.времена."map- это еще один способ для итерации, полезно, когда вы хотите преобразовать один массив в другой.selectявляется итератором для использования, когда вы хотите выбрать подмножество.injectполезно для генерации сумм или продуктов, или сбора одного результата.это может показаться много, чтобы помнить, но не волнуйтесь, вы можете получить, не зная их. Но когда вы начнете изучать и использовать различные методы, ваш код станет чище и яснее, и вы будете на своем пути за мастерство Рубина.
я этого не говорю
Array->|value,index|иHash->|key,value|не является безумным (см. комментарий Горация Леба), но я говорю, что есть разумный способ ожидать этого соглашения.когда я имею дело с массивами, я сосредоточен на элементах в массиве (а не на индексе, потому что индекс является временным). Метод каждый с индексом, т. е. каждый + индекс, или / каждый, индекс|, или
|value,index|. Это также согласуется с индексом, рассматриваемым как необязательный аргумент, например |значение| эквивалентно |значение индекса=шь| которая соответствует |значение индекса|.когда я имею дело с хэшами, я часто больше сосредоточен на ключах, чем на значениях, и я обычно имею дело с ключами и значениями в этом порядке, либо
key => valueилиhash[key] = value.если вы хотите Duck-typing, то либо явно используйте определенный метод, как показал Брент Лонгборо, либо неявный метод, как показал maxhawkins.
Рубин-все о размещения язык для того чтобы одеть программист, не о программисте приспосабливая для того чтобы одеть язык. Вот почему существует так много способов. Есть так много способов думать о чем-то. В Ruby вы выбираете ближайший, а остальная часть кода обычно выпадает очень аккуратно и лаконично.
что касается исходного вопроса, "каков" правильный " способ итерации через массив в Ruby?"ну, я думаю, что основной способ (т. е. без мощного синтаксического сахара или объектно-ориентированной мощности) - это делать:
for index in 0 ... array.size puts "array[#{index}] = #{array[index].inspect}" endно Ruby - это все о мощном синтаксическом сахаре и объектно-ориентированной мощности, но в любом случае здесь есть эквивалент для хэшей, и ключи могут быть упорядочены или нет:
for key in hash.keys.sort puts "hash[#{key.inspect}] = #{hash[key].inspect}" endИтак, мой ответ: "правильный "способ перебора массива в Ruby зависит от вас (т. е. программиста или команды программистов) и проекта.". Лучший программист Ruby делает лучший выбор (какой синтаксической мощности и/или какой объектно-ориентированный подход). Этот лучший программист Ruby продолжает искать больше способов.
теперь я хочу задать еще один вопрос: "каков" правильный " способ итерации через диапазон в Ruby назад?"! (Этот вопрос, как я пришел на эту страницу.)
это приятно делать (для нападающих):
(1..10).each{|i| puts "i=#{i}" }но я не люблю делать (в обратном порядке):
(1..10).to_a.reverse.each{|i| puts "i=#{i}" }Ну, я на самом деле не делать это слишком много, но когда я учу идти назад, я хочу покажите моим студентам хорошую симметрию (т. е. с минимальной разницей, например, только добавление обратного или шаг -1, но без изменения чего-либо еще). Вы можете сделать (для симметрии):
(a=*1..10).each{|i| puts "i=#{i}" }и
(a=*1..10).reverse.each{|i| puts "i=#{i}" }что мне не нравится, но вы не можете сделать
(*1..10).each{|i| puts "i=#{i}" } (*1..10).reverse.each{|i| puts "i=#{i}" } # (1..10).step(1){|i| puts "i=#{i}" } (1..10).step(-1){|i| puts "i=#{i}" } # (1..10).each{|i| puts "i=#{i}" } (10..1).each{|i| puts "i=#{i}" } # I don't want this though. It's dangerousвы могли бы в конечном итоге сделать
class Range def each_reverse(&block) self.to_a.reverse.each(&block) end endно я хочу преподавать чистый Ruby, а не объектно-ориентированные подходы (пока). Я хотел бы повторить назад:
- без создания массива (считать 0..1000000000)
- работает для любого диапазона (например, строки, а не только целые числа)
- без использования какой-либо дополнительной объектно-ориентированной мощности (т. е. без модификации класса)
я считаю, что это невозможно без определения
predметод, что означает изменение класса диапазона для его использования. Если вы можете сделать это, пожалуйста, дайте мне знать, в противном случае подтверждение невозможности будет оценил, хотя это было бы разочарованием. Возможно, Ruby 1.9 обращается.(Спасибо за ваше время на чтение этого.)
другие ответы просто прекрасны, но я хотел бы указать еще одну периферийную вещь: массивы упорядочены, тогда как хэши не находятся в 1.8. (В Ruby 1.9 хэши упорядочены по порядку вставки ключей.) Таким образом, до 1.9 не было бы смысла перебирать хэш таким же образом/последовательность, как массивы, которые всегда имели определенный порядок. Я не знаю, каков порядок по умолчанию для ассоциативных массивов PHP (по-видимому, мой google fu недостаточно силен, чтобы понять это), но Я не знаю, как вы можете считать обычные массивы PHP и ассоциативные массивы PHP "одинаковыми" в этом контексте, поскольку порядок ассоциативных массивов кажется неопределенным.
таким образом, путь Ruby кажется мне более ясным и интуитивно понятным. :)
пытаюсь сделать то же самое последовательно с массивами и хэшами может просто запах кода, но, рискуя быть заклейменным как кодористый полу-обезьяна-патчер, если вы ищете последовательное поведение, будет ли это делать трюк?:
class Hash def each_pairwise self.each { | x, y | yield [x, y] } end end class Array def each_pairwise self.each_with_index { | x, y | yield [y, x] } end end ["a","b","c"].each_pairwise { |x,y| puts "#{x} => #{y}" } {"a" => "Aardvark","b" => "Bogle","c" => "Catastrophe"}.each_pairwise { |x,y| puts "#{x} => #{y}" }
Я пытался построить меню (в поход и Markaby) С помощью хэша.
каждый элемент имеет 2 элемента: a ярлык меню и URL, поэтому хэш казался правильным, но ' / 'URL для' Home ' всегда появлялся последним (как и следовало ожидать для хэша), поэтому пункты меню появлялись в неправильном порядке.
используя массив с
each_sliceработает:['Home', '/', 'Page two', 'two', 'Test', 'test'].each_slice(2) do|label,link| li {a label, :href => link} endдобавление дополнительных значений для каждого пункта меню (например, как CSS ID name) просто означает увеличение значения среза. Так, как хэш, но с группами, состоящими из любого количества элементов. Идеальный.
Так что это просто сказать спасибо за непреднамеренно намек на решение!
очевидно, но стоит отметить: я предлагаю проверить, если длина массива делится на значение среза.
вот четыре варианта, перечисленные в вашем вопросе, упорядоченные по свободе управления. Вы можете использовать другой в зависимости от того, что вам нужно.
просто пройдите через значения:
array.eachпросто пройдите через индексы:
array.each_indexпройдите через индексы + переменная индекса:
for i in arrayсчетчик контуров управления + переменная индекса:
array.length.times do | i |
Если вы используете перечисли mixin (как и Rails) вы можете сделать что-то похожее на приведенный фрагмент php. Просто используйте метод each_slice и сгладьте хэш.
require 'enumerator' ['a',1,'b',2].to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" } # is equivalent to... {'a'=>1,'b'=>2}.to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }требуется меньше обезьян-исправлений.
однако, это вызывает проблемы, когда у вас есть рекурсивный массив или хэш со значениями массива. В ruby 1.9 эта проблема решается с помощью параметра метода flatten, который указывает, как глубоко рекурсировать.
# Ruby 1.8 [1,2,[1,2,3]].flatten => [1,2,1,2,3] # Ruby 1.9 [1,2,[1,2,3]].flatten(0) => [1,2,[1,2,3]]как для вопрос о том, является ли это запахом кода, я не уверен. Обычно, когда мне приходится наклоняться назад, чтобы повторить что-то, я отступаю назад и понимаю, что неправильно атакую проблему.
правильный путь-это тот, который вы чувствуете себя наиболее комфортно и который делает то, что вы хотите это делать. В программировании редко есть один "правильный" способ делать вещи, чаще есть несколько способов выбора.
Если вас устраивает определенный способ делать вещи, делайте только это, если это не работает - тогда пришло время найти лучший способ.
в Ruby 2.1 метод each_with_index удаляется. Вместо этого вы можете использовать each_index
пример:
a = [ "a", "b", "c" ] a.each_index {|x| print x, " -- " }выдает:
0 -- 1 -- 2 --
Comments