3

来自 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继续增长,直到item的长度为6,然后退出并打印'all done'

4

4 回答 4

7

最简单的方法是使用递归

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

makelist []

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

run = false对于您在需要时使用的新 F# 编码器,您的原始代码也存在一个常见问题run <- false。在 F# 中,=总是用于比较。编译器确实对此发出警告。

于 2014-09-11T01:43:26.623 回答
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 个元素。

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

于 2014-09-11T02:12:07.980 回答
2

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

[1..6] |> List.fold (fun acc _ -> Console.ReadLine()::acc) []
于 2014-09-11T02:14:57.357 回答
1

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

open System

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

makelist []
于 2014-09-11T01:59:19.270 回答