为什么这会改变F#

时间:2017-12-07 20:16:17

标签: f# iterator lazy-evaluation memoization

我正在使用F#完成Project Euler难题(https://projecteuler.net/)。事实证明,许多谜题需要生成素数,这是一项昂贵的计算。

为了让我的生活更轻松,我想创建一个可以生成素数的模块,还可以将它们写入文本文件,这样我就可以在下次需要时查看它们。

这是我的主要生成器模块:

module Utilities

open System
open System.IO
open System.Reflection

module Primes =

    //This is where the cache file is
    let private cachePath = 
        Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) 
        + "\PrimeCache.txt"

    //Parses the file to an int list
    let private readCache : int list =
        let text = File.ReadAllText(cachePath).Trim()            
        text.Split([|','|]) 
        |> Seq.filter(fun w -> w |> String.length > 0)
        |> Seq.map(fun w -> Int32.Parse(w)) 
        |> Seq.toList

    //Writes an int list to the file
    let writeCache cache =
        let text = String.Join(",", cache |> Seq.map(fun n -> n.ToString()))
        File.WriteAllText(cachePath, text)

    //In-memory cache
    let mutable cache = []
    cache <- readCache

    let private appendElement element list = 
        element::(list |> List.rev) |> List.rev

    let private isPrimeInner (n : int) =
        if n < 2 then false
        else 
            let len = cache.Length
            //Factor can't be greater than the square root
            let factorLimit = Convert.ToInt32(Math.Ceiling(Math.Sqrt(Convert.ToDouble(n))))

            let mutable i = 0
            let mutable stop = false
            let mutable isPrime = true

            while stop = false do
                //Get the next element from cache
                let p = cache.[i]

                i <- i+1

                //Don't go past last index of cache
                if i >= len
                then stop <- true 
                else ()

                //Don't check primes > sqrt(n)
                if p > factorLimit
                then stop <- true
                else ()

                //If its divisible by any prime, its not a prime
                if n % p = 0
                then 
                    stop <- true
                    isPrime <- false
                else ()

            //Add primes to cache
            if isPrime
            then cache <- appendElement n cache
            else ()

            isPrime

    let isPrime (n : int) : bool =            
        let result = isPrimeInner n
        result

    let getPrimes : int seq =
        seq {
            for p in cache do 
                yield p

            let next = if cache.Length > 0 
                       then cache.[cache.Length-1]+1
                       else 2

            for n = next to Int32.MaxValue do
                if isPrimeInner n
                then yield n
                else ()
        }

    let saveCache : unit =
        writeCache cache

以下是我目前正在处理的问题的代码。 (实际问题是总计2,000,000以下的所有素数,但我现在测试1000):

module Problem10

let getAnswer =

    let limit = 1000

    let primes = Utilities.Primes.getPrimes
                |> Seq.takeWhile(fun p -> p < limit)

    let result = primes |> Seq.sum

    Utilities.Primes.saveCache

    result

这将给出正确答案,但会将空缓存文件保存回磁盘。如果我在调试器中逐步执行它,那么在saveCache内设置的任何断点之前,对isPrimeInner的调用是最先击中的事情之一。如果我将调用代码切换为:

module Problem10

let getAnswer =

    let limit = 1000

    let primes = Utilities.Primes.getPrimes
                |> Seq.takeWhile(fun p -> p < limit)

    let result = primes |> Seq.sum

    let x = Utilities.Primes.cache
    Utilities.Primes.writeCache x

    result

缓存将使用新的素数正确保存。

我在C#之前看到过这种行为,当时误用了迭代器块。我不明白这两个版本的调用代码在运行时是如何不同的。我在这里做错了什么?

0 个答案:

没有答案