Abraxas000
Abraxas000

Reputation: 57

F# Creating a single loop with multiple functions

I'm just starting to dig into some programming and decided to go with F#. As practice, I was trying to convert a script I made in .bat into F#. I'm having trouble creating a looping function that does more than one thing. Here is the code from the old script for this loop.

:Select
cls
echo.
echo Which Game?
echo.
echo    1. Assassin's Creed
echo    2. Crysis
echo    3. Mass Effect
echo.
echo.
set/p "game=>"
if /I %game%==1 goto Creed
if /I %game%==2 goto Crysis
if /I %game%==3 goto Mass
echo.
echo Invalid selection!
echo.
echo.
pause
goto Select

The code I've tried to make for the same function in F# looks like this so far:

let rec gameprint gameselect =
    printfn "Which Game?\n\n   1.%s\n   2.%s\n   3.%s\n\n\n" game1 game2 game3
    let mutable gameselect = Int32.Parse(stdin.ReadLine())
    if gameselect = "1" then game1
    elif gameselect = "2" then game2
    elif gameselect = "3" then game3
    else printf "temp"
    Console.Clear

I know I'm missing something that tells it to run again if it reaches the last "else"; and I'm getting these errors:

The expression should have type 'unit' but has type 'string'. Use 'ignore' to discard the result of the expression, or 'let' to bind the result to a name.

Error 1 This expression was expected to have type int but here has type string 19 21

Error 2 This expression was expected to have type int but here has type string 20 23

Error 3 This expression was expected to have type int but here has type string 21 23

Error 4 This expression was expected to have type string but here has type unit 22 17

Warning 5 This expression should have type 'unit', but has type 'string'. Use 'ignore' to discard the result of the expression, or 'let' to bind the result to a name. 19 5

I'd prefer to use an approach like this (Very incomplete):

let rec getGame() =
    match Int32.Parse(stdin.ReadLine()) with
    | 1 -> "Assassin's Creed"
    | 2 -> "Crysis"
    | 3 -> "Mass Effect"
    | _ -> printf "Temp"

But I'm getting:

Error 1 This expression was expected to have type string but here has type unit 36 19

And I'm not sure how I would loop it and make it 'printf' and 'Console.Clear'

If there is a more functional approach that I don't know about, I would certainly love to learn :-)

Thanks in advance!

Upvotes: 3

Views: 625

Answers (2)

Abraxas000
Abraxas000

Reputation: 57

Thanks for the VERY prompt response! I've been working on this hurdle since yesterday xD

Current code implementation:

#light
open System
open System.IO
//Simplify pausing
let pause() = Console.ReadLine()
//Identify the durrent drive letter associated with the flash drive for use
//when saving/deleting Save Data
let drive = System.IO.Directory.GetDirectoryRoot(System.IO.Directory.GetCurrentDirectory())
printfn "You're using the %s drive.\n\n" drive

//Identify the games to save
let game1 = "Assassin's Creed"
let game2 = "Crysis"
let game3 = "Mass Effect"

//Identify which game to Save/Load the data for
let rec getGame() =
  printfn "Which Game?\n\n   1.%s\n   2.%s\n   3.%s\n\n" game1 game2 game3
  match Int32.TryParse(stdin.ReadLine()) with
  | true,1 -> game1
  | true,2 -> game2
  | true,3 -> game3
  | _ ->
     printfn "You did not enter a valid choice."
     //The '_' is to ignore the result of Console.Readline() without binding it to anything
     //that way you can re-use the same line as many times as you like
     let _ = pause()
     Console.Clear()
     getGame()

//Print the selected game
let gameprint = getGame()
printf "You have chosen %s\n\n" gameprint
let _ = pause()

I took out:

Type Game = Creed | Crysis | Mass

because it was interfering with 'printf' of the result of getGame()

The notes are for any new people that are interested in details.

Upvotes: 0

kvb
kvb

Reputation: 55184

The biggest problem with your first attempt is that you're parsing the input from a string into an int, but then you try to pattern match against strings. Using 1, 2, and 3 instead of "1", "2", and "3" will fix that problem, but then you'll be at roughly the same point as your second attempt.

Your second attempt nearly works, but F# is telling you that you aren't using a consistent return type across all of your branches: in the first three cases you're returning a string but in the last case you're not returning anything. All you need to do is loop in that case, and the compiler will be happy:

let rec getGame() =
    match Int32.Parse(stdin.ReadLine()) with
    | 1 -> "Assassin's Creed"
    | 2 -> "Crysis"
    | 3 -> "Mass Effect"
    | _ -> printf "Temp"; getGame()

I'd do something more like this:

type Game = Creed | Crysis | Mass

let rec getGame() =
  printfn "Which Game?\n\n   1.%A\n   2.%A\n   3.%A\n\n" Creed Crysis Mass
  match stdin.ReadLine() |> Int32.TryParse with
  | true,1 -> Creed
  | true,2 -> Crysis
  | true,3 -> Mass
  | _ -> 
     printfn "You did not enter a valid choice."
     // put any other actions you want into here
     getGame()

Upvotes: 7

Related Questions