我开始编写一个有趣的玩具命令行数据存储.我目前将解释器的所有命令存储在一个映射中,该映射将命令的字符串名映射为键,并将要执行的方法映射为其值.但是,DisplayHelp命令需要引用Commands的第Map个命令.DisplayHelp方法、Commands map 和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定义中消除递归可能也很有趣.作为一种 Select ,可以使用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相关问答推荐

在计算Total毫秒时,.NET TimeSpan类中是否存在错误?

为什么.Net 8.0.100是预览版?

为什么Regex.Escape支持数字符号和空格?

为什么解码后的字节数组与原始字节数组不同?

使用 SSH.NET 查找具有特定文件名的最新 SFTP 文件

将日期时间转换为日期格式 dd/mm/yyyy

"投掷;" 是什么意思?靠自己做什么?

如何创建 LINQ to SQL 事务?

如何获取控制台应用程序的执行目录

来自奥尔森时区的 .NET TimeZoneInfo

为什么 .NET 中没有可序列化 XML 的字典?

运算符重载 ==, !=, Equals

找不到 Assert.Fail 和 Assert.Pass 或等效项

MemoryStream.Close() 或 MemoryStream.Dispose()

有没有办法从方法返回匿名类型?

绑定到不在列表中的值的可编辑组合框

为什么 Roslyn 中有异步状态机类(而不是 struct )?

作者主签名的时间戳发现了一个建链问题:UntrustedRoot: self-signed certificate in certificate chain

WPF 中的 Application.DoEvents() 在哪里?

如何在我的机器上找到 fuslogvw.exe?