// Learn more about F# at http://fsharp.org
// See the 'F# Tutorial' project for more help.
open System
let highLowGame () =
let rng = new Random()
let secretNumber = rng.Next() % 100 + 1
let rec highLowGameStep () =
printfn "Guess the secret number: "
let guessStr = Console.ReadLine()
let guess = Int32.Parse(guessStr)
match guess with
| _ when guess > secretNumber
-> (printfn "The secret number is lower")
highLowGameStep ()
| _ when guess = secretNumber
-> (printfn "You've guessed correctly!")
()
| _ when guess < secretNumber
-> (printfn "The secret number is higher")
highLowGameStep ()
highLowGameStep ()
[<EntryPoint>]
let main argv =
printfn "%A" argv
0 // return an integer exit code
I've checked this code like a thousand times and I get a warning saying guess is incomplete on pattern matching which is definitely impossible... and I also get an error saying the value is not a function and cannot be applied.
I copied this straight out of a book so I don't get how this is not compiling?!
The white space on the pattern match is wrong. It should be something like this:
match guess with
| _ when guess > secretNumber ->
(printfn "The secret number is lower")
highLowGameStep ()
| _ when guess = secretNumber ->
(printfn "You've guessed correctly!")
()
| _ when guess < secretNumber ->
(printfn "The secret number is higher")
highLowGameStep ()
That fixes the compilation error. As for the warning, the compiler is making a best guess, but it isn't able to determine that all cases are actually covered. If you want to fix the warning, you can add another case:
| _ -> invalidOp("Invalid input")
If you add semicolons after the printfn statements, it will work. I believe the reason it hadn't been compiling is because you were trying to return a printfn and another function (highLowGameStep) from your match.
Related
F# 6.0.3
I have seen some solutions on Google that are close to what I need; but being a Newbie I can't quite get how to use bind and map to get the solution.
I have many working procedures of the following format:
Example #1:
let saveAllDiagnosis =
let savealldiagnosis = match m.Encounter with
| None -> failwith "No encounter found"
| Some e -> match e.EncounterId with
| None -> failwith "No Encounter id found"
| Some id -> m.AllDiagnosisList
|> List.iter ( fun dx -> match dx.Key with
| None -> ()
| Some k -> Async.RunSynchronously (editAllDiagnosisInPreviousEncountersAsync id dx))
savealldiagnosis
Example #2
let saveEncounterDiagnosis =
let savedx = match m.Encounter with
| None -> failwith "No encounter found"
| Some e -> match e.EncounterId with
| None -> failwith "No Encounter id found"
| Some id -> m.BillingDiagnosisList |> List.iter ( fun dx -> Async.RunSynchronously (saveDxAsync id dx))
savedx
As can be seen, these are nested methods with almost identical behavior--differing only in the async procedure being called and the initializing list. What I would like to do is something along the lines of:
let runProcedures (fn: Model->Async) Model = ????
That is, a single procedue that encapsulates everything except the Async method and it's parameters but manages all the "None"s in a better way.
I hope my intent is clear.
TIA
If you are happy with using exceptions, then you do not even need railway-oriented programming (ROP). ROP is useful for more complex validation tasks, but I think exceptions are often perfectly reasonable and easy way of handling errors. In your case, you could define a helper that extracts a value of option<'T> or fails with a given error message:
let orFailWith msg opt =
match opt with
| Some v -> v
| None -> failwithf "%s" msg
Using this, you can then rewrite your code as follows:
let saveAllDiagnosis =
let e = m.Encounter |> orFailWith "No encounter found"
let id = e.EncounterId |> orFailWith "No Encounter id found"
for dx in m.AllDiagnosisList do
dx.Key |> Option.iter (fun k ->
editAllDiagnosisInPreviousEncountersAsync id dx |> Async.RunSynchronously)
let saveEncounterDiagnosis =
let e = m.Encounter |> orFailWith "No encounter found"
let id = e.EncounterId |> orFailWith "No Encounter id found"
for dx in m.BillingDiagnosisList do
saveDxAsync id dx |> Async.RunSynchronously
As I do not know the broader context of this, it is hard to say more - your code is imperative, but that may be perfectly fine if you are following the sandwich pattern.
Using mentioned ROP code can be rewritten as such. Result is used to track error and throw it at the end of pipeline. With current design is possible to avoid exceptions by just logging error instead of throwing at before last line.
type Encounter = { EncounterId : int option }
type Diagnostic = { Key : int option }
type Thing = {
Encounter : Encounter option
AllDiagnosisList : Diagnostic list
}
let editAllDiagnosisInPreviousEncountersAsync id diag = async { return () }
module Result =
let ofOption err opt =
match opt with
| Some v -> Ok v
| None -> Error err
let join res =
match res with
| Error v
| Ok v -> v
let saveAllDiagnosis m =
m.Encounter
|> Result.ofOption "No encounter found" // get value from option or log error
|> Result.map (fun e -> e.EncounterId)
|> Result.bind (Result.ofOption "No Encounter id found") // get EncounterId or log error
|> Result.map (fun id -> (
m.AllDiagnosisList
|> Seq.where (fun dx -> dx.Key.IsSome)
|> Seq.iter (fun dx -> Async.RunSynchronously (editAllDiagnosisInPreviousEncountersAsync id dx))
))
|> Result.mapError failwith // throw error
|> Result.join // Convert Result<unit, unit> into unit
The solutions posted above are very helpful to this newbie. But adding my own two cents worth, I going with this:
let _deleteDxFromEncounterAsync (encounterId:int) (dx:Diagnosis) : Async<unit> = deleteDxFromEncounterAsync encounterId dx.Description
let _deleteDxFromAllPreviousEncountersAsync (encounterId:int) (dx:Diagnosis) : Async<unit> = deleteDxFromAllPreviousEncountersAsync encounterId dx.Description
let _saveDxAsync (encounterId:int) (dx:Diagnosis) : Async<unit> = saveDxAsync encounterId dx
let _editAllDiagnosisInPreviousEncountersAsync (encounterId:int) (dx:Diagnosis) : Async<unit> = editAllDiagnosisInPreviousEncountersAsync encounterId dx
let listchk (dxs:Diagnosis list) : Diagnosis list option =
match dxs with
| [] -> None
| _ -> Some dxs
let _save (fn:int -> Diagnosis-> Async<unit>) (dxs:Diagnosis list) : unit =
match dxs |> listchk, m.Encounter |> Option.bind (fun v -> v.EncounterId) with
| Some dxs, Some id -> dxs |> List.iter (fun dx -> Async.RunSynchronously(fn id dx))
| _,_ -> failwith "Missing Encounter or EncounterId or Empty List"
m.DeletedBillingDiagnosis |>_save _deleteDxFromEncounterAsync
m.DeletedAllDiagnosis |>_save _deleteDxFromAllPreviousEncountersAsync
m.BillingDiagnosisList |>_save _saveDxAsync
m.AllDiagnosisList |> List.filter (fun dx -> dx.Key.IsSome) |>_save _editAllDiagnosisInPreviousEncountersAsync
For speed, in the future, I will probably have the Async functions act on the entire list at one time rather then one item; but for now, this code comes closest to my intent in asking the question. IMPROVEMENTS AND CRITISM IS GLADDLY APPRECIATED! F# is fun!
Thanks to all.
It's my third time today running and this buzz word functional language F# is driving me mad yet when I get certain bits right it feels good
earlier I had a problem with recursive loop someone suggested a way forward and now I am getting the above error, the problem is I know my type is going to be a string so why is the compiler complaining?
Aim was to put 12 weeks of learning into practice so I wanted to work on basic chat bot so far I can hold a basic level conversation, however, there are few things that are still out of my scope for example
why can't I call my type? IUserError to pass user input and check if they have said something that is not in my subject list then to response back with invalid input.
My other issue is to keep things simple I want to convert all input into lowercase string this is also proven to be a challenge.
and then there is numeric at some stage of the conversation user gets to room location for some reason I can find a way other than to do this check.
there is very few tutorial online and the book I am using does not explain things well. I am pleased with what I have achieved so far
if someone can explain where I am going wrong with this because in C# this would have been gone and dusted.:(
here is my whole code feel free to discuss:
open System
open System.IO
open System.Speech.Synthesis
// required for regular expression
open System.Text.RegularExpressions
// init randomizer
let rand = new Random()
// recursive response function find the first match with a key token
// response back acordingly
// Initialise a new instance of SpeechSynthesizer
let voice (sentence: string) =
use speech = new SpeechSynthesizer(Rate= -3)
//speech.SelectVoice("Microsoft Huihui Desktop")
speech.Speak(sentence)
// define functions of set list campus area
let mpCampusArea = Set.ofList ["Cisco Labs"; "The Bridge"; "Security
Area";
"Mac Labs"; "Open Access"]
//active patterns
//method for checking room number
let chkroom () =
let roomNumbers = seq {
yield 158
yield 123
yield 333 }
printfn "Room not found could it be :"
for items in roomNumbers do
printfn "%A" items
let (|Campuses|None|) users =
if Regex.Match(users,".*(MP?|mp|Curzon|curzon|ParkSide?).*" ).Success
then Campuses
else
None
// Apply Active pattern
let(|Repair|None|) input = // any sentence with broken|break|damaged is
require repair
if Regex.Match(input , ".*(broken?|break|damaged?).*").Success
then Repair
else
None
let (|ParkSide|None|) input =
if Regex.Match(input , ".*(P158|P159|P160).*").Success
then ParkSide
else
None chkroom
let (|RoomLocation|None|) str2 =
if Regex.Match(str2, ".*(158|140|150).*").Success
then RoomLocation
else
None chkroom
// Define an active recognizer for keywords that express salutation.
let (|Bye|Answer|NoSubject|MyGirlFriend|Faulty|None|) input =
match input with
| "goodbye" | "bye" | "go" |"get lost"
-> Bye
| "who" | "how" | "when" |"where"
-> Answer
| "car" |"what" |"name" |"bcu"
-> NoSubject
| "lonely" |"love" | "friendship"
-> MyGirlFriend
| "device" |"software" |"phone"
-> Faulty
| _
-> None
let (|Computer|Other|) input =
match input with
|"goodbye"|"bye"|"go" -> Computer
|_ -> Other
// select possible likely hood response based on random number for hello
subject
// Interact with the user
// Subject faulty software and Hardware
let faulty_response (str:string) =
let x = rand.Next(5)
match x with
| 0 -> "My advice is to restart the software / hardware?"
| 1 -> "My advice is relax it will be sorted."
| 2 -> "My advice is bin your device / software."
| 3 -> "Please throw your software / hardware in the recycle bin"
| 5 -> "Kiss your device / software as this always works for me."
| _ -> ""
let good_bye_response () =
let b = rand.Next(5)
match b with
| 0 -> "Good bye Babe"
| 1 -> "Thank God "
| 2 -> "We have to be positive love BCU"
| 3 -> "Live is beautiful but you are a smelly poo little fella BYE!"
| 4 -> "Good bye and thanks for complainting"
| _ -> ""
let answer_response () =
let x = rand.Next(10)
match x with
| 0 -> "Please go and complait to Waheed Rafiq"
| 1 -> "Please go and see Emmett Cooper"
| 2 -> "So you want me to kick a fuss?"
| 3 -> "What a waste of time"
| 4 -> "Please go and see BCU tech department"
| 5 -> "OMG and so what"
| 6 -> "Jump of the roof it will most likely help us all"
| 7 -> "Let's talk about the toliet shall we"
| 8 -> "why don't you use pattern matching with regular expressions!"
| 9 -> "Speak to the Queen she will mostly likely deals with BCU
complaints"
| _ -> ""
let none_response (str:string) =
let n = rand.Next(10)
match n with
| 0 -> "What would you"+ str + "like to chat about ?"
| 1 -> "I do not understand please ask again !"
| 2 -> "How about you Speak english and I log your helpdesk call yeah?"
| 3 -> "Sorry to hear that. Are you sure you want to complaint ?"
| 4 -> "This is a complaint Chat bot where you log helpdesk calls.
Please Refer to Cortana for her services.!"
| 5 -> "Let just complaint yeah for the sake of complaining ?"
| 6 -> "OKay what is your complaint about ?"
| 7 -> "Are you a human because you certainly do not behave like one!"
| 8 -> "The moon is epic. What is broken ?"
| 9 -> "Do you always complaint? Try logging it like my PC is broken
yeah !"
| _ -> ""
type Day =
| Monday
| Tuesday
| Wednesday
| Thursday
| Friday
| Saturday
| Sunday
let isWeekend x =
match x with
|Saturday |Sunday -> true
|_-> false
// using regular expression to tokenisse line of text
let matchWords = Regex(#"\w+")
let token (text:string) =
text.ToLowerInvariant()
|> matchWords.Matches
// Crossing the stream
type IUserError =
interface
end
type Error = { ErrorMessage:string; ErrorCode:int}
interface IUserError
type Success = { Status:string }
interface IUserError
let error = {ErrorMessage = "Incorrect input please enter a subject
phase";
ErrorCode = 250} :> IUserError
match error with
| :? Error as e -> printfn "Code %i \n Message: %s" e.ErrorCode
e.ErrorMessage
| :? Success -> printfn "Success"
|_ -> failwith "Invalid option"
//printfn "%A" error
//recursive response function
let rec response (token: string) (str: string) =
match token with
| Bye
-> good_bye_response ()
| Answer
-> answer_response ()
| Faulty
-> faulty_response str
| Repair
->
sprintf "%s" "Which Campus is the device in?"
| Campuses
-> sprintf "%s" "Which room is the device in?"
| RoomLocation
-> sprintf "%s" "Your call is log. Do you wish to quit?"
|_ when token.Contains("yes") -> "Okay logging you out"
|_ when token.Contains("no") -> answer_response ()
| NoSubject
-> none_response str
| None when (str.IndexOf(" ") > 0)
-> response (str.Substring(0,str.IndexOf(" ")))
(str.Substring(str.IndexOf(" ")+1))
| None when (str.IndexOf(" ") < 0)
-> response str ""
let rec chat () =
let valueInput = Console.ReadLine()
printf "Helpdesk-BCU Response --> %s \n" (response "" valueInput)
let keepRunning, message = response valueInput
printfn ">> %s" message
if keepRunning then chat()
//let rec chat () =
// if Break = false then
// let valueInput = Console.ReadLine()
// printf "Helpdesk-BCU Response --> %s \n" (response "" valueInput)
// if Break = false then
// chat()
// else
// ChatEnd()
let BCU_response (str: string) =
if (str.IndexOf(" ") > 0) then
response (str.Substring(0,str.IndexOf(" "))) (str.Substring(str.IndexOf("
")+1)) + "\n"
else
response str "" + "\n"
// call back feature for the chatbot
//[<EntryPoint>]
//let main argv = printfn "%A" argv
// Advance expression lamba [ Emmett helps required]
let ifancyHerList =
[
("Sara",1); ("Saima",2); ("Zoe",3); ("Scarlett",4);
("Jennifer",5);("Sandra Bullock",6)
]
let myGirlFriend () =
List.pick (fun funToNight ->
let n = rand.Next(10)
if (snd funToNight) = n
then Some (fst funToNight)
else None
) ifancyHerList
//match myGirlFriend with
//| Some name -> printfn "Your date for tonight is %A lucky fella" name
//| None -> printfn "You don't have a date tonight!"
//
printfn "Welcome to the BCU Complaint Chat Bot"
printf "Please enter your first name -->"
let data = Console.ReadLine()
//let rec complaints n =
// printf "%s what do you want to complain about? -->" data
//complaints()
chat()
printfn "The avaialbe areas at Millennium point are: %A" mpCampusArea
printfn "Which day is Weekend on?"
let x = Console.ReadLine()
0
any helps / pointers / anything as this is driving me nuts
I shall post a link direct to the project file if you wish to download it and have a closer look , much appericate your support.
the link to the project file
I'm not sure using an interface is what you want to do here. You don't have any abstract methods defined for IUserError, but then again maybe you were saving that for later. Also, you have a match with block hanging there with no function.
Here is my interpretation of what you could do:
// Crossing the stream
type Error = { ErrorMessage:string; ErrorCode:int}
type Success = { Status:string }
type UserError =
| Error of Error
| Success of Success
let printResponse (error:UserError) =
match error with
| Error (e) -> printfn "Code %i \n Message: %s" e.ErrorCode e.ErrorMessage
| Success _ -> printfn "Success"
|_ -> failwith "Invalid option"
let error = Error {ErrorMessage = "Incorrect input please enter a subject phase"; ErrorCode = 250}
Using fsi to evaluate printResponse, it should look like this:
> printResponse error;;
Code 250
Message: Incorrect input please enter a subject phase
val it : unit = ()
trying to work around a problem in outside library - is there a way to try-catch the generator itself item by item (probably not, but just to be sure...)?
let myTest() =
let mySeq = seq { for i in -3 .. 3 -> 1 / i }
// how to keep the line above intact, but modify the code below to try-catch-ignore the bad one?
mySeq |> Seq.iter (fun i -> printfn "%d" i)
()
You can't.
Once the exception happens, the state of the source enumerator is screwed up. If you can't get into the source enumerator to "fix" its state, you can't make it keep producing values.
You can, however, make the whole process "stop" after the exception, but you'll have to go a level below and work with IEnumerator<T>:
let takeUntilError (sq: seq<_>) = seq {
use enm = sq.GetEnumerator()
let next () = try enm.MoveNext() with _ -> false
let cur () = try Some enm.Current with _ -> None
while next() do
match cur() with
| Some c -> yield c
| None -> ()
}
mySeq |> takeUntilError |> Seq.iter (printf "%d")
I want to be able to write a computation expression in F# that will be able to retry an operation if it throws an exception. Right now my code looks like:
let x = retry (fun() -> GetResourceX())
let y = retry (fun() -> GetResourceY())
let z = retry (fun() -> DoThis(x, y))
etc. (this is obviously an astract representation of the actual code)
I need to be able to retry each of the functions a set number of times, which I have defined elswhere.
I was thinking a computation expression could help me here, but I don't see how it could help me remove explicitly wrapping each right hand side to a Retryable<'T>
I could see the computation expression looking something like:
let! x = Retryable( fun() -> GetResourceX())
etc.
I understand that Monads, in a crude fashion, are wrapper types, but I was hoping a way around this. I know I can overload an operator and have a very succinct syntax for converting an operation into a Retryable<'T>, but to me that's just making the repetition/wrapping more succinct; it's still there. I could wrap each function to be a Retryable<'T>, but once again, I don't see the value over doing what's done at the top of the post (calling retry on each operation. At least it's very explicit).
Maybe computation expressions are the wrong abstraction here, I'm not sure. Any ideas on what could be done here?
Computation expressions have a few extensions (in addition to the standard monadic features), that give you a nice way to do this.
As you said, the monads are essentially wrappers (creating e.g. Retryable<'T>) that have some additional behavior. However, F# computation expression can also define Run member which automatically unwraps the value, so the result of retry { return 1 } can have just a type int.
Here is an example (the builder is below):
let rnd = new System.Random()
// The right-hand side evaluates to 'int' and automatically
// retries the specified number of times
let n = retry {
let n = rnd.Next(10)
printfn "got %d" n
if n < 5 then failwith "!" // Throw exception in some cases
else return n }
// Your original examples would look like this:
let x = retry { return GetResourceX() }
let y = retry { return GetResourceY() }
let z = retry { return DoThis(x, y) }
Here is the definition of the retry builder. It is not really a monad, because it doesn't define let! (when you use computation created using retry in another retry block, it will just retry the inner one X-times and the outer one Y-times as needed).
type RetryBuilder(max) =
member x.Return(a) = a // Enable 'return'
member x.Delay(f) = f // Gets wrapped body and returns it (as it is)
// so that the body is passed to 'Run'
member x.Zero() = failwith "Zero" // Support if .. then
member x.Run(f) = // Gets function created by 'Delay'
let rec loop(n) =
if n = 0 then failwith "Failed" // Number of retries exceeded
else try f() with _ -> loop(n-1)
loop max
let retry = RetryBuilder(4)
A simple function could work.
let rec retry times fn =
if times > 1 then
try
fn()
with
| _ -> retry (times - 1) fn
else
fn()
Test code.
let rnd = System.Random()
let GetResourceX() =
if rnd.Next 40 > 1 then
"x greater than 1"
else
failwith "x never greater than 1"
let GetResourceY() =
if rnd.Next 40 > 1 then
"y greater than 1"
else
failwith "y never greater than 1"
let DoThis(x, y) =
if rnd.Next 40 > 1 then
x + y
else
failwith "DoThis fails"
let x = retry 3 (fun() -> GetResourceX())
let y = retry 4 (fun() -> GetResourceY())
let z = retry 1 (fun() -> DoThis(x, y))
Here is a first try at doing this in a single computation expression. But beware that it's only a first try; I have not thoroughly tested it. Also, it's a little bit ugly when re-setting the number of tries within the computation expression. I think the syntax could be cleaned-up a good bit within this basic framework.
let rand = System.Random()
let tryIt tag =
printfn "Trying: %s" tag
match rand.Next(2)>rand.Next(2) with
| true -> failwith tag
| _ -> printfn "Success: %s" tag
type Tries = Tries of int
type Retry (tries) =
let rec tryLoop n f =
match n<=0 with
| true ->
printfn "Epic fail."
false
| _ ->
try f()
with | _ -> tryLoop (n-1) f
member this.Bind (_:unit,f) = tryLoop tries f
member this.Bind (Tries(t):Tries,f) = tryLoop t f
member this.Return (_) = true
let result = Retry(1) {
do! Tries 8
do! tryIt "A"
do! Tries 5
do! tryIt "B"
do! tryIt "C" // Implied: do! Tries 1
do! Tries 2
do! tryIt "D"
do! Tries 2
do! tryIt "E"
}
printfn "Your breakpoint here."
p.s. But I like both Tomas's and gradbot's versions better. I just wanted to see what this type of solution might look like.
I was reading up on functional languages and i wondered how i would implement 'tries' in a pure functional language. So i decided to try to do it in F#
But i couldnt get half of the basics. I couldnt figure out how to use a random number, how to use return/continue (at first i thought i was doing a multi statement if wrong but it seems like i was doing it right) and i couldnt figure out how to print a number in F# so i did it in the C# way.
Harder problems is the out param in tryparse and i still unsure how i'll do implement tries without using a mutable variable. Maybe some of you guys can tell me how i might correctly implement this
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 very broken and wrong F# code
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()
Welcome to F#!
Here's a working program; explanation follows 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 number of things...
If you're calling methods with multiple arguments (like Random.Next), use parens around the args (.Next(1,100)).
You seemed to be working on a recursive function (dummyWhileTrue) rather than a while loop; a while loop would work too, but I kept it your way. Note that there is no break or continue in F#, so you have to be a little more structured with the if stuff inside there.
I changed your Console.WriteLine to a printfn to show off how to call it with an argument.
I showed the way to call TryParse that is most like C#. Declare your variable first (make it mutable, since TryParse will be writing to that location), and then use &n as the argument (in this context, &n is like ref n or out n in C#). Alternatively, in F# you can do like so:
let b, n = Int32.TryParse(v)
where F# lets you omit trailing-out-parameters and instead returns their value at the end of a tuple; this is just a syntactic convenience.
Console.ReadLine returns a string, which you don't care about at the end of the program, so pipe it to the ignore function to discard the value (and get rid of the warning about the unused string value).
Here's my take, just for the 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()
Things to note:
Pattern matching is prettier, more concise, and - I believe - more idiomatic than nested ifs
Used the tuple-return-style TryParse suggested by Brian
Renamed dummyWhileTrue to TryLoop, seemed more descriptive
Created two inner functions doneWith and notDoneWith, (for purely aesthetic reasons)
I lifted the main pattern match from Evaluate in #Huusom's solution but opted for a recursive loop and accumulator instead of #Hussom's (very cool) discriminate union and application 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)))
Also for the fun of 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 an enumeration for state and Seq.unfold over input to find the result.