How to write a classic high / low level game in F #?

I read in functional languages, and I wondered how to implement “attempts” in a functional language pure . So I decided to try to do it in F #

But I could not get half the basics. I could not understand how to use a random number, how to use return / continue (at first I thought that I was doing a verbose statement if it was wrong, but it seemed like I was doing it right), and I could not figure out how to print the number in F #, so I did it in the way of C #.

More difficult problems are the out parameter in tryparse, and I still don't know how I will implement tries without using a mutable variable. Perhaps some of you guys can tell me how I can implement this correctly.

C # code I had to do last week

 using System; namespace CS_Test { class Program { static void Main(string[] args) { var tries = 0; var answer = new Random().Next(1, 100); Console.WriteLine("Guess the number between 1 and 100"); while (true) { var v = Console.ReadLine(); if (v == "q") { Console.WriteLine("you have quit"); return; } int n; var b = Int32.TryParse(v, out n); if (b == false) { Console.WriteLine("This is not a number"); continue; } tries++; if (n == answer) { Console.WriteLine("Correct! You win!"); break; } else if (n < answer) Console.WriteLine("Guess higher"); else if (n > answer) Console.WriteLine("Guess lower"); } Console.WriteLine("You guess {0} times", tries); Console.WriteLine("Press enter to exist"); Console.ReadLine(); } } } 

The most broken and incorrect code F #

 open System; let main() = let tries = 0; let answer = (new Random()).Next 1, 100 printfn "Guess the number between 1 and 100" let dummyWhileTrue() = let v = Console.ReadLine() if v = "q" then printfn ("you have quit") //return printfn "blah" //let b = Int32.TryParse(v, out n) let b = true; let n = 3 if b = false then printfn ("This is not a number") //continue; //tries++ (* if n = answer then printfn ("Correct! You win!") //break; elif n < answer then printfn ("Guess higher") elif n>answer then printfn ("Guess lower") *) dummyWhileTrue() (Console.WriteLine("You guess {0} times", tries)) printfn ("Press enter to exist") Console.ReadLine() main() 
+7
f #
source share
4 answers

Welcome to F #!

Here is the work program; explanation below.

 open System let main() = let answer = (new Random()).Next(1, 100) printfn "Guess the number between 1 and 100" let rec dummyWhileTrue(tries) = let v = Console.ReadLine() if v = "q" then printfn "you have quit" 0 else printfn "blah" let mutable n = 0 let b = Int32.TryParse(v, &n) if b = false then printfn "This is not a number" dummyWhileTrue(tries) elif n = answer then printfn "Correct! You win!" tries elif n < answer then printfn "Guess higher" dummyWhileTrue(tries+1) else // n>answer printfn "Guess lower" dummyWhileTrue(tries+1) let tries = dummyWhileTrue(1) printfn "You guess %d times" tries printfn "Press enter to exit" Console.ReadLine() |> ignore main() 

A few things...

If you call methods with multiple arguments (e.g. Random.Next ), use parens around args ( .Next(1,100) ).

You seem to be working on a recursive function ( dummyWhileTrue ), and not in a while loop; the while loop will work too, but I saved it. Note that there is no break or continue in F #, so you should be a little structured with the contents of the if inside.

I changed your Console.WriteLine to printfn to show how to call it with an argument.

I showed a way to call TryParse , which is more like C #. First declare your variable (make it mutable, since TryParse will write to this place), and then use &n as an argument (in this context &n is like ref n or out n in C #), Also in F # you can do this:

 let b, n = Int32.TryParse(v) 

where F # allows you to omit the trailing out parameters and instead returns their value at the end of the tuple; it's just syntactic convenience.

Console.ReadLine returns a string that you do not need at the end of the program, so pass it to the ignore function to discard the value (and get rid of the warning about an unused string value).

+6
source share

Here is my trick, just for fun:

 open System let main() = let answer = (new Random()).Next(1, 100) printfn "Guess the number between 1 and 100" let rec TryLoop(tries) = let doneWith(t) = t let notDoneWith(s, t) = printfn s; TryLoop(t) match Console.ReadLine() with | "q" -> doneWith 0 | s -> match Int32.TryParse(s) with | true, v when v = answer -> doneWith(tries) | true, v when v < answer -> notDoneWith("Guess higher", tries + 1) | true, v when v > answer -> notDoneWith("Guess lower", tries + 1) | _ -> notDoneWith("This is not a number", tries) match TryLoop(1) with | 0 -> printfn "You quit, loser!" | tries -> printfn "Correct! You win!\nYou guessed %d times" tries printfn "Hit enter to exit" Console.ReadLine() |> ignore main() 

Notes:

  • Matching patterns is more beautiful, more concise, and, I think, more idiomatic than nested ifs
  • Uses the tuple-return-style TryParse style suggested by Brian
  • Renamed dummyWhileTrue to TryLoop , seemed more descriptive
  • Two internal functions doneWith and notDoneWith (for purely aesthetic reasons)
+6
source share

I canceled the correspondence of the main template from Evaluate in the @Huusom solution, but chose a recursive loop and battery instead of @Hussom (very cool), which discriminates the combination and use of Seq.unfold for a very compact solution.

 open System let guessLoop answer = let rec loop tries = let guess = Console.ReadLine() match Int32.TryParse(guess) with | true, v when v < answer -> printfn "Guess higher." ; loop (tries+1) | true, v when v > answer -> printfn "Guess lower." ; loop (tries+1) | true, v -> printfn "You won." ; tries+1 | false, _ when guess = "q" -> printfn "You quit." ; tries | false, _ -> printfn "Not a number." ; loop tries loop 0 let main() = printfn "Guess a number between 1 and 100." printfn "You guessed %i times" (guessLoop ((Random()).Next(1, 100))) 
+5
source share

Also for fun, if:

 open System type Result = | Match | Higher | Lower | Quit | NaN let Evaluate answer guess = match Int32.TryParse(guess) with | true, v when v < answer -> Higher | true, v when v > answer -> Lower | true, v -> Match | false, _ when guess = "q" -> Quit | false, _ -> NaN let Ask answer = match Evaluate answer (Console.ReadLine()) with | Match -> printfn "You won." None | Higher -> printfn "Guess higher." Some (Higher, answer) | Lower -> printfn "Guess lower." Some (Lower, answer) | Quit -> printfn "You quit." None | NaN -> printfn "This is not a number." Some (NaN, answer) let main () = printfn "Guess a number between 1 and 100." let guesses = Seq.unfold Ask ((Random()).Next(1, 100)) printfn "You guessed %i times" (Seq.length guesses) let _ = main() 

I use enumeration for state and Seq.unfold for input to find the result.

+3
source share

All Articles