12

昨天我开始在空闲时间看 F#。我想我会从打印出所有质数到 100 的标准问题开始。这就是我想出的……

#light
open System

let mutable divisable = false
let mutable j = 2

for i = 2 to 100 do
    j <- 2
    while j < i do
        if i % j = 0 then divisable <- true
        j <- j + 1

    if divisable = false then Console.WriteLine(i)
    divisable <- false

问题是我觉得我是从 C/C# 的角度来处理这个问题的,并没有接受真正的函数式语言方面。

我想知道其他人能想出什么 - 以及是否有人有任何提示/指针/建议。我觉得目前在网络上很难找到好的 F# 内容,而我接触的最后一个函数式语言是大约 5 年前在大学时的HOPE 。

4

7 回答 7

9

下面是F#中埃拉托色尼筛法的简单实现:

let rec sieve = function
    | (p::xs) -> p :: sieve [ for x in xs do if x % p > 0 then yield x ]
    | []      -> []

let primes = sieve [2..50]
printfn "%A" primes  // [2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47]

此实现不适用于非常大的列表,但它说明了功能解决方案的优雅。

于 2009-07-08T11:52:17.453 回答
3

使用像Eratosthenes这样的 Sieve 函数是一个不错的方法。函数式语言在列表上工作得非常好,所以我会从结构上考虑到这一点。

另一方面,函数式语言可以很好地由函数构成(呵呵)。对于函数式语言“感觉”,我会构建一个 Sieve 函数,然后调用它来打印素数。您甚至可以将其拆分——一个函数构建列表并完成所有工作,一个函数通过并完成所有打印,巧妙地分离功能。

这里有几个有趣的版本。并且有其他类似语言的众所周知的实现。 这是OCAML 中的一个优于 C 中的一个。

于 2009-07-08T11:36:00.950 回答
3

这是我的两分钱:

let rec primes = 
  seq { 
    yield 2
    yield! (Seq.unfold (fun i -> Some(i, i + 2)) 3) 
           |> Seq.filter (fun p -> 
                primes
                |> Seq.takeWhile (fun i -> i * i <= p)
                |> Seq.forall (fun i -> p % i <> 0))
  }
  for i in primes do
    printf "%d " i

或者也许这个更清晰的版本isprime被定义为一个单独的函数:

let rec isprime x =
    primes
    |> Seq.takeWhile (fun i -> i*i <= x)
    |> Seq.forall (fun i -> x%i <> 0)

and primes = 
    seq {
        yield 2
        yield! (Seq.unfold (fun i -> Some(i,i+2)) 3)
                |> Seq.filter isprime
    }
于 2016-03-13T04:00:15.767 回答
2

你肯定不想从这个例子中学习,但是我写了一个基于消息传递的 NewSqueak 筛子的 F# 实现:

type 'a seqMsg =   
    | Die   
    | Next of AsyncReplyChannel<'a>   

type primes() =   
    let counter(init) =   
        MailboxProcessor.Start(fun inbox ->   
            let rec loop n =   
                async { let! msg = inbox.Receive()   
                        match msg with   
                        | Die -> return ()   
                        | Next(reply) ->   
                            reply.Reply(n)   
                            return! loop(n + 1) }   
            loop init)   

    let filter(c : MailboxProcessor<'a seqMsg>, pred) =   
        MailboxProcessor.Start(fun inbox ->   
            let rec loop() =   
                async {   
                    let! msg = inbox.Receive()   
                    match msg with   
                    | Die ->   
                        c.Post(Die)   
                        return()   
                    | Next(reply) ->   
                        let rec filter' n =   
                            if pred n then async { return n }   
                            else  
                                async {let! m = c.AsyncPostAndReply(Next)   
                                       return! filter' m }   
                        let! testItem = c.AsyncPostAndReply(Next)   
                        let! filteredItem = filter' testItem   
                        reply.Reply(filteredItem)   
                        return! loop()   
                }   
            loop()   
        )   

    let processor = MailboxProcessor.Start(fun inbox ->   
        let rec loop (oldFilter : MailboxProcessor<int seqMsg>) prime =   
            async {   
                let! msg = inbox.Receive()   
                match msg with   
                | Die ->   
                    oldFilter.Post(Die)   
                    return()   
                | Next(reply) ->   
                    reply.Reply(prime)   
                    let newFilter = filter(oldFilter, (fun x -> x % prime <> 0))   
                    let! newPrime = oldFilter.AsyncPostAndReply(Next)   
                    return! loop newFilter newPrime   
            }   
        loop (counter(3)) 2)   

    member this.Next() = processor.PostAndReply( (fun reply -> Next(reply)), timeout = 2000)

    interface System.IDisposable with
        member this.Dispose() = processor.Post(Die)

    static member upto max =   
        [ use p = new primes()
          let lastPrime = ref (p.Next())
          while !lastPrime <= max do
            yield !lastPrime
            lastPrime := p.Next() ]

它有效吗?

> let p = new primes();;

val p : primes

> p.Next();;
val it : int = 2
> p.Next();;
val it : int = 3
> p.Next();;
val it : int = 5
> p.Next();;
val it : int = 7
> p.Next();;
val it : int = 11
> p.Next();;
val it : int = 13
> p.Next();;
val it : int = 17
> primes.upto 100;;
val it : int list
= [2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47; 53; 59; 61; 67; 71;
   73; 79; 83; 89; 97]

甜的!:)

于 2009-07-08T15:20:45.093 回答
1

简单但低效的建议:

  • 创建一个函数来测试单个数字是否为素数
  • 为 2 到 100 的数字创建一个列表
  • 按功能过滤列表
  • 用另一个函数组合结果以打印出结果

为了提高效率,你真的想通过检查它是否可以被任何较低的素数整除来测试一个数字是否为素数,这将需要记忆。可能最好等到您首先使用简单的版本 :)

如果这还不够暗示,请告诉我,我会想出一个完整的例子——我想可能要到今晚才会……

于 2009-07-08T11:17:14.387 回答
1

这是我在 HubFS关于使用递归序列实现素数生成器的旧帖子。

如果您想要快速实施,Markus Mottl 提供了不错的 OCaml 代码

PS,如果您想将素数迭代到 10^20,您真的想将 DJ Bernstein 的素数移植到 F#/OCaml :)

于 2009-07-21T16:20:41.990 回答
1

在解决同样的问题时,我在 F# 中实现了阿特金斯筛。它是最有效的现代算法之一。

// Create sieve
let initSieve topCandidate =
    let result = Array.zeroCreate<bool> (topCandidate + 1)
    Array.set result 2 true
    Array.set result 3 true
    Array.set result 5 true
    result
// Remove squares of primes
let removeSquares sieve topCandidate =
    let squares =
        seq { 7 .. topCandidate}
            |> Seq.filter (fun n -> Array.get sieve n)
            |> Seq.map (fun n -> n * n)
            |> Seq.takeWhile (fun n -> n <= topCandidate)
    for n2 in squares do
        n2
            |> Seq.unfold (fun state -> Some(state, state + n2))
            |> Seq.takeWhile (fun x -> x <= topCandidate)
            |> Seq.iter (fun x -> Array.set sieve x false)
    sieve

// Pick the primes and return as an Array
let pickPrimes sieve =
    sieve
        |> Array.mapi (fun i t -> if t then Some i else None)
        |> Array.choose (fun t -> t)
// Flip solutions of the first equation
let doFirst sieve topCandidate =
    let set1 = Set.ofList [1; 13; 17; 29; 37; 41; 49; 53]
    let mutable x = 1
    let mutable y = 1
    let mutable go = true
    let mutable x2 = 4 * x * x
    while go do
        let n = x2 + y*y
        if n <= topCandidate then
            if Set.contains (n % 60) set1 then
                Array.get sieve n |> not |> Array.set sieve n

            y <- y + 2
        else
            y <- 1
            x <- x + 1
            x2 <- 4 * x * x
            if topCandidate < x2 + 1 then
                go <- false
// Flip solutions of the second equation
let doSecond sieve topCandidate =
    let set2 = Set.ofList [7; 19; 31; 43]
    let mutable x = 1
    let mutable y = 2
    let mutable go = true
    let mutable x2 = 3 * x * x
    while go do
        let n = x2 + y*y
        if n <= topCandidate then
            if Set.contains (n % 60) set2 then
                Array.get sieve n |> not |> Array.set sieve n

            y <- y + 2
        else
            y <- 2
            x <- x + 2
            x2 <- 3 * x * x
            if topCandidate < x2 + 4 then
                go <- false
// Flip solutions of the third equation
let doThird sieve topCandidate =
    let set3 = Set.ofList [11; 23; 47; 59]
    let mutable x = 2
    let mutable y = x - 1
    let mutable go = true
    let mutable x2 = 3 * x * x
    while go do
        let n = x2 - y*y
        if n <= topCandidate && 0 < y then
            if Set.contains (n % 60) set3 then
                Array.get sieve n |> not |> Array.set sieve n

            y <- y - 2
        else
            x <- x + 1
            y <- x - 1
            x2 <- 3 * x * x
            if topCandidate < x2 - y*y then
                go <- false

// Sieve of Atkin
let ListAtkin (topCandidate : int) =
    let sieve = initSieve topCandidate

    [async { doFirst sieve topCandidate }
     async { doSecond sieve topCandidate }
     async { doThird sieve topCandidate }]
        |> Async.Parallel
        |> Async.RunSynchronously
        |> ignore

    removeSquares sieve topCandidate |> pickPrimes

我知道有些人不建议使用并行异步,但它确实提高了我的 2 核(4 带超线程)i5 的速度约 20%。这与我使用 TPL 获得的增长大致相同。

我曾尝试以函数方式重写它,读取循环和可变变量,但性能下降了 3-4 倍,因此决定保留此版本。

于 2015-10-27T16:30:43.173 回答