I'm new to F# and I'm coding little challenges to learn the nitty-gritty details about the language. I think I have a problem because of immutability.
Scenario: I have to read height lines in the console, each line contains one integer. That integer represent the size of a mountain. After reading the input i need to write the line number of the highest mountains. If the index given is the highest mountain then the size is set to zero else I loose. Repeat the scenario until all mountains have their size set to zero.
Here the code I wrote:
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()
This code is going to an infinite loop, I believe it's because the
let readlineInt() = int(Console.In.ReadLine())
is immutable, so the value is set once and after it's never stop again to read the line. I try to put 'mutable' keyword for
let mutable readAllMountainsData = [ for a in 0 .. 7 do yield readMountainData a ]
But it didn't change a thing. Do you have any idea?
Edit: I know that this code is going into an infinite loop because after adding logging into the main loop as follow:
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()
Then I have this in the output:
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...
Why do I want to reread the value? Because the values are provided by an external source. So the workflow is as follow:
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 () = ...
defines a function. It's body will be executed every time you call it. And in this case the body has a side-effect and that side-effect (reading from stdin) will be executed every time the body is executed. So that's not your problem.
readAllMountainsData
is defined to be a list containing the data of seven mountains. Each of those mountains will have its own height (because readLineInt()
is called once for each mountain). This list is calculated once and does not change after that. It is not re-calculated every time you use readAllMountainsData
as it is a variable, not a function (even though the name might suggest otherwise). That seems perfectly sensible as re-reading the mountain data every time would make no sense.
Adding the mutable
keyword to the definition allows you to re-assign the variable. That is, it allows you to write readAllMountainsData <- someNewValue
later in the program to change the variable's value. Since you never actually do that, nothing changes.
The reason that your program loops infinitely is that mainLoop
always calls itself again. It has no exit condition. So to fix that you should decide how often you want to loop / under which condition you want to exit, and then implement that logic accordingly.
In your edit you clarified, that you do want to re-read your values, so you simply need to make readAllMountainsData
a function by giving it a parameter list (let readAllMountainsData () = ...
) and then call it as a function. This way you'll get new data on each iteration, but the loop will still be infinite unless you add an exit condition.