【问题标题】:How to Translate Dictionary of Functions如何翻译函数字典
【发布时间】:2018-01-17 00:32:17
【问题描述】:

在这里我很难想出一个好的标题,但希望我的描述能弥补它。

作为一个业余爱好,我正在尝试为 toy language(您必须为这本书付费,只需链接以显示我来自哪里)从 Go 移植到 F#。

这一切都很好,直到我需要调用函数字典中的其他函数。

这是一个用 F# 编写的 Go 代码尝试执行的非常简化的示例:

let processA a remainingCharacters =
    1

let processB b remainingCharacters =
    2

let processC c remainingCharacters =
    // this doesn't work, obviously, as funcMap is declared below
    let problem = funcMap.[remainingCharacters.Head]
    3 + problem

// i assume there is a better way of doing this, I'm just not sure what it is
let funcMap = dict[('a', processA); ('b', processB); ('c', processC)]

let processCharacter currentCharacter remainingCharacters =
    let processFunc = funcMap.[currentCharacter]
    processFunc currentCharacter remainingCharacters

let input = ['a'; 'b'; 'a'; 'c']
let processInput() =
    let rec processInputRec currentCharacter (remainingCharacters: char list) sum =
        if remainingCharacters.IsEmpty then
            sum
        else
            let currentValue = processCharacter currentCharacter remainingCharacters
            processInputRec remainingCharacters.Head remainingCharacters.Tail (sum + currentValue)
    processInputRec input.Head input.Tail 0

let result = processInput()
sprintf "%i" result |> ignore

因此,基本上,它试图将给定的输入值映射到不同的函数,在某些情况下,需要在这些函数中引用该映射(或至少获取另一个映射函数)。

我将如何在 F# 中做到这一点?

【问题讨论】:

    标签: f# functional-programming


    【解决方案1】:

    F# 中的编译顺序是一项功能,而不是错误。它有助于确保您的代码不是意大利面条,所有依赖项都是良好且线性的。

    像这样的“环回”场景通常通过参数化来解决。

    所以在这种特殊情况下,如果您希望特定函数递归调用processCharacter,只需将其传入:

    let processA a remainingCharacters _ =  // an extra unused parameter here
        1
    
    let processB b remainingCharacters _ =  // and here
        2
    
    let processC c remainingCharacters procChar = // here is where the extra parameter is used
        let problem = procChar (List.head remainingCharacters) (List.tail remainingCharacters)
        3 + problem
    
    ...    
    
    let rec processCharacter currentCharacter remainingCharacters =
        let processFunc = funcMap.[currentCharacter]
        processFunc currentCharacter remainingCharacters processCharacter
    

    但是请注意,尽管这会解决您的直接问题,但这(可能)不会一直有效,因为您没有跟踪输入中消耗了哪些字符。因此,如果processC 决定再处理一个字符,周围的代码将不知道它,并且在从processC 返回时将再次处理相同的字符。我不确定这是否是您的意图(很难从代码中看出),如果是,请忽略此警告。

    解析这样的输入流的常用方法是让每个处理函数返回一对 - 处理的结果加上剩余输入的尾部,例如:

    let processA chars = 
        1, (List.tail chars)
    

    然后周围的“驱动程序”函数会将返回的列表尾部线程化到下一个处理函数。这样一来,每个处理函数可以消耗的输入不一定是一个,而是任意数量的输入——从零到全部。

    这种方法也已在图书馆中实施。看看FParsec


    另一个注意事项:您的代码似乎非常不符合 F#-y。您没有使用许多 F# 功能,这使您的代码比需要的更长和更复杂。例如,不是访问.Tail.Head,而是习惯上在列表上进行模式匹配:

    let rec processInputRec current rest sum =
        match rest with
        | [] -> sum
        | (next, rest') ->
            let currentValue = processCharacter current rest
            processInputRec next rest' (sum + currentValue)
    

    【讨论】:

    • 谢谢,我完全理解排序是一项功能而不是错误;我只是在精神上被困在试图解决我默认的 OOP 思维方式。我已经开始添加适当的函数作为参数,但不确定这是否可行。您处理那些处理多个输入字符的函数是完全正确的,我的实际实现已经返回“结果”和“新剩余”输入。
    • 另外感谢关于模式匹配的提醒,这个例子是我徒手写的,但你说得对,我让事情变得比我需要的更冗长。
    猜你喜欢
    • 2022-11-30
    • 2011-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多