F# Matching mutable object (string)
Here is the full code so far:
module clean
#light
open System
open System.IO
let pause() = Console.ReadLine()
let drive = System.IO.Directory.GetDirectoryRoot(System.IO.Directory.GetCurrentDirectory())
printfn "You're using the %s drive.\n\n" drive
let game1 = "Assassin's Creed"
let game2 = "Crysis"
let game3 = "Mass Effect"
let local1 = "\%APPDATA\%\\Ubisoft\\Assassin's Creed\\Saved Games\\"
let local2 = "\%USERPROFILE\%\\Documents\\My Games\\Crysis\\SaveGames\\"
let local3 = "\%USERPROFILE\%\\Documents\\BioWare\\Mass Effect\\Save\\"
let roam1 = drive + "Saves\\Abraxas\\" + game1 + "\\"
let roam2 = drive + "Saves\\Abraxas\\" + game2 + "\\"
let roam3 =开发者_高级运维 drive + "Saves\\Abraxas\\" + game3 + "\\"
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."
let _ = pause()
Console.Clear()
getGame()
let mutable gameprint = getGame()
printf "You have chosen %s\n\n" gameprint
let roaming =
match gameprint with
| game1 -> roam1
| game2 -> roam2
| game3 -> roam3
| _ -> "test"
printf "Roaming set to %s\n\n" roaming
let local =
match gameprint with
| game1 -> local1
| game2 -> local2
| game3 -> local3
| _ -> "test"
printf "Local set to %s\n\n" local
printf "Check gameprint %s" gameprint
In the section that sets the roaming and local objects, it is telling me that it will never match with anything other than 'game1'.
I did the 'printf' to check before and after matching with the local and roaming objects... The gameprint shows correctly in both of the printf commands, but doesn't match to anything other than game1... I'm not sure where I made the mistake.
Two things.
In F#, bindings can be shadowed. In particular, within your
match
, when you usegame1
,game2
, andgame3
in patterns, you are actually declaring new bindings with these names. Therefore, the first pattern will always match, and will just assign whatever value you are trying to match against it to the new bindinggame1
before evaluating the right hand side.One way to work around this is to declare your gameN bindings with the
[<Literal>]
attribute (but note that they must also start with capital letters to work as constants):[<Literal>] let Game1 = "Assassin's Creed"
Now you can use
Game1
in a pattern match and it will work as you expect.You may be aware of this, but you're not actually updating the
gameprint
binding anywhere anyway, so it's going to be set to the same value throughout your program and there is no point to its being mutable.
See F# matching with two values for an explanation.
When comparing against a few non-literal values, I'd just use an if-then-else
if gameprint = game1 then ...
elif gameprint = game2 then ...
...
Maybe something a little more like this would allow you to make it more extensible (if you populate the list of games at runtime) ... sorry the codes a little rushed I'm trying to get ready for work:
open System
type Game = {Title:string; local:string; roam:string}
let game1 = {
Title= "Assassin's Creed";
local = "\%APPDATA\%\\Ubisoft\\Assassin's Creed\\Saved Games\\";
roam = "Saves\\Abraxas\\\Assassin's Creed\\"
}
let game2 = {
Title= "Crysis";
local = "\%USERPROFILE\%\\Documents\\My Games\\Crysis\\SaveGames\\";
roam = "Saves\\Abraxas\\\Crysis\\"
}
let games = [game1; game2]
let printGamelListItem i g = printfn "%i: %s" i g.Title
let printChoice() =
printfn "Which Game?\n"
games |> List.fold (fun acc g ->
printGamelListItem acc g
acc+1) 1
|> ignore
let rec getGame() =
printChoice()
match Int32.TryParse(Console.ReadLine()) with
|true, x when x <= games.Length -> games.[x-1]
| _ ->
printfn "You did not enter a valid choice."
let _ = Console.ReadLine()
Console.Clear()
getGame()
let selection = getGame()
printfn "Roaming set to: %s" (selection.roam)
printfn "Local set to: %s" (selection.local)
精彩评论