2014-09-11 25 views
2

从面向对象的背景来看,我很难包装我的头,试图避免突变时如何解决FP的简单问题。在这个例子中避免突变在F#

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#中的变异的上述w/o的例子吗?

最终的结果应该是player1List继续增长,直到物品的长度6,然后退出,并打印“全部完成”

+1

顺便说一句:你的代码实际上不工作。你需要'run < - false',并且'addValue'应该改变'player1List'并存储新的值(否则你只是打印它然后忽略它)。 – 2014-09-11 02:12:53

+0

谢谢,这是一种类型,我改为< - ... addValue的意思是这样的例子,因为我的目标是避免突变。 – schmoopy 2014-09-11 02:17:54

回答

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#中,=始终用于比较。编译器实际上对此提出警告。

+0

哈哈,是啊,那个= vs < - 只是一个错字,因为我的快速原型,因为我从来没有达到过这条线,我从来没有看到失败。 非常感谢您的回答 – schmoopy 2014-09-11 02:11:10

1

我会从比赛疏通管道的可读性,但使用它的最后一个表达式,以避免额外的括号:

open System 

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

makelist [] 
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

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

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