Итерация по массиву массивов

def compute(ary)
  return nil unless ary
  ary.map { |a, b| !b.nil? ? a + b : a }
end

compute([1,2],[3,4])

Может кто-нибудь объяснить мне, как вычисление добавляет значения внутреннего массива?

Мне кажется, что вызов map для этого массива массивов объединит два массива, а не внутренние элементы каждого массива.


person Bo G.    schedule 18.07.2014    source источник
comment
Я попробовал это и получил в «вычислении»: неправильное количество аргументов (2 из 1) (ArgumentError)   -  person Some Guy    schedule 18.07.2014
comment
Я скопировал этот код, строка за строкой, из RubyMonk, и я заметил, что код, который он использует, довольно странный. Я никогда не пробовал это в IRB, так что спасибо за внимание.   -  person Bo G.    schedule 18.07.2014


Ответы (2)


map в основном перебирает элементы объекта:

foo = [
  ['a', 'b'],
  ['c', 'd']
]

foo.map{ |ary| puts ary.join(',') }
# >> a,b
# >> c,d

В этом примере он передает каждый подмассив, который назначен ary.

Если посмотреть на это немного иначе:

foo.map{ |ary| puts "ary is a #{ary.class}" }
# >> ary is a Array
# >> ary is a Array

Поскольку Ruby позволяет нам присваивать несколько значений одновременно, это можно было бы написать так:

foo.map{ |item1, item2| puts "item1: #{ item1 }, item2: #{ item2 }" }
# >> item1: a, item2: b
# >> item1: c, item2: d

Если map выполняет итерацию по массиву хэшей, каждая итерация дает вложенный хеш для блока:

foo = [
  {'a' => 1},
  {'b' => 2}
]

foo.map{ |elem| puts "elem is a #{ elem.class }" }
# >> elem is a Hash
# >> elem is a Hash

Если map выполняет итерацию по хешу, каждая итерация возвращает пару ключ/значение в блок:

foo = {
  'a' => 1,
  'b' => 2
}

foo.map{ |k, v| puts "k: #{k}, v: #{v}" }
# >> k: a, v: 1
# >> k: b, v: 2

Однако, если вы дадите блоку только один параметр, Ruby присвоит переменной и ключ, и значение, так что вы увидите ее как массив:

foo.map{ |ary| puts "ary is a #{ary.class}" }
# >> ary is a Array
# >> ary is a Array

Таким образом, вы должны знать о множестве вещей, которые происходят, когда вы перебираете контейнер и когда Ruby передает значения в блок map.

Помимо всего этого, важно помнить, что map будет возвращать значение или значения для каждой переданной вещи. map, также известный как collect, используется для преобразования значений. Его не следует использовать в качестве замены each, который выполняет только итерацию. Во всех приведенных выше примерах я не показал правильное использование map, потому что пытался показать, что происходит с переданными элементами. Обычно мы делаем что-то вроде:

foo = [['a', 'b'], ['c', 'd']]
foo.map{ |ary| ary.join(',') }
# => ["a,b", "c,d"]

Or:

bar = [[1,2], [3,4]]
bar.collect{ |i, j| i * j }
# => [2, 12]

Также есть map!, который изменяет итерируемый объект, а не возвращает значения. Я бы порекомендовал избегать map! до тех пор, пока вы не поймете, почему это может быть вам полезно, потому что, похоже, это бесконечно сбивает с толку людей, если они не понимают, как передаются переменные и как работают массивы и хэши.

Лучше всего играть с map в IRB. Вам будет легче увидеть, что происходит.

person the Tin Man    schedule 18.07.2014
comment
Меня просто сбивает с толку механизм, стоящий за блоком, когда вы используете две переменные в каналах вместо одной. Вы используете одну переменную, и ruby ​​знает, что вы имеете в виду каждый массив внутри массива. Вы используете два, и Ruby знает, что вы имеете в виду каждый отдельный элемент в каждом отдельном массиве внутри массива. Однако, спасибо. Ваш пример очень помог. - person Bo G.; 18.07.2014
comment
@BoG.: Привязка аргументов для блоков и Procs использует другую семантику, чем привязка аргументов для методов и лямбда-выражений. Связывание аргументов для блоков и Procs намного ближе к семантике присваивания, чем семантика аргументов метода/лямбда. (Фактически, до версии Ruby 1.8 блоки/Procs фактически использовали семантику присваивания.) См. stackoverflow.com/ a/19841196/2988, stackoverflow.com/a/2270433/2988 и stackoverflow.com/a/16073076/2988 для некоторых примеров. И подумайте о Hash#each, Hash#map и друзьях и о том, как неудобно было бы ими пользоваться, если бы это было не так. - person Jörg W Mittag; 19.07.2014

Я думаю, что понял это сам.

map выбирает первый массив массива массивов и передает его в блок. Таким образом, переменные a и b относятся к внутренним элементам первого массива, а не к первому массиву и второму массиву в массиве массивов.

person Bo G.    schedule 18.07.2014
comment
Нет, map ничего не выбирает. Как прокомментировал Some Guy, ваш код выдаст ошибку и вообще не запустится. - person sawa; 18.07.2014