Ruby: проблема с внедрением при превращении массива в хеш

a = [[1, 'a'],[2, 'b'],[3, 'c'], [4, 'd']]
a.inject({}) {|r, val| r[val[0]] = val[1]}

Когда я запускаю это, я получаю ошибку индекса

Когда я поменяю блок на

a.inject({}) {|r, val| r[val[0]] = val[1]; r}

Тогда это работает.

Как Рубин обрабатывает первую попытку инъекции, которая не дает того, что я хочу?
Есть лучший способ сделать это

 KChaloux13 мая 2013 г., 17:12
+ 1 Для того, чтобы задать вопрос, имеющий отношение к чему-то, что я пытался сделать, он помог мне. ;)

Ответы на вопрос(4)

a.inject({}) { |r, val| r.merge({ val[0] => val[1] }) }

Есть более простой способ -

a = [[1, 'a'],[2, 'b'],[3, 'c'], [4, 'd']]
b = Hash[a] # {1=>"a", 2=>"b", 3=>"c", 4=>"d"}

Причина, по которой первый метод не работает, заключается в том, что команда inject использует результат блока какr в следующей итерации. Для первой итерацииr устанавливается на аргумент, который вы передаете ему, в данном случае это{}.

 Dave Newton13 мая 2012 г., 22:47
+ 1; ничего не объясняет, ноявляетс лучший способ (если пример кода действительно то, что они делают) - часть вторая вопроса.

inject, вторая возвращает хэш, так что на самом деле это работает.

Многие люди считают, что такое использованиеinject анти-паттерн; рассмотреть возможностьeach_with_object вместо.

 Michael Kohl13 мая 2012 г., 23:38
Лично я не думаю, что это анти-паттерн для фолда. Если вам явно нужно возвращать хеш, просто используйте другой метод:a.inject({}) { |r, (k, v)| r.merge(k => v) } (илиmerge! если тебе мешают одноразовые хеши).
 Dave Newton13 мая 2012 г., 23:41
@ MichaelKohl Я не сказалI сделал ... хотя, если бы меня заставили принять сторону, я был бы против этого по принципу элегантности.
 Dave Newton13 мая 2012 г., 22:48
@ Chuck Правильно, если код ОП именно то, что они пытаются сделать, я согласен. Я отвечал на первую часть.
Решение Вопроса

что Ruby динамически и неявно типизирован, не означает, что вам не нужно думать о типах.

ТипEnumerable#inject без явного аккумулятора (обычно это называетсяreduce) это что-то вроде

reduce :: [a] → (a → a → a) → a

или в более рубиновой записи, которую я только что состав

Enumerable[A]#inject {|A, A| A } → A

Вы заметите, что все типы одинаковы. Тип элементаEnumerable, два типа аргумента блока, тип возвращаемого значения блока и тип возвращаемого значения общего метода.

ТипEnumerable#inject явный аккумулятор (обычно это называетсяfold) это что-то вроде

fold :: [b] → a → (a → b → a) → a

ил

Enumerable[B]#inject(A) {|A, B| A } → A

Вот видишь, что аккумулятор может иметьдруго type, чем тип элемента коллекции.

Эти два правила, как правило, помогают тебе пройти всеEnumerable#inject связанные с типом проблемы:

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

В данном случае это правило №1 кусает тебя. Когда вы делаете что-то вроде

acc[key] = value

в вашем блоке присвоения оцениваются назначенному значению, а не получателю назначения. Вам придется заменить это на

acc.tap { acc[key] = value }

Смотрите также Почему метод ввода Ruby не может суммировать длины строк без начального значения?

BTW: вы можете использовать деструктурирующую привязку, чтобы сделать ваш код более читабельным:

a.inject({}) {|r, (key, value)| r[key] = value; r }
 That Umbrella Guy14 мая 2012 г., 18:38
О, так что инъекция - это операция сгиба. Это имеет смысл тогда.
 Jörg W Mittag14 мая 2012 г., 19:37
Да,inject - это имя Руби (на самом деле, Smalltalk) дляfold / reduce. Кстати: это освежает дон получаю пустой взгляд, говоря "inject это простоfold ": -)

Ваш ответ на вопрос