Как должен работать каждый_объект?
new для ruby, я пытаюсь понять, как использовать each_with_object
.
Я пытаюсь использовать его для очень простой суммы, поэтому я написал:
> (1..3).each_with_object(0) {|i,sum| sum+=i}
=> 0
Эй, я бы предположил, что результатом будет 6! Где моя ошибка?
Ответы
Ответ 1
each_with_object
не работает с неизменяемыми объектами, такими как integer.
(1..3).each_with_object(0) {|i,sum| sum += i} #=> 0
Это происходит потому, что each_with_object
выполняет итерацию по коллекции, передавая каждый элемент и данный объект блоку. Он не обновляет значение объекта после каждой итерации и возвращает исходный заданный объект.
Он будет работать с хэшем, так как изменение значения хэш-ключа изменяет его для исходного объекта.
(1..3).each_with_object({:sum => 0}) {|i,hsh| hsh[:sum] += i}
#=> {:sum => 6}
String
Объекты интересны. Они изменяемы, поэтому вы можете ожидать следующего
для возврата "abc"
("a".."c").each_with_object("") {|i,str| str += i} # => ""
но это не так. Это связано с тем, что str += "a"
возвращает новый объект, а исходный объект остается прежним. Однако если мы делаем
("a".."c").each_with_object("") {|i,str| str << i} # => "abc"
работает, потому что str << "a"
изменяет исходный объект.
Для получения дополнительной информации см. ruby docs для each_with_object
Для вашей цели используйте inject
(1..3).inject(0) {|sum,i| sum += i} #=> 6
# or
(1..3).inject(:+) #=> 6
Ответ 2
Документация Enumerable#each_with_object
очень проста:
Итерирует данный блок для каждого элемента с заданным произвольным объектом, а возвращает первоначально заданный объект.
В вашем случае (1..3).each_with_object(0) {|i,sum| sum+=i}
вы проходите 0
, что является неизменным объектом. Таким образом, здесь intial object для метода each_with_object
имеет значение 0
, поэтому метод возвращает 0
. Он работает на нем, рекламируется. Ниже показано, как работает each_with_object
,
(1..3).each_with_object(0) do |e,mem|
p "#{mem} and #{e} before change"
mem = mem + e
p mem
end
# >> "0 and 1 before change"
# >> 1
# >> "0 and 2 before change"
# >> 2
# >> "0 and 3 before change"
# >> 3
Это означает, что в каждом проходе mem
устанавливается исходный переданный объект. Возможно, вы думаете о первом проходе mem
is 0
, а затем в следующем проходе mem
является результатом mem+=e
, т.е. mem
будет 1
. Но NO, в каждом проходе mem
- ваш исходный объект, который 0
.
Ответ 3
Один простой, но общий пример для each_with_object
- это когда вам нужно построить хеш в зависимости от элементов массива. Очень часто вы видите что-то вроде:
hash = {}
[1, 2, 3, 4].each do |number|
hash[number] = number**2
end
hash
С each_with_object
намного проще:
[1,2,3,4].each_with_object({}) { |number, hash| hash[number] = number**2 }
Советую прочитать документы для inject
, tap
и each_with_index
. Они очень полезны, когда вам нужен короткий и читаемый код.