在我掌握F#的使命中我创造了一个口袋游戏。 我想要实现某种高分榜。
到目前为止,我已将名称,得分和时间写入文件,然后该文件将读入显示所有之前的应用程序分数。 是的,这不是理想的,因为列表增长得非常快。
我有点想挑选前三个分数,而不是关心名称或时间。
问题:我应该将文件读入数组/列表,然后从那里挑选出最高分,还是有更好的方法直接从文件中挑选出最高分?
热烈欢迎指针,代码,提示和技巧。
let scoreFile = sprintf ("Name: %s\nTime: %i sec\nScore: %d\n\n") name stopWatch.Elapsed.Seconds finalScore
let readExistingFile = File.ReadAllText ("hiscore.txt")
File.WriteAllText ("hiscore.txt", scoreFile + readExistingFile)
let msg = File.ReadAllText ("hiscore.txt")
printfn "\nHighscores:\n\n%s\n\n\n\nPress ANY key to quit." msg
答案 0 :(得分:1)
我是否应该将文件读入数组/列表并从那里挑选出最高分或是否有更好的方法直接从文件中选出最高分?
除非分数已经在文件中排序,否则您必须仔细查看它们才能找出前三名是什么。你现在正在编写文件的方式,解析数据可能有点困难 - 分数存储在多行上,所以你必须处理它。
假设文件不必是人性化的,我会改为使用逗号分隔值列表。通过打开文件,人类更难阅读,但它使得 lot 更容易在程序中解析。例如,如果这些行看起来像Name,Time,Score
,则可以像这样解析它们:
type ScoreData = {
Name : string
Time : string // could be a DateTime, using string for simplicity
Score : int
}
let readHighScores file =
File.ReadAllLines file
|> Array.choose (fun line ->
match line.Split ',' with
| [| name; time; score |] ->
{
Name = name
Time = time
Score = (int)score // This will crash if the score isn't an integer - see paragraph below.
}
|> Some
| _ ->
// Line doesn't match the expected format, we'll just drop it
None
)
|> Array.sortBy (fun scoreData -> -scoreData.Score) // Negative score, so that the highest score comes first
|> Seq.take 3
这将读取您的文件并输出三个最大分数。使用Array.choose只允许保留与您期望的格式匹配的行。这也允许您根据需要添加额外的验证,例如确保分数是整数,并且可能将时间解析为System.DateTime
而不是将其存储为int
。
然后你可以通过这样的方式打印你的高分:
let highScores = readHighScores "hiscore.txt"
printfn "High scores:"
highScores
|> Seq.iteri (fun index data ->
printfn "#%i:" (index + 1)
printfn " Name: %s" data.Name
printfn " Time: %s" data.Time
printfn " Score: %i" data.Score
)
这将调用先前定义的函数并打印返回的每个分数 - 在这种情况下为前3个。使用Seq.iteri
,除了得分数据本身之外,还可以在输出中包含索引。使用我编写的一些数据,最终看起来像这样:
High scores:
#1:
Name: Awots
Time: 2015-06-15
Score: 2300
#2:
Name: Roujo
Time: 2016-03-01
Score: 2200
#3:
Name: Awots
Time: 2016-03-02
Score: 2100
现在,可能有一种方法可以在不将整个文件一次性加载到内存中的情况下执行此操作,但除非您拥有非常大的文件,否则我认为它不值得 - 在这种情况下您可能希望对其进行排序或使用更合适的存储方法,如数据库。