Что преимуществом создания является счетный объект с помощью to_enum в Ruby?

Почему Вы создали бы ссылку прокси на объект в Ruby, при помощи to_enum метода вместо того, чтобы просто использовать объект непосредственно? Я не могу думать ни о каком практическом применении для этого, пытаясь понять это понятие и где кто-то мог бы использовать его, но все примеры, которые я видел, кажутся очень тривиальными.

Например, почему использование:

"hello".enum_for(:each_char).map {|c| c.succ }

вместо

"hello".each_char.map {|c| c.succ }

Я знаю, что это - очень простой пример, у кого-либо есть какие-либо реальные примеры?

14
задан Nakilon 11 April 2014 в 17:00
поделиться

3 ответа

Я думаю, это как-то связано с внутренними и внешними итераторами. Когда вы возвращаете перечислитель, подобный этому:

p = "hello".enum_for(:each_char)

p - внешний перечислитель. Одним из преимуществ внешних итераторов является то, что:

Внешние итераторы более гибкие, чем внутренние итераторы. Легко сравнить две коллекции на равенство, например, с внешним итератором, но практически невозможно с внутренними итераторами…. Но с другой стороны, внутренние итераторы проще в использовании, потому что они определяют логику итераций за вас. [Из книги «Язык программирования Ruby», гл. 5.3]

Итак, с внешним итератором вы можете сделать, например:

p = "hello".enum_for(:each_char)
loop do
    puts p.next
end
3
ответ дан 1 December 2019 в 12:38
поделиться

Большинство встроенных методов, принимающих блок, возвращают перечислитель в случае отсутствия блока (как String#each_char в вашем примере). Для них нет смысла использовать to_enum; оба метода дадут одинаковый эффект.

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

# How many elements are equal to their position in the array?
[4, 1, 2, 0].to_enum(:count).each_with_index{|elem, index| elem == index} #=> 2

В качестве другого примера, Array#product, #uniq и #uniq! не принимали блок. В версии 1.9.2 это было изменено, но для сохранения совместимости формы без блока не могут возвращать Enumerator. Можно по-прежнему "вручную" использовать to_enum для получения перечислителя:

require 'backports/1.9.2/array/product' # or use Ruby 1.9.2+
# to avoid generating a huge intermediary array:
e = many_moves.to_enum(:product, many_responses)
e.any? do |move, response|
  # some criteria
end 

Основное использование to_enum - это когда вы реализуете свой собственный итеративный метод. Обычно в качестве первой строки у вас будет:

def my_each
  return to_enum :my_each unless block_given?
  # ...
end
14
ответ дан 1 December 2019 в 12:38
поделиться

Это не совсем ответ на ваш вопрос, но, надеюсь, он актуален.

Во втором примере вы вызываете each_char без передачи блока. При вызове без блока each_char возвращает Enumerator , так что ваши примеры на самом деле представляют собой всего лишь два способа сделать одно и то же. (т.е. оба результата приводят к созданию перечислимого объекта.)

irb(main):016:0> e1 = "hello".enum_for(:each_char)
=> #<Enumerator:0xe15ab8>
irb(main):017:0> e2 = "hello".each_char
=> #<Enumerator:0xe0bd38>
irb(main):018:0> e1.map { |c| c.succ }
=> ["i", "f", "m", "m", "p"]
irb(main):019:0> e2.map { |c| c.succ }
=> ["i", "f", "m", "m", "p"]
1
ответ дан 1 December 2019 в 12:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: