Я попытался реализовать сокращение с помощью фигурных скобок, но не смог заставить его работать. Предполагается, что функция принимает массив слов (символов или строк) и создает хэш, где ключи - это слова, а значения - длина слов. Вот моя попытка фигурных скобок:

def find_word_lengths(word_list)
  word_list.reduce(Hash.new(0)) { |hash,word| hash[word] = word.length}
end

Я все время получаю сообщение об ошибке undefined method '[]=' for 3:Integer.

Когда я использую do вместо фигурных скобок, он работает, но только когда я возвращаю аккумулятор в конце блока сокращения.

def find_word_lengths(word_list)
  word_list.reduce(Hash.new(0)) do |hash,word| 
    hash[word] = word.length
    hash
  end
end

Я понимаю, почему второй работает, но не понимаю, почему первый нет. Насколько я понимаю, сокращение состоит в том, что он неявно возвращает аккумулятор после прохождения через массив, который он вызвал. По крайней мере, когда я пишу вызов функции уменьшения, который сводится к числу, он автоматически возвращает сумму

my_numbers = [5, 6, 7, 8]

my_numbers.reduce(1000) { |sum, number| sum + number }
#=>1026

По-разному ли reduce ведет себя в зависимости от типа аккумулятора?

1
charmingduchess 18 Ноя 2021 в 15:11
"но только когда я возвращаю аккумулятор в конце блока сокращения" - это реальная разница и вся причина, по которой работает ваш пример do ... end.
 – 
Stefan
18 Ноя 2021 в 17:48

2 ответа

Лучший ответ

Аккумулятор НЕ возвращается неявно. Это предположение, которое нарушает весь ваш анализ.

Если вы внимательно прочитаете, это указано в документах:

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

Это говорит о том, что результат выполнения блока, фигурные скобки или do / end , становится новым значением для memo в следующем раунде.

Таким образом, фигурные скобки и do / end работают одинаково, просто вы предполагали, что памятка была возвращена неявно, а это не так.

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

Помните, что последняя инструкция внутри блока будет значением памятки на следующей итерации.

3
Casper 18 Ноя 2021 в 15:59

В первом примере вы возвращаете результат присвоения word.length hash[word], то есть самого word.length. Таким образом, вторая итерация цикла получит длину первого слова вашего word_list в качестве аргумента.

Во втором примере вы явно возвращаете хэш в блоке (который затем работает).

Эквивалент однострочной версии с фигурными скобками вашей рабочей многострочной версии:

word_list.reduce(Hash.new(0)) { |hash, word| hash[word] = word.length; hash }

Вместо использования reduce вы также можете использовать вместо него each_with_object, который всегда будет возвращать исходный объект для каждой итерации:

word_list.each_with_object(Hash.new(0)) { |hash, word| hash[word] = word.length }
3
Holger Just 18 Ноя 2021 в 15:54