在F#的这个例子中避免变异

时间:2014-09-11 01:28:42

标签: f# functional-programming mutation

来自OO背景,我无法解决如何在尝试避免变异时解决FP的简单问题。

let mutable run = true
let player1List = ["he"; "ho"; "ha"]

let addValue lst value =
    value :: lst

while run do
    let input = Console.ReadLine()
    addValue player1List input |> printfn "%A"
    if player1List.Length > 5 then 
        run <- false
        printfn "all done" // daz never gunna happen

我知道在某些情况下使用突变是可以的,但我正在努力训练自己避免突变作为默认值。有了这个说,有人可以告诉我一个上面没有使用F#突变的例子吗?

最终结果应该是player1List继续增长,直到项目的长度为6,然后退出并打印所有已完成&#39;

4 个答案:

答案 0 :(得分:7)

最简单的方法是使用递归

open System
let rec makelist l = 
    match l |> List.length with
    |6  -> printfn "all done"; l
    | _ -> makelist ((Console.ReadLine())::l)

makelist []

我还删除了一些addValue函数,因为在典型的F#代码中使用::更加惯用。

您希望run = false使用run <- false的新F#编码器时,您的原始代码也存在常见问题。在F#中,=总是用于比较。编译器确实警告过这个。

答案 1 :(得分:4)

正如其他人已经解释的那样,您可以使用递归重写命令式循环。这很有用,因为它是一种始终有效且对函数式编程非常重要的方法。

或者,F#提供了一组丰富的库函数来处理集合,这些函数实际上可以很好地表达您需要的逻辑。所以,你可以这样写:

let player1List = ["he"; "ho"; "ha"]
let player2List = Seq.initInfinite (fun _ -> Console.ReadLine())
let listOf6 = Seq.append player1List list2 |> Seq.take 6 |> List.ofSeq 

这里的想法是你创建一个无限的懒惰序列,从控制台读取输入,将它附加在初始player1List的末尾,然后取出前6个元素。

根据你的实际逻辑,你可能会有所不同,但好处是这可能更接近你想要实现的逻辑......

答案 2 :(得分:2)

在F#中,我们使用递归来进行循环。但是,如果您知道需要迭代多少次,可以使用F#List.fold来隐藏递归实现。

[1..6] |> List.fold (fun acc _ -> Console.ReadLine()::acc) []

答案 3 :(得分:1)

为了便于阅读,我会从匹配中删除管道,但在最后一个表达式中使用它以避免使用额外的括号:

open System

let rec makelist l = 
    match List.length l with
    | 6 -> printfn "all done"; l
    | _ -> Console.ReadLine()::l |> makelist

makelist []