我开始编写一个玩具命令行数据存储来娱乐。我目前将解释器的所有命令存储在一个映射中,该映射将命令的字符串名称映射为键,并将要执行的方法映射为其值。但是,DisplayHelp
命令需要从 Commands
的 Map
引用自身。 DisplayHelp
方法、Commands
映射和 InterpCommand
代码都如下所示:
let private DisplayHelp (shellState : ShellState) =
printfn "Available commands: "
Commands // <- This is where mutual recursive reference occurs
|> Map.map (fun k _ -> printfn "%s" k)
|> ignore
shellState
let private Commands =
[
"exit", Exit
"help", DisplayHelp // <- This is the other place where mutual reference recursion occurs
"store", StoreItem
"list", ListItems
]
|> Map.ofList
let private InterpCommand (shellState : ShellState) =
let nextState =
match Commands.Keys.Contains shellState.Command with
| true ->
shellState
|> Commands[shellState.Command]
| false ->
shellState
|> DisplayUnrecognizedCommand
// return
nextState
|> ResetStateInputs
如您所见,相互递归发生在 DisplayHelp
方法和 Commands
映射之间。我试图使用 and
关键字来相互定义它们,但这似乎是不好的做法。这里使用的正确模式是什么,或者问题本身是否以完全不同的方式解决?
最佳答案
您可以使用@Fyodor Soikin建议的方法来摆脱相互递归,即通过引入参数来抽象具体引用。在我们的例子中 - 从函数 DisplayHelp
中抽象对 Commands
的引用:
let private DisplayHelp(shellState : ShellState, commands) = // introduce parameter commands
printfn "Available commands: "
commands
|> Map.map(fun k _->printfn "%s" k)
|> ignore
shellState
let rec private Commands =
[
"exit", Exit
"help", fun state -> DisplayHelp(state, Commands) // pass Commands to DisplayHelp as parameter
"store", StoreItem
"list", ListItems
]
|> Map.ofList
从理论角度来看,从Commands
定义中消除递归也可能很有趣。作为一个选项,可以使用Fixed-point combinator来完成。 。这个想法是向目标函数(createCommands
)引入附加参数(f
),应该使用它来代替递归调用。在这种情况下,需要将目标函数 (createCommands
) 包装到定点组合器 (fix
) 中。函数fix本身是递归的(至少在 F# 实现中),但它可以普遍用于以通用方式消除任何类型的递归:
// Declare fixed point combinator
let rec fix' f = lazy f (fix' f)
let fix f = (fix' f).Value
let private DisplayHelp(shellState : ShellState, commands) =
printfn "Available commands: "
commands
|> Map.map(fun k _->printfn "%s" k)
|> ignore
shellState
let private createCommands (f: Lazy<_>) () = // transform value to non-recursive function and add f parameter for self referncing
[
"exit", Exit
"help", fun state -> DisplayHelp(state, f.Value ()) // call f function (unwrapped from lazy) insted of recursive call
"store", StoreItem
"list", ListItems
]
|> Map.ofList
let Commands = fix createCommands () // wrap function with fixed point combinator and call it
关于.net - 避免函数和其他对象之间相互递归的模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76411024/