Enumerator::Yielder#yield 方法什么时候有用?

发布于 2024-10-18 09:05:02 字数 232 浏览 7 评论 0原文

问题“yield 这个词的含义”提到了 Enumerator::Yielder #yield 方法。我以前没有使用过它,不知道在什么情况下它会有用。

当您想要创建无限的项目列表(例如埃拉托斯特尼筛法)以及需要使用外部迭代器时,它主要有用吗?

The question "Meaning of the word yield" mentions the Enumerator::Yielder#yield method. I haven't used it before, and wonder under what circumstances it would be useful.

Is it mainly useful when you want to create an infinite list of items, such as the Sieve of Eratosthenes, and when you need to use an external iterator?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

囚你心 2024-10-25 09:05:02

如何创建无限可枚举的 Times?”演讲关于构造和惰性迭代器,但我最喜欢的用法是用附加功能包装现有的 Enumerable(任何可枚举的,而不需要知道它到底是什么,无论它是否是无限的等)。

一个简单的例子是实现 each_with_index 方法(或者更一般地说,with_index 方法):

module Enumerable
  def my_with_index
    Enumerator.new do |yielder|
      i = 0
      self.each do |e|
        yielder.yield e, i
        i += 1
      end
    end
  end

  def my_each_with_index
    self.my_with_index.each do |e, i|
      yield e, i
    end
  end
end

[:foo, :bar, :baz].my_each_with_index do |e,i|
  puts "#{i}: #{e}"
end
#=>0: foo
#=>1: bar
#=>2: baz

扩展到核心库中尚未实现的内容,例如循环分配值将给定数组分配给每个可枚举元素(例如,用于为表行着色):

module Enumerable
  def with_cycle values
    Enumerator.new do |yielder|
      self.each do |e|
        v = values.shift
        yielder.yield e, v
        values.push v
      end
    end
  end
end

p (1..10).with_cycle([:red, :green, :blue]).to_a # works with any Enumerable, such as Range
#=>[[1, :red], [2, :green], [3, :blue], [4, :red], [5, :green], [6, :blue], [7, :red], [8, :green], [9, :blue], [10, :red]]

重点是这些方法返回一个 Enumerator,然后将其与常用的 Enumerable 方法(例如 select)结合使用映射注入等。

"How to create an infinite enumerable of Times?" talks about constructing and lazy iterators, but my favorite usage is wrapping an existing Enumerable with additional functionality (any enumerable, without needing to know what it really is, whether it's infinite or not etc).

A trivial example would be implementing the each_with_index method (or, more generally, with_index method):

module Enumerable
  def my_with_index
    Enumerator.new do |yielder|
      i = 0
      self.each do |e|
        yielder.yield e, i
        i += 1
      end
    end
  end

  def my_each_with_index
    self.my_with_index.each do |e, i|
      yield e, i
    end
  end
end

[:foo, :bar, :baz].my_each_with_index do |e,i|
  puts "#{i}: #{e}"
end
#=>0: foo
#=>1: bar
#=>2: baz

Extending to something not already implemented in the core library, such as cyclically assigning value from a given array to each enumerable element (say, for coloring table rows):

module Enumerable
  def with_cycle values
    Enumerator.new do |yielder|
      self.each do |e|
        v = values.shift
        yielder.yield e, v
        values.push v
      end
    end
  end
end

p (1..10).with_cycle([:red, :green, :blue]).to_a # works with any Enumerable, such as Range
#=>[[1, :red], [2, :green], [3, :blue], [4, :red], [5, :green], [6, :blue], [7, :red], [8, :green], [9, :blue], [10, :red]]

The whole point is that these methods return an Enumerator, which you then combine with the usual Enumerable methods, such as select, map, inject etc.

听风念你 2024-10-25 09:05:02

例如,您可以使用它来构建内联的 Rack 响应主体,而无需创建类。 Enumerator 也可以“由外向内”工作 - 您调用 Enumerator#each,它会调用枚举器上的 next 并按顺序返回每个值。例如,您可以使 Rack 响应正文返回数字序列:

run ->(env) {
  body = Enumerator.new do |y|
   9.times { |i| y.yield(i.to_s) }
  end
  [200, {'Content-Length' => '9'}, body]
}

For example you can use it to construct Rack response bodies inline, without creating classes. An Enumerator can also work "outside-in" - you call Enumerator#each which calls next on the enumerator and returns every value in sequence. For example, you can make a Rack response body returning a sequence of numbers:

run ->(env) {
  body = Enumerator.new do |y|
   9.times { |i| y.yield(i.to_s) }
  end
  [200, {'Content-Length' => '9'}, body]
}
心房的律动 2024-10-25 09:05:02

由于 Mladen 提到获得其他答案,我想我应该举一个我今天早些时候在编写一个应用程序时所做的事情的例子,该应用程序将从多个物理设备接收数据,分析数据并连接相关数据(我们从多个设备看到) 。这是一个长期运行的应用程序,如果我从不丢弃数据(例如,至少一天前没有更新),那么它会变得无限大。

在过去,我会做这样的事情:

delete_old_stuff if rand(300) == 0

并使用随机数来完成此操作。然而,这并不是纯粹的确定性。我知道它大约每 300 次评估(即秒)运行一次,但不会恰好每 300 次运行一次。

我之前写的内容如下所示:

counter = Enumerator.new do |y|
  a = (0..300)
  loop do
    a.each do |b|
      y.yield b
    end
    delete_old_stuff
  end
end

我可以将 delete_old_stuff if rand(300) == 0 替换为 counter.next

现在,我确信还有更多执行此操作的有效或预制方法,但由于您的问题和链接的问题而激发了使用 Enumerator::Yielder#yield ,这就是我想出的方法。

Since Mladen mentioned getting other answers, I thought I would give an example of something I just did earlier today while writing an application that will receive data from multiple physical devices, analyze the data, and connect related data (that we see from multiple devices). This is a long-running application, and if I never threw away data (say, at least a day old with no updates), then it would grow infinitely large.

In the past, I would have done something like this:

delete_old_stuff if rand(300) == 0

and accomplish this using random numbers. However, this is not purely deterministic. I know that it will run approximately once every 300 evaluations (i.e. seconds), but it won't be exactly once every 300 times.

What I wrote up earlier looks like this:

counter = Enumerator.new do |y|
  a = (0..300)
  loop do
    a.each do |b|
      y.yield b
    end
    delete_old_stuff
  end
end

and I can replace delete_old_stuff if rand(300) == 0 with counter.next

Now, I'm sure there is a more efficient or pre-made way of doing this, but being sparked to play with Enumerator::Yielder#yield by your question and the linked question, this is what I came up with.

浅紫色的梦幻 2024-10-25 09:05:02

当您想要枚举多个对象,但 flat_map 不合适,并且您希望将枚举与另一个操作链接起来时,它似乎很有用:

module Enumerable
  def count_by
    items_grouped_by_criteria = group_by {|object| yield object}
    counts = items_grouped_by_criteria.map{|key, array| [key, array.length]}
    Hash[counts]
  end
end

def calculate_letter_frequencies
  each_letter.count_by {|letter| letter}
end

def each_letter
  filenames = ["doc/Quickstart", "doc/Coding style"]
  # Joining the text of each file into a single string would be memory-intensive
  enumerator = Enumerator.new do |yielder|
    filenames.each do |filename|
      text = File.read(filename)
      text.chars.each {|letter| yielder.yield(letter)}
    end
  end
  enumerator
end

calculate_letter_frequencies

It seems to be useful when you have multiple objects you want to enumerate over, but flat_map isn't suitable, and you want to chain the enumeration with another action:

module Enumerable
  def count_by
    items_grouped_by_criteria = group_by {|object| yield object}
    counts = items_grouped_by_criteria.map{|key, array| [key, array.length]}
    Hash[counts]
  end
end

def calculate_letter_frequencies
  each_letter.count_by {|letter| letter}
end

def each_letter
  filenames = ["doc/Quickstart", "doc/Coding style"]
  # Joining the text of each file into a single string would be memory-intensive
  enumerator = Enumerator.new do |yielder|
    filenames.each do |filename|
      text = File.read(filename)
      text.chars.each {|letter| yielder.yield(letter)}
    end
  end
  enumerator
end

calculate_letter_frequencies
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文