我想在SML.中制作一个多选题程序
我有一个文本文件,其内容结构如下:
category1:Basic sml/sql
1-How many subsets does the power set of an empty set have?
A) One
B) Two
C) Three
D) Zero
Ans:A
2-What is the cardinality of the set of odd positive integers less than 10?
A) 20
B) 3
C) 5
D) 10
Ans:C
每个类别有5个以上的问题
每个问题都属于一个类别
对于每个问题,有4个建议的答案,然后是另一行上的答案
我希望能够检索并向用户显示问题(随机问题(和相应的建议答案。我已经编写了这个函数,它允许我逐行检索文件的所有内容。但我仍然纠结于如何卸载问答块。
fun getFromFile(file_name) =
let
val file = TextIO.openIn file_name
val text = TextIO.inputAll file
val _ = TextIO.closeIn file
in
String.tokens (fn c => c = #"n") text
end
val table = getFromFile("question_file.txt");
我该如何继续?是否可以在不首先通过表的情况下检索文件的行(直接检索文本(?
我仍然纠结于如何卸载问答块。
我该如何继续?
找到一种方法来对包含多个问题的多个类别进行编码,每个问题包含多个答案。一旦你找到了将其存储到文件(文件格式(中的方法,就编写一个解码器。您当前的解码器是线路解码器。您可以使用行对事物中的事物进行编码,但也可以通过其他方式进行编码。
例如,使用JSON:
[
{
"category": "Basic sml/sql",
"questions": [
{
"question": "How many subsets does the power set of an empty set have?",
"answers": [
{ "answer": "Zero", "correct": false },
{ "answer": "One", "correct": true },
{ "answer": "Two", "correct": false },
{ "answer": "Three", "correct": false }
]
},
...
]
},
...
]
如果依赖第三方库似乎太难了,你可以自己想出一种文件格式,例如基于行的格式:
CATEGORY Basic sml/sql
QUESTION How many subsets does the power set of an empty set have?
ANSWER Zero
ANSWER_CORRECT One
ANSWER Two
ANSWER Three
QUESTION ...
因此,给你的基于行的阅读器,在每一行上循环,看看第一个词:
- 如果是
CATEGORY
,则启动一个新类别 - 如果是
QUESTION
,则在当前类别中开始一个新问题 - 如果是
ANSWER
或ANSWER_CORRECT
,请在当前类别中的当前问题中提供一个选项
这建议使用一个递归函数(因为它需要遍历每一行(,该函数包含许多参数:当前类别、当前问题以及迄今为止的类别、问题和答案的集合。
在这一点上,你可能需要思考:我如何在记忆中存储具有多个答案的问题类别?我应该使用什么数据类型?例如,使用类型别名,您可以这样表达您的数据模型:
type answer_option = string * bool
val example_answer_option = ("Zero", false) : answer_option
type question_answers = string * answer_option list
val example_question_answers =
("How many subsets does the power set of an empty set have?",
[
("Zero", false),
("One", true),
("Two", false),
("Three", false)
]
) : question_answers
type category = string * question_answers list
val example_category =
("Basic sml/sql",
[ example_question_answers ]
) : category
val example_categories = [ example_category ] : category list
SML类型别名的工作方式是,您将所有这些别名扩展到它们所组成的基元类型中,因此它们可能会显示在REPL中,如下所示:
> type answer_option = string * bool
type question_answers = string * (string * bool) list
type category = string * (string * (string * bool) list) list
其可读性相当低并且是使用诸如datatype
、abstype
或不透明模块之类的替代方案的原因之一。
然而,接下来,您可以定义一个存根,如:
fun parse (line::lines, currentQuestion, currentAnswers, currentCategory, acc) =
case splitFirstWord line of
("CATEGORY", cat) => ...
| ("QUESTION", q) => ...
| ("ANSWER", aWrong) => ...
| ("ANSWER_CORRECT", aRight) => ...
| _ => raise Fail ("Unknown: " ^ line)
现在有两个子问题:
splitFirstWord
实际上并不存在。有很多关于当前状态的书籍。
祝你好运!
是否可以在不首先通过表的情况下检索文件的行(直接检索文本(?
我真的不明白这个问题。毫无疑问,是吗?
如果通过";表";你指的是某种可索引的数据结构,比如列表:
只是不要在输入上调用String.tokens (fn c => ...)
。
这为您提供了一个基本的string
。
请注意,table
只是一个值绑定的名称。
如果你愿意,你可以通过chair
来代替:
fun getFromFile(file_name) =
let
val file = TextIO.openIn file_name
val text = TextIO.inputAll file
val _ = TextIO.closeIn file
in
text
end
val chair = getFromFile "question_file.txt"
还要注意,函数参数周围的括号在SML中是不必要的。事实上,如果你认为它们是必要的,你可能很快就会犯语法错误。为了更清晰,尽量避免使用多余的语法。