2012-01-16 20 views
1

我有一系列质数除数,我想为每个总理候选人进行迭代。我使用GetEnumerator()MoveNext()和Current。我无法重新初始化枚举器从头开始。我尝试了Reset(),它编译了,但是给出了未实现的运行时错误。F#:想要重新初始化枚举器

我使用F#2.0互动打造4.0.40219.1

有什么建议?

问候, 道格


为了阐明该问题:对于每一个素数候补NI想要遍历通的素因子序列(高达约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 
+2

我会发现当你只需要使用'for x in y'语法时,实际上需要在'IEnumerator'上显式调用成员是非常罕见的。如果你需要;只需创建一个新的枚举器。发布你的代码会很有帮助,因为我认为我们可以找到更好的选择。 – vcsjones

+1

你能举一个具体的例子来证明这个错误吗? – pad

+0

您是否尝试过使用Seq.cache? – Huusom

回答

3

如果你的意思,你尝试重置枚举器的原因是再生的素数序列的成本很高,你可以考虑caching您的序列。这种使用你的序列的方式对F#来说是惯用的。为了向你展示如何做到这一点我是指你从this context采取以下片段:

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 

您可以在这个片段中发挥地看到,重列举的处罚这里变得可以忽略不计。

谈到Reset()方法IEnumerator,我记得它没有在当前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) 

在我的笔记本电脑测试的执行需要几为3ms。

+0

感谢您的所有意见。看起来,为了重置枚举数(因此它从一个序列的开头开始),你必须创建一个新的枚举器(这对于测试大量素数候选者似乎很尴尬),或者创建一个新序列(我不想做)。 – DougT

+0

我用“回答你的问题”并发送了它,但它还没有出现。 – DougT

+1

@DougT:这是对的,重置一个枚举器就相当于创建一个新的枚举器;后者在F#中是正常的和惯用的。使用兑现顺序可以让您在此过程中不付出任何性能损失。你可以用'fsi'上面的代码来自己看看。 –