如何摆脱 SML 中的无限循环
How to get out of this infinite loop in SML
这是我的代码现在的样子。
我正在尝试做一个选择题程序
作为一种处理方式,它非常简陋,但我发现很难以更“专业”的方式构建代码。
fun readlist(filename) =
let
val file = TextIO.openIn filename
val text = TextIO.inputAll file
val _ = TextIO.closeIn file
in
String.tokens (fn c => c = #"\n") text
end;
(*Get user answer from keybord*)
fun getAnswer() = (
print "\nPlease enter your answer. Options(case sensitive): \nA-) \tB-) \tC-) \tD-)\n";
let
val return = valOf (TextIO.inputLine TextIO.stdIn)
in
return
end
);
(*Verify if the choosen line is a new starting question block base on fact that each question block contains 7 line*)
fun getQuestionLine(t) =
case t of
[] => []
|t::ts => if t mod 7 = 0 then
t::getQuestionLine(ts)
else
getQuestionLine(ts);
fun toIntList(x:int) = x;
val getblockStarts = getQuestionLine(List.tabulate(List.length(readlist("questionBank.txt")), toIntList));
fun member (x, []) = false
| member (x, y::yt) = x = y orelse member (x, yt);
fun seed () =
let
val aReal = Time.toReal(Time.now()) - 1.0e9
val floorReal = Real.realFloor(aReal)
val dec = aReal - floorReal
val sd1 = Real.floor(dec);
val sd2 = Real.floor(1000.0 * dec);
in
Random.rand(sd1,sd2)
end;
fun randList (N) =
let
fun subfun (N, i) intList =
if N = 0 then intList
else
let
val tmp = Random.randRange(1, (List.length(readlist("questionBank.txt"))))
val randVal = tmp i
in
if member(randVal, getblockStarts) then
subfun (N - 1, i) (randVal :: intList)
else
subfun (N, i) intList
end;
in
subfun (N, seed()) []
end;
fun ask(questionIndexList) =
let
fun loop (n) =
let
val question = print(
"\n"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1))^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+1)^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+2)^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+3)^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+4)^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+5)^"\n"
)
val userAns = getAnswer()
in
if userAns = List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+6)^"\n" then
(
print("Your answer is CORRECT.\n");
loop (List.length(List.take(questionIndexList, List.length(questionIndexList)-1)))
)
else (
print(
"Your answer is INCORRECT.\nThe correct answer is: "^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+6)
^"\n\n"
);
loop (List.length(List.take(questionIndexList, List.length(questionIndexList)-1)))
)
end
in
loop (List.length(questionIndexList))
end;
ask(randList(3));
我从我的问题文件中添加了一个捕获。
所有问题都遵循此格式
我的代码按照我想要的方式运行,但循环的最后一圈是无休止的。控制台给我三个问题,我回答,如果我没有找到,我有正确的答案,但是第三个问题被问了又问,没完没了。
我找遍了,也不知道我的错误在哪里。我需要帮助才能摆脱这个循环。
我更改了 ask
函数以使用要问的问题数量而不是要显示的问题块列表。
fun ask(n) =
let
fun loop (n) =
let
val newQues = randList(1)
val question = print(
"\n"^
List.nth(readlist("questionBank.txt"), hd newQues)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+1)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+2)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+3)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+4)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+5)^"\n"
)
val userAns = getAnswer()
in
if userAns = List.nth(readlist("questionBank.txt"), (hd newQues)+6)^"\n" then
(
print("Your answer is CORRECT.\n");
if n > 1 then loop (n-1) else ()
)
else (
print(
"Your answer is INCORRECT.\nThe correct answer is: "^
List.nth(readlist("questionBank.txt"), (hd newQues)+6)
^"\n\n"
);
if n > 1 then loop (n-1) else ()
)
end
in
loop (n)
end;
ask(5);
所以它现在可以正常工作了。
如果有人有优化的想法,我会很乐意阅读。
没有条件带你出去loop
;您在两个条件分支中无条件递归。
你可以通过检查零来解决这个问题,如果不是因为
List.length(List.take(questionIndexList, List.length(questionIndexList)-1))
总是相同的数字。
(即 List.length(questionIndexList)-1
– 如果您从列表中取出 K 个元素,结果的长度为 K。无需创建列表来确定其长度。)
但我会采用不同的结构。
每次需要问题时,您都在阅读文件。
最好只读取一次文件并传递列表。
首先,表示问题和答案的类型:
type Options = string * string * string * string;
datatype QA = QA of string * Options * string;
还有一些函数用于从笨重的字符串列表中收集这些函数:
fun toQAs [] = []
| toQAs (q::o1::o2::o3::o4::a::qas) = (QA (q, (o1,o2,o3,o4), a)) :: (toQAs qas);
fun readQuestions file = toQAs (readlist file);
以及打印问题的实用函数:
fun printQuestion (QA (q, (o1,o2,o3,o4), _)) = print ("\n"^ q^"\n\t"^ o1^"\n\t"^ o2^"\n\t"^ o3^"\n\t"^ o4^"\n");
ask
函数只获取问题列表并询问所有问题:
fun ask [] = print "No further questions.\n"
| ask (q::qs) =
let val (QA (_, _, answer)) = q
in
printQuestion q;
if getAnswer() = answer^"\n" then
print "Your answer is CORRECT.\n"
else
print ("Your answer is INCORRECT.\nThe correct answer is: "^ answer ^"\n\n");
ask qs
end;
最后,一个根据文件创建随机问题列表并提出问题的函数:
fun go () =
let
val QAs = readQuestions "questionBank.txt"
in
ask (map (fn i => List.nth(QAs, i)) (randList (length QAs) 3))
end;
这是我的代码现在的样子。
我正在尝试做一个选择题程序
作为一种处理方式,它非常简陋,但我发现很难以更“专业”的方式构建代码。
fun readlist(filename) =
let
val file = TextIO.openIn filename
val text = TextIO.inputAll file
val _ = TextIO.closeIn file
in
String.tokens (fn c => c = #"\n") text
end;
(*Get user answer from keybord*)
fun getAnswer() = (
print "\nPlease enter your answer. Options(case sensitive): \nA-) \tB-) \tC-) \tD-)\n";
let
val return = valOf (TextIO.inputLine TextIO.stdIn)
in
return
end
);
(*Verify if the choosen line is a new starting question block base on fact that each question block contains 7 line*)
fun getQuestionLine(t) =
case t of
[] => []
|t::ts => if t mod 7 = 0 then
t::getQuestionLine(ts)
else
getQuestionLine(ts);
fun toIntList(x:int) = x;
val getblockStarts = getQuestionLine(List.tabulate(List.length(readlist("questionBank.txt")), toIntList));
fun member (x, []) = false
| member (x, y::yt) = x = y orelse member (x, yt);
fun seed () =
let
val aReal = Time.toReal(Time.now()) - 1.0e9
val floorReal = Real.realFloor(aReal)
val dec = aReal - floorReal
val sd1 = Real.floor(dec);
val sd2 = Real.floor(1000.0 * dec);
in
Random.rand(sd1,sd2)
end;
fun randList (N) =
let
fun subfun (N, i) intList =
if N = 0 then intList
else
let
val tmp = Random.randRange(1, (List.length(readlist("questionBank.txt"))))
val randVal = tmp i
in
if member(randVal, getblockStarts) then
subfun (N - 1, i) (randVal :: intList)
else
subfun (N, i) intList
end;
in
subfun (N, seed()) []
end;
fun ask(questionIndexList) =
let
fun loop (n) =
let
val question = print(
"\n"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1))^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+1)^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+2)^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+3)^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+4)^"\n\t"^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+5)^"\n"
)
val userAns = getAnswer()
in
if userAns = List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+6)^"\n" then
(
print("Your answer is CORRECT.\n");
loop (List.length(List.take(questionIndexList, List.length(questionIndexList)-1)))
)
else (
print(
"Your answer is INCORRECT.\nThe correct answer is: "^
List.nth(readlist("questionBank.txt"), List.nth(questionIndexList, n-1)+6)
^"\n\n"
);
loop (List.length(List.take(questionIndexList, List.length(questionIndexList)-1)))
)
end
in
loop (List.length(questionIndexList))
end;
ask(randList(3));
我从我的问题文件中添加了一个捕获。
所有问题都遵循此格式
我的代码按照我想要的方式运行,但循环的最后一圈是无休止的。控制台给我三个问题,我回答,如果我没有找到,我有正确的答案,但是第三个问题被问了又问,没完没了。
我找遍了,也不知道我的错误在哪里。我需要帮助才能摆脱这个循环。
我更改了 ask
函数以使用要问的问题数量而不是要显示的问题块列表。
fun ask(n) =
let
fun loop (n) =
let
val newQues = randList(1)
val question = print(
"\n"^
List.nth(readlist("questionBank.txt"), hd newQues)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+1)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+2)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+3)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+4)^"\n\t"^
List.nth(readlist("questionBank.txt"), (hd newQues)+5)^"\n"
)
val userAns = getAnswer()
in
if userAns = List.nth(readlist("questionBank.txt"), (hd newQues)+6)^"\n" then
(
print("Your answer is CORRECT.\n");
if n > 1 then loop (n-1) else ()
)
else (
print(
"Your answer is INCORRECT.\nThe correct answer is: "^
List.nth(readlist("questionBank.txt"), (hd newQues)+6)
^"\n\n"
);
if n > 1 then loop (n-1) else ()
)
end
in
loop (n)
end;
ask(5);
所以它现在可以正常工作了。
如果有人有优化的想法,我会很乐意阅读。
没有条件带你出去loop
;您在两个条件分支中无条件递归。
你可以通过检查零来解决这个问题,如果不是因为
List.length(List.take(questionIndexList, List.length(questionIndexList)-1))
总是相同的数字。
(即 List.length(questionIndexList)-1
– 如果您从列表中取出 K 个元素,结果的长度为 K。无需创建列表来确定其长度。)
但我会采用不同的结构。
每次需要问题时,您都在阅读文件。
最好只读取一次文件并传递列表。
首先,表示问题和答案的类型:
type Options = string * string * string * string;
datatype QA = QA of string * Options * string;
还有一些函数用于从笨重的字符串列表中收集这些函数:
fun toQAs [] = []
| toQAs (q::o1::o2::o3::o4::a::qas) = (QA (q, (o1,o2,o3,o4), a)) :: (toQAs qas);
fun readQuestions file = toQAs (readlist file);
以及打印问题的实用函数:
fun printQuestion (QA (q, (o1,o2,o3,o4), _)) = print ("\n"^ q^"\n\t"^ o1^"\n\t"^ o2^"\n\t"^ o3^"\n\t"^ o4^"\n");
ask
函数只获取问题列表并询问所有问题:
fun ask [] = print "No further questions.\n"
| ask (q::qs) =
let val (QA (_, _, answer)) = q
in
printQuestion q;
if getAnswer() = answer^"\n" then
print "Your answer is CORRECT.\n"
else
print ("Your answer is INCORRECT.\nThe correct answer is: "^ answer ^"\n\n");
ask qs
end;
最后,一个根据文件创建随机问题列表并提出问题的函数:
fun go () =
let
val QAs = readQuestions "questionBank.txt"
in
ask (map (fn i => List.nth(QAs, i)) (randList (length QAs) 3))
end;