使用Enumerator :: Lazy(Ruby)过滤素数

时间:2016-03-27 19:36:26

标签: ruby generator lazy-evaluation enumerator

为了更好地理解Ruby Fibers和Enumerators,我编写了一个小程序来生成前10个素数回文。 (我没有实现Eratosthenes的筛子或使用Ruby的Prime模块;我想专注于生成器和过滤器。)

我的第一个版本使用光纤来生成素数,并使用枚举器来过滤回文:

module PrimeSeries
  extend self

  def generator
    Fiber.new do
      Fiber.yield(2)
      3.step(by: 2) { |n| Fiber.yield(n) if prime?(n) }
    end
  end

  def prime?(n)
    return false if n < 2
    return false if n > 2 && n.even?

    sqrt = Math.sqrt(n).ceil
    (3..sqrt).step(2).none? { |i| n % i == 0 }
  end
end

module Palindrome
  extend self

  def filter(generator)
    Enumerator.new do |yielder|
      loop do
        n = generator.resume
        yielder.yield(n) if palindrome?(n)
      end
    end
  end

  def palindrome?(n)
    str = n.to_s
    str == str.reverse
  end
end

primes = PrimeSeries.generator
filter = Palindrome.filter(primes)

p filter.take(10)
# => [2, 3, 5, 7, 11, 101, 131, 151, 181, 191]

真正想要的是忽略琐碎的一位数的回文。为了这样称呼它,

filter.select { |n| n.to_s.size > 1 }.take(10)

我必须将我的生成器更改为枚举器才能利用 Enumerator :: Lazy

module PrimeSeries
  # ...
  def generator
    Enumerator.new do |yielder|
      yielder.yield(2)
      3.step(by: 2) { |n| yielder.yield(n) if prime?(n) }
    end
  end
  # ...
end

module Palindrome
  # changed "generator.resume" to "generator.next"
  # otherwise the same as previous version
end

primes = PrimeSeries.generator
filter = Palindrome.filter(primes).lazy

p filter.select { |n| n.to_s.size > 1 }.take(10).force
# => [11, 101, 131, 151, 181, 191, 313, 353, 373, 383]

起初,我在调用结束时没有.force,而我只是得到了一个懒惰的枚举器(将延迟枚举器链接到素数,过滤,选择和获取的结果)。< / p>

虽然我认为我理解它的必要性,但我找不到force方法的任何文档(看起来它是to_a的别名?)或者你做的情​​况或者不必使用它。我很好奇是否有需要致电force才能评估您的最终结果。

0 个答案:

没有答案