【问题标题】:When is an enumerator useful? [closed]枚举器什么时候有用? [关闭]
【发布时间】:2015-05-29 16:36:22
【问题描述】:

Enumerator 对象可以通过调用某些方法而不传递块来创建,例如Array#reject

(1..3).reject.to_a
# => [1, 2, 3]

这只是一些 ruby​​ 规则以便于链接,还是有其他方法可以将行为传递给枚举器?

【问题讨论】:

  • " 是否有其他方法可以将行为传递给枚举器?" - 不知道你的意思。
  • 我不同意你将绿色奖授予@Sergio 的决定,但下次考虑在做出决定之前等待一段时间(例如,至少几个小时)。快速选择(在您发布问题后大约 25 分钟后)可能会阻止其他答案,并且那些仍在研究他们的答案的人不会赞赏。即使后续答案不是“更好”,它们也可能包含有用的观察结果,您可以在其他情况下应用这些观察结果。不用着急。

标签: ruby enumerator


【解决方案1】:

这只是一些 ruby​​ 规则以便于链接吗

是的,正是这个原因。简单的枚举器链接。考虑这个例子:

ary = ['Alice', 'Bob', 'Eve']

records = ary.map.with_index do |item, idx|
  {
    id: idx,
    name: item,
  }
end

records # => [{:id=>0, :name=>"Alice"}, {:id=>1, :name=>"Bob"}, {:id=>2, :name=>"Eve"}]

map 将每个元素生成为with_index,它会在其顶部添加项目索引并生成您的块。块返回值到with_index,它返回到map,它(做它的事情,映射,)返回给调用者。

【讨论】:

    【解决方案2】:

    这只是一些 ruby​​ 规则以便于链接

    这不是 Ruby 的规则。只是reject 方法(例如)如果在没有块的情况下调用,则返回Enumerator。它可以做其他事情。

    或者还有其他方法可以将行为传递给Enumerator

    是的,Enumerator 封装了您创建 Enumerator 的方法的行为。例如,从reject 创建一个Enumerator 会创建一个对象,该对象将拒绝其某些元素。 EnumeratorEnumerable 混合在一起,所以可以用这个Enumerator 做任何你可以用任何其他Enumerable 做的事情。例如:

    enum = (1..3).reject
    
    enum.with_index {|el, i| i.even? }
    # => [2]
    

    【讨论】:

    • 能够将拒绝行为传递给枚举的好例子。
    【解决方案3】:

    正如@Sergio 所说,它主要用于链接,但不止于此。如果您有枚举器e,则可以使用Enumerator#nextEnumerator#peek 提取元素。以下是枚举器如何发挥优势的两个示例。

    问题:给定一个数组a,构造另一个数组,如果i 为奇数,则索引i 的值为a[i],如果i 为偶数,则为2*a[i]。假设a = [1,2,3,4]

    人们通常会看到:

    a.map.with_index { |n,i| n.odd? ? n : 2*n } #=> [1,4,3,8]
    

    但也可以这样写:

    e = [1,2].cycle          #=> #<Enumerator: [1, 2]:cycle> 
    a.map { |n| e.next * n } #=> [1, 4, 3, 8] 
    

    问题:给定一个数组a,将相等的连续值分块到数组中。让我通过展示它通常是如何完成的来使这个陈述更准确。假设a = [1,1,2,3,3,3,4]

    a.chunk(&:itself).map(&:last) #=> [[1, 1], [2], [3, 3, 3], [4]]
    

    在 Ruby v2.2(#itself 首次亮相)中,您可以使用 Enumerable#slice_when

    a.slice_when { |f,l| f != l }.to_a
      #=> [[1, 1], [2], [3, 3, 3], [4]]
    

    但您也可以使用枚举器:

    e = a.to_enum
      #=> #<Enumerator: [1, 1, 2, 3, 3, 3, 4]:each> 
    b = [[]]
    loop do
      n = e.next
      b[-1] << n
      nxt = e.peek
      b << [] if nxt != n
    end
    b
      #=> [[1, 1], [2], [3, 3, 3], [4]]
    

    请注意,当ne 的最后一个值时,e.peek 将引发StopInteration 异常。该异常由Kernel#loop 通过跳出循环来处理。

    我不建议使用最后一种方法优先于我提到​​的其他两个选项,但在其他情况下可以有效地使用这种方法。

    还有一件事:如果你有一个链式方法的表达式,你可以通过将枚举器转换为数组来检查其元素被传递给块的枚举器的内容。从那里您可以看到需要哪些块变量。假设你想写:

    [1,2,3,4].each_with_index.with_object({}) {....}
    

    并在块中做一些事情,但不确定如何表达块变量。你可以这样做:

    e = [1,2,3,4].each_with_index.with_object({})
      #=> #<Enumerator: #<Enumerator: [1, 2, 3, 4]:each_with_index>
            :with_object({})> 
    e.to_a
      #=> [[[1, 0], {}], [[2, 1], {}], [[3, 2], {}], [[4, 3], {}]] 
    

    这表明(比如说)传递给块的e 的第一个元素是:

    [[1, 0], {}]
    

    告诉使用块变量应该是:

    (n,i), h = [[1, 0], {}]
    n #=> 1 
    i #=> 0 
    h #=> {} 
    

    表示应该写成表达式:

    [1,2,3,4].each_with_index.with_object({}) { |(n,i),h|....}
    

    【讨论】:

      猜你喜欢
      • 2011-03-22
      • 2010-10-11
      • 2013-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多