.net - 避免函数和其他对象之间相互递归的模式?

标签 .net recursion f#

我开始编写一个玩具命令行数据存储来娱乐。我目前将解释器的所有命令存储在一个映射中,该映射将命令的字符串名称映射为键,并将要执行的方法映射为其值。但是,DisplayHelp 命令需要从 CommandsMap 引用自身。 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/

相关文章:

c# - 为什么 VS2010 在 COMException 上停止,如果存在 try catch 并且异常设置设置为不在第一次机会时抛出

c# - 为什么 CLS 要求抛出/捕获异常派生对象?

java - 递归方法绘制树状图

ruby - 如何在递归方法中返回具有累积值的数组

f# - 使用 FParsec 解析方法参数

reflection - 使用代码引用构建 AST 与表达式树

f# - printf 和格式规则

c# - 从字符串中删除除字母以外的所有内容

C# 反转字符串中的所有数字?

python - 递归使用 glob.glob,同时还利用 .format 语法