2015-04-06 3 views
2

Новое для Ruby; Когда я работаю с массивами, существует ли разница между Enumerable#each_with_index и Array#index Я работаю с многомерным массивом, в котором я пытаюсь найти местоположение заданного значения. Мой код может передавать все тесты, когда я использую один из них. Координаты: массив Я использую, чтобы держать местоположение заданного значения (в данном случае 1)Использовать случай each_with_index против индекса в Ruby

field=[[1,0],[0,0]] 
coordinates=[] 
field.each_with_index do |item| 
    if item.index(1) 
    coordinates.push(field.index(item)).push(item.index(1)) 
    end 
end 

Заранее спасибо за помощь.

ответ

5

Давайте внимательнее посмотрим на код:

field=[[1,0],[0,0]] 
coordindates = [] 
field.each_with_index do |item| 
    if item.index(1) 
    coordinates.push(field.index(item)).push(item.index(1)) 
    end 
end 

Пусть:

enum = field.each_with_index 
    #=> #<Enumerator: [[1, 0], [0, 0]]:each_with_index> 

Как вы видите, это возвращает перечислитель.

Руби видит свой код так:

enum.each do |item| 
    if item.index(1) 
    coordinates.push(field.index(item)).push(item.index(1)) 
    end 
end 

Элементы счетчику будут переданы в блок по Enumerator#each, который будет вызывать Array#each с приемником, field является экземпляром класса Array.

Мы можем увидеть элементы enum путем преобразования его в массив:

enum.to_a 
    #=> [[[1, 0], 0], [[0, 0], 1]] 

Как вы видите, она состоит из двух элементов, каждый из которых массив из двух элементов, первый из которых является массивом из двух целых чисел а второе - целое число.

Мы можем имитировать работу each, посылая Enumerator#next к enum и назначая блок переменных к значению, возвращаемых next. Так как только один блок переменной, item, мы имеем:

item = enum.next 
    #=> [[1, 0], 0] 

То есть вполне вероятно, ни то, что вы ожидали, ни то, что вы хотели.

Далее вы вызываете Array#index на item:

item.index(1) 
    #=> nil 

index поиск массива item для элемента, который равен 1. Если он находит один, он возвращает индекс этого элемента в массиве. (Например, item.index(0) #=> 1). Поскольку ни [1,0], ни 0 не равно 1, indexnil.

Давайте перемотаем (и воссоздаем перечислитель). Вам нужно два блока переменных:

field.each_with_index do |item, index|... 

, который так же, как:

enum.each do |item, index|... 

Итак:

item, index = enum.next 
     #=> [[1, 0], 0] 
item #=> [1, 0] 
index #=> 0 

и

item.index(1) 
    #=> 0 

Я дам вам взять отсюда, но позвольте мне упомянуть еще одну нг. Я не защищаю его, но вы могли бы написать:

field.each_with_index do |(first, second), index|... 

в этом случае:

(first, second), index = enum.next 
     #=> [[1, 0], 0] 
first #=> 1 
second #=> 0 
index #=> 0 
+1

++ Возможно, ваш следующий ответ может включать в себя некоторые примеры flossing **^,,,^** – naomik

1

См рубин документ: http://ruby-doc.org/core-2.2.0/Array.html#method-i-index и http://ruby-doc.org/core-2.2.1/Enumerable.html#method-i-each_with_index

index является метод Array, он обнаруживает, существует ли элемент в определенном Array, возвращает индекс, если элемент существует, и nil, если не существует.

each_with_index - метод Enumerable mixin, он обычно принимает 2 аргумента, первый - это элемент, а второй - индекс.

Так что ваш пример может быть упрощен:

field = [[1, 0], [0, 0]] 
coordinates = [] 
field.each_with_index do |item, index| 
    item_index = item.index(1) 
    coordinates << index << item_index if item_index 
end 
puts coordinates.inspect # => [0, 0] 

Примечание ваш field.index(item) просто index.

1

Array#index и Enumerable#each_with_index не связаны вообще (функционально говоря), один используется для получения индекса объекта в массиве, а другой используется для прохождения через коллекцию.

Фактически each_with_index является мутацией метода each, который дополнительно дает индекс фактического объекта в коллекции. Этот метод очень полезен, если вам нужно отслеживать текущую позицию объекта, в котором вы находитесь. Это избавляет вас от необходимости создавать и увеличивать дополнительную переменную.

Например

['a', 'b', 'c', 'd'].each_with_index do|char, idx| 
    puts "#{idx}) #{char}" 
end 

выход:

0) a 
1) b 
2) c 
3) d 

Без each_with_index

idx = 0 
['a', 'b', 'c', 'd'].each do|char| 
    puts "#{idx}) #{char}" 
    idx += 1 
end 

выход:

0) a 
1) b 
2) c 
3) d 

Чтобы найти индекс объекта в пределах многомерного массива вам придется итерацию по крайней мере, через все строки матрицы, как это (с использованием each_with_index и index)

def find_position(matrix, obj) 
    matrix.each_with_index do|row, i| 
      return [i, j] if j = row.index(obj) 
    end 
    return nil 
end 

, например:

find_position([[2,3],[4,5]], 5) # => [1, 1] 
1

Учитывая, что:

fields = [[1, 0], [0, 0]] 

перечислимых # each_with_index проходит каждый индекс, а также каждый объект к блоку:

fields.each_with_index do |field, idx| 
    puts "#{field}, #{idx}" 
end 

#=> [1, 0], 0 
#=> [0, 0], 1 

Индекс массива # возвращает индекс соответствующего элемента массива:

puts fields.index([1, 0]) 

#=> 0 
Смежные вопросы