爲什麼要使用to_enum方法而不是直接使用對象來爲Ruby中的對象創建代理引用?我想不出有什麼實際用途,試圖理解這個概念&在哪裏有人可能會使用它,但我所看到的所有例子似乎非常微不足道。在Ruby中使用to_enum創建可枚舉對象的優點是什麼?
例如,爲什麼要用:中
"hello".enum_for(:each_char).map {|c| c.succ }
代替
我知道這是一個很簡單的例子,沒有任何人有任何真實世界的例子?
爲什麼要使用to_enum方法而不是直接使用對象來爲Ruby中的對象創建代理引用?我想不出有什麼實際用途,試圖理解這個概念&在哪裏有人可能會使用它,但我所看到的所有例子似乎非常微不足道。在Ruby中使用to_enum創建可枚舉對象的優點是什麼?
例如,爲什麼要用:中
"hello".enum_for(:each_char).map {|c| c.succ }
代替
我知道這是一個很簡單的例子,沒有任何人有任何真實世界的例子?
這不是您的問題的答案,但希望它是相關的。
在你的第二個例子你沒有通過調用塊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"]
大多數內置在接受塊將在返回的情況下一個枚舉的方法沒有塊提供(如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
我認爲這與內部和外部迭代器有關。當您返回像這樣的統計員時:
p = "hello".enum_for(:each_char)
p是一個外部枚舉器。外部迭代器的一個優點是:
外部迭代器比內部迭代器更靈活。例如,比較兩個集合與外部迭代器的相等性是很容易的,但對於內部迭代器來說實際上是不可能的。但另一方面,內部迭代器更易於使用,因爲它們爲您定義了迭代邏輯。 [來自Ruby編程語言手冊,ch。 5.3]
因此,與外部迭代器,你可以做,例如:
p = "hello".enum_for(:each_char)
loop do
puts p.next
end
比方說,我們要採取鍵數組和值數組和散列縫起來:
隨着#to_enum
def hashify(k, v)
keys = k.to_enum(:each)
values = v.to_enum(:each)
hash = []
loop do
hash[keys.next] = values.next
# No need to check for bounds,
# as #next will raise a StopIteration which breaks from the loop
end
hash
end
無# to_enum:
def hashify(k, v)
hash = []
keys.each_with_index do |key, index|
break if index == values.length
hash[key] = values[index]
end
hash
end
閱讀第一種方法比較容易,你不覺得嗎?不容易,但想象一下,如果我們以某種方式操縱3個陣列的物品? 5? 10?
可怕的例子。使用'next'會殺死你的表現。這兩個示例都不以相同的方式處理大小差異(一個提高,另一個停止)。 – 2014-08-06 15:15:53
對於大型或無限的發生器對象來說,這非常棒。例如,以下將給出針對整個斐波納契序列的枚舉器,從0到無窮大。
def fib_sequence
return to_enum(:fib_sequence) unless block_given?
yield 0
yield 1
x,y, = 0, 1
loop { x,y = y,x+y; yield(y) }
end
to_enum
有效地讓你寫這與普通yields
而不必惹Fiber
秒。
然後可以分析它,只要你想,這將是非常高效存儲,因爲沒有陣列將被存儲在內存中:
module Slice
def slice(range)
return to_enum(:slice, range) unless block_given?
start, finish = range.first, range.max + 1
copy = self.dup
start.times { copy.next }
(finish-start).times { yield copy.next }
end
end
class Enumerator
include Slice
end
fib_sequence.slice(0..10).to_a
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
fib_sequence.slice(10..20).to_a
#=> [55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
它也是有用的,如果你使用第三方庫,唐」返回一個枚舉器。 – 2011-12-01 07:43:41