F# 不变性、纯函数和副作用
F# Immutability, pure function and side effect
我是 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
let readlineInt () = ...
定义了一个函数。每次调用它时都会执行它的主体。在这种情况下,主体有副作用,并且每次执行主体时都会执行该副作用(从标准输入读取)。所以那不是你的问题。
readAllMountainsData
定义为包含七座山的数据的列表。每座山都有自己的高度(因为 readLineInt()
每座山都被调用一次)。此列表计算一次,之后不会更改。它不会在您每次使用 readAllMountainsData
时重新计算,因为它是一个变量,而不是一个函数(即使名称可能另有含义)。这似乎非常明智,因为每次都重新读取山区数据是没有意义的。
在定义中添加 mutable
关键字允许您重新分配变量。也就是说,它允许您稍后在程序中编写 readAllMountainsData <- someNewValue
来更改变量的值。由于您实际上从未这样做过,因此没有任何变化。
你的程序无限循环的原因是mainLoop
总是再次调用自己。它没有退出条件。因此,要解决这个问题,您应该决定要循环的频率/要退出的条件,然后相应地实现该逻辑。
在你的编辑中你澄清了,你确实想重新读取你的值,所以你只需要通过给它一个参数列表 (let readAllMountainsData () = ...
) 来使 readAllMountainsData
成为一个函数,然后将其称为函数。这样您将在每次迭代中获得新数据,但循环仍然是无限的,除非您添加退出条件。
我是 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
let readlineInt () = ...
定义了一个函数。每次调用它时都会执行它的主体。在这种情况下,主体有副作用,并且每次执行主体时都会执行该副作用(从标准输入读取)。所以那不是你的问题。
readAllMountainsData
定义为包含七座山的数据的列表。每座山都有自己的高度(因为 readLineInt()
每座山都被调用一次)。此列表计算一次,之后不会更改。它不会在您每次使用 readAllMountainsData
时重新计算,因为它是一个变量,而不是一个函数(即使名称可能另有含义)。这似乎非常明智,因为每次都重新读取山区数据是没有意义的。
在定义中添加 mutable
关键字允许您重新分配变量。也就是说,它允许您稍后在程序中编写 readAllMountainsData <- someNewValue
来更改变量的值。由于您实际上从未这样做过,因此没有任何变化。
你的程序无限循环的原因是mainLoop
总是再次调用自己。它没有退出条件。因此,要解决这个问题,您应该决定要循环的频率/要退出的条件,然后相应地实现该逻辑。
在你的编辑中你澄清了,你确实想重新读取你的值,所以你只需要通过给它一个参数列表 (let readAllMountainsData () = ...
) 来使 readAllMountainsData
成为一个函数,然后将其称为函数。这样您将在每次迭代中获得新数据,但循环仍然是无限的,除非您添加退出条件。