Как должен работать each_with_object?
Новичок в рубине, я пытаюсь выяснить, как each_with_object
должен быть использован.
Я пытаюсь использовать это для очень простой суммы, поэтому я написал:
> (1..3).each_with_object(0) {|i,sum| sum+=i}
=> 0
Эй, я бы предположил, что результат будет 6! Где моя ошибка?
3 ответа
each_with_object
не работает с неизменяемыми объектами, такими как целое число.
(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 для each_with_object
Для вашей цели используйте инъекцию
(1..3).inject(0) {|sum,i| sum += i} #=> 6
# or
(1..3).inject(:+) #=> 6
Простой, но распространенный пример использования each_with_object
это когда вам нужно построить хеш в зависимости от элементов в массиве. Очень часто вы видите что-то вроде:
hash = {}
[1, 2, 3, 4].each { |number| hash[number] = number**2 }
hash
С помощью each_with_object
избегает явной инициализации и возврата hash
переменная.
[1,2,3,4].each_with_object({}) { |number, hash| hash[number] = number**2 }
Я советую читать документы для inject
, tap
а также each_with_index
, Эти методы полезны, когда вы стремитесь к короткому и читабельному коду.
Документация Enumerable#each_with_object
очень понятно
Повторяет данный блок для каждого элемента с заданным произвольным объектом и возвращает первоначально заданный объект.
В твоем случае, (1..3).each_with_object(0) {|i,sum| sum+=i}
Вы проходите 0
, который является неизменным объектом. Таким образом, здесь первый объект 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
является 0
затем в следующем проходе mem
является результатом mem+=e
, т.е. mem
будет 1
.Но НЕТ, в каждом проходе mem
ваш начальный объект, который 0
,