F#不变性,纯粹的功能和副作用

时间:2017-07-27 20:53:51

标签: f# immutability purely-functional

我是F#的新手,我正在编写很少的挑战来学习有关该语言的细节。我认为因为不变性而有问题。

方案: 我必须在控制台中读取高度线,每行包含一个整数。该整数表示山的大小。 阅读输入后我需要写出最高山脉的行号。 如果给出的指数是最高的山峰,那么大小设置为零,否则我会松动。 重复该场景,直到所有山脉的大小都设置为零。

这里是我写的代码:

open System

type Mountain = {Id:int; Height:int}

let readlineInt() = int(Console.In.ReadLine())
let readMountainData id = {Id = id; Height = readlineInt()}
let readAllMountainsData = [ for a in 0 .. 7 do yield readMountainData a ]

let rec mainLoop () =
     let mountains = readAllMountainsData
     let highestMountain = mountains |> List.maxBy (fun x -> x.Height)

     printfn "%i" highestMountain.Id
     mainLoop()

mainLoop()

这段代码将无限循环,我相信这是因为

let readlineInt() = int(Console.In.ReadLine())

是不可变的,因此该值设置一次,之后永远不会再次停止读取该行。我尝试为

添加'mutable'关键字
let mutable readAllMountainsData = [ for a in 0 .. 7 do yield readMountainData a ]

但它没有改变一件事。 你有什么想法吗?

编辑: 我知道这段代码进入了一个无限循环,因为在将主页循环中的日志记录添加后如下:

let rec mainLoop () =
     let mountains = readAllMountainsData
     Console.Error.WriteLine("Mountain Count:{0} ", mountains.Length)
     mountains |> List.iter (fun x -> Console.Error.WriteLine("Mountain Id:{0} Height:{1}", x.Id, x.Height))
     let highestMountain = mountains |> List.maxBy (fun x -> x.Height)

     printfn "%i" highestMountain.Id
     mainLoop()

然后我在输出中有这个:

Standard Error Stream:

Mountain Count:8 
Mountain Id:0 Height:9
Mountain Id:1 Height:8
Mountain Id:2 Height:7
Mountain Id:3 Height:6
Mountain Id:4 Height:5
Mountain Id:5 Height:4
Mountain Id:6 Height:3
Mountain Id:7 Height:2
Mountain Count:8 
Mountain Id:0 Height:9
Mountain Id:1 Height:8
Mountain Id:2 Height:7
Mountain Id:3 Height:6
Mountain Id:4 Height:5
Mountain Id:5 Height:4
Mountain Id:6 Height:3
Mountain Id:7 Height:2
Mountain Count:8 
Mountain Id:0 Height:9
Mountain Id:1 Height:8
Mountain Id:2 Height:7
etc...

为什么我要重读这个值?因为值是由外部源提供的。所以工作流程如下:

Loop one:
I read 8 values for the height of the mountains in the console
I output the value of the highest mountain

Loop two:
I read 8 values for the height of the mountains in the console
I output the value of the highest mountain

Loop three:
I read 8 values for the height of the mountains in the console
I output the value of the highest mountain

etc

1 个答案:

答案 0 :(得分:5)

let readlineInt () = ...定义了一个函数。每当你打电话给它时,它的身体就会被执行。在这种情况下,身体具有副作用,并且每次执行身体时都会执行副作用(从stdin读取)。所以这不是你的问题。

readAllMountainsData被定义为包含七座山脉数据的列表。这些山脉中的每一座都有自己的高度(因为每座山都会召唤一次readLineInt())。此列表计算一次,之后不会更改。每次使用readAllMountainsData时都不会重新计算它,因为它是变量,而不是函数(即使名称可能暗示其他情况)。这似乎是非常合理的,因为每次重读山地数据都没有意义。

mutable关键字添加到定义中可以重新分配变量。也就是说,它允许您稍后在程序中编写readAllMountainsData <- someNewValue来更改变量的值。既然你从未真正做到这一点,那就什么都没有改变。

你的程序无限循环的原因是mainLoop总是再次调用自己。它没有退出条件。因此,为了解决这个问题,您应该决定循环/在哪种情况下要退出的频率,然后相应地实现该逻辑。

在您的编辑中,您澄清了,您确实想要重新读取您的值,因此您只需要通过为其提供参数列表(readAllMountainsData)来使let readAllMountainsData () = ...成为函数,然后调用它作为一个功能。通过这种方式,您可以获得每次迭代的新数据,但除非您添加退出条件,否则循环仍将是无限的。