【发布时间】:2019-01-23 08:03:51
【问题描述】:
我想我忘记了如何使用 F# :(
我正在实现一个控制台界面。由于它们可以持续很长时间,因此我使用输入的行产生的序列。在遇到退出命令时,产生停止(也适用于 EOF,对管道很重要)。
open System
type Return =
| Code of int
| Assert of int
let rec consoleLines () = seq {
printf ">>> "
match Console.ReadLine() with
| null -> ()
| line ->
match line.Trim().ToLowerInvariant() with
| "quit" -> printfn "Bye!"
| line ->
yield line
yield! consoleLines()
}
let handle (str:string) = async {
if str.StartsWith "noop" then return Code 0
elif str.StartsWith "assert" then
let num = str.Substring(7) |> int
return Assert num
elif str.StartsWith "sing" then
printfn "La la la"
return Code 0
elif str.StartsWith "cry" then
printfn "Boo hoo"
return Code 2
//elif other commands...
else
printfn "Unknown command: %s" str
return Code -1
}
let results =
consoleLines()
|> Seq.map handle
|> Seq.map Async.RunSynchronously
let firstBad =
results
// Want to execute all lines, not just up to the first not-ok.
|> Seq.toList
|> List.tryFind ((<>)(Code 0))
match firstBad with
| Some (Code error) ->
eprintfn "Exiting with error code %i." error
exit error
| _ ->
printfn "Exiting normally."
exit 0
我想在程序退出时返回第一个不正常的代码(即 > 0),但所有行都应该被处理。
assert 命令是个例外,它可能会也可能不会阻止后面的命令被执行,这取决于它的参数和之前的命令。但是当它退出时,程序返回应该是崩溃命令之前的最后一个值。所以说我愿意
cry
assert 2
sing
程序哭了,但这是意料之中的,所以它也应该执行 sing 命令并以 0 退出。断言任何其他数字应该停止执行,返回 2 并且不让程序唱歌。
我尝试了很多类似的东西
-
Seq.pairwise似乎是个好主意,但重复(并且不起作用) -
Seq.map后跟Seq.concat -
Seq.unfold但不知道 - 各种
let rec loop () = seq { … loop() } - 几个
seq { for (l, r) in … }
但我无法让它发挥作用。如果我有一个列表,我认为这将非常容易:
let matchExceptions =
let rec matchExceptions acc = function
| [] -> acc |> List.rev
| Code code :: Assert ass :: rest when code = ass ->
matchExceptions (0 :: acc) rest
| Code code :: Assert _ :: _ ->
[code]
| Assert _ :: rest ->
// Unmatched assert is useless.
matchExceptions acc rest
| Code code :: rest ->
matchExceptions (code :: acc) rest
matchExceptions []
但我无法将其转换为列表,因为它可能是无限的。而且每次输入后我都不会得到输出。
我知道我不应该以递归/循环的方式同时使用 Seq.item 或 Seq.head 和 Seq.tail,因为这可能会很快二次爆发。
也许我可以得到seq 表达式与可变变量的工作,但没有更好/更实用的解决方案吗?我觉得我错过了一些东西。
【问题讨论】:
-
一个尾递归函数怎么样,你可以同时传递状态(可能是带有第一个错误代码的
Option<Return>,或者Returns的序列?)和函数的其余行,它评估当前行,或者以相同的状态递归,以新的状态递归,或者终止并打印状态?我可能也会将Quit设为Return案例之一,以便在尾递归函数中更容易处理。