【问题标题】:F#: Want to reinitialize enumeratorF#:想要重新初始化枚举器
【发布时间】:2012-01-17 23:35:06
【问题描述】:

我有一系列素数除数,我想为每个素数候选者迭代。我使用 GetEnumerator() MoveNext() 和 Current。我无法重新初始化枚举器以从头开始。我尝试了 Reset(),它已编译,但运行时错误未实现。

我正在使用 F# 2.0 Interactive build 4.0.40219.1

有什么建议吗?

问候, 道格


为了澄清问题:对于每个素数候选者 N,我想遍历素数除数序列(最多约 sqrt N)并完全分解 N 或确定它是否为素数。使用 GetEnumerator、MoveNext、Current 方法适用于第一个主要候选人,但在第二个主要候选人上,我想从头开始迭代我的除数序列。看来,这样做的唯一方法是创建一个新的迭代器(这对于大量素数候选者来说很尴尬)或创建一个新的素数序列(我不想这样做)。

使用“seqPrimes 中的除数”之类的建议似乎在停止之前用尽了所有除数,但我想在素数除数除以主要候选人时立即停止。

如果上述陈述中我的逻辑有错误,请告诉我。

我调查了 Seq.cache,这对我有用。结果代码如下:


// Recursive isprime function (modified from MSDN)
let isPrime n =
    let rec check i =
        i > n/2 || (n % i <> 0 && check (i + 2))
    if n = 2 then true
    elif (n%2) = 0 then false
    else check 3

let seqPrimes = seq { for n in 2 .. 100000 do if isPrime n then yield n }

// Cache the sequence to avoid recomputing the sequence elements.
let cachedSeq = Seq.cache seqPrimes


// find the divisors of n (or determine prime) using the seqEnum enumerator 
let rec testPrime n (seqEnum:System.Collections.Generic.IEnumerator<int>) =
  if n = 1 then printfn "completely factored"
  else
    let nref = ref n
    if seqEnum.MoveNext() then
      let divisor = seqEnum.Current
      //printfn "trial divisor %A" divisor
      if divisor*divisor > n then printfn "prime %A" !nref
      else
        while ((!nref % divisor) = 0) do
          printfn "divisor %A" divisor
          nref := !nref / divisor
        testPrime !nref seqEnum

// test
for x = 1000000 to 1000010 do
  printfn "\ndivisors of %d = " x
  let seqEnum = cachedSeq.GetEnumerator()
  testPrime x seqEnum
  seqEnum.Dispose()   // not needed

【问题讨论】:

  • 当您可以只使用for x in y 语法时,我会发现实际上需要在IEnumerator 上显式调用成员是非常罕见的。如果你需要;只需创建一个新的枚举器。发布您的代码会很有帮助,因为我认为我们可以找到更好的替代方案。
  • 你能举一个具体的例子来说明这个错误吗?
  • 你试过使用 Seq.cache 吗?

标签: f# ienumerable


【解决方案1】:

如果您的意思是您尝试重置枚举器的原因是重新生成素数序列的高成本,您可以考虑caching 您的序列。这种使用序列的方式对于 F# 来说是惯用的。为了向您展示如何做到这一点,我建议您参考以下来自 this context 的 sn-p:

let rec primes = 
    Seq.cache <| seq { yield 2; yield! Seq.unfold nextPrime 3 }
and nextPrime n =
    if isPrime n then Some(n, n + 2) else nextPrime(n + 2)
and isPrime n =
    if n >= 2 then
        primes 
        |> Seq.tryFind (fun x -> n % x = 0 || x * x > n)
        |> fun x -> x.Value * x.Value > n
    else false

您可以使用这个 sn-p 来查看这里重新枚举的惩罚可以忽略不计。

谈到IEnumeratorReset()方法,我记得在当前的F#中没有实现它,即抛出System.NotSupportedException。有关理由,请参阅MSDN reference

补充: 为了使用您在下面建议的测试对其进行测试:

for x in [1000000..1000010] do
    printfn "\ndivisors of %d" x
    primes
    |> Seq.takeWhile ((>) (int(sqrt(float x))))
    |> Seq.iter (fun n -> if x%n = 0 then printf "%d " n)

在我的笔记本电脑上执行测试只需要 3 毫秒。

【讨论】:

  • 感谢所有 cmets。似乎要重置枚举器(因此它从序列的开头开始),您必须创建一个新的枚举器(这对于测试大量素数候选者来说似乎很尴尬)或创建一个新的序列(我没有想做)。
  • 我用了“回答你的问题”发了,但是还没出现。
  • @DougT:没错,重置一个枚举器相当于创建一个新的枚举器;后者在 F# 中是正常且惯用的。使用兑现序列将使您无需为此操作过程支付性能损失。您可以在fsi 中使用上面的代码来亲自查看。
  • 基因:谢谢你的帮助。 -- 道格
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多