Lambda的树对被歧视的联合树

时间:2016-06-14 02:10:08

标签: lambda f# token discriminated-union

我想将语法树编译为Turtle模块的方法。

module Turtle =
    let rotateDefaultAmount amount state = ...
    let move vector state = ...

此选项产生代码重复(实际上有更多命令):

type TurtleCommand =
    | Rotate of float32
    | Move of Vector2
/...
match symbol with
        | 'w' -> Move (Vector2.up)
        | '\' -> Rotate 90.0f
//...
let applyCommand command state =
    match command with
            | Move shift -> Turtle.move shift state
            | Rotate amount -> Turtle.rotate amount state
applyCommand command someState

我用它替换了它:

type TurtleCommand = TurtleState -> TurtleState
/...
match symbol with
        | 'w' -> Turtle.move (Vector2.up)
        | '\' -> Turtle.rotate 90.0f
//...
command someState

现在我有一个命令Turtle.moveAndEvadeCollision。这取决于其他命令。特别是,应该在每个move命令后执行,以确保它不会进入占用位置。此外,它不应该在树中跟随move命令。

我无法编写一个验证的方法moveAndEvadeCollision后面没有move,因为它们都是无法区分的TurtleState -> TurtleState lambdas。这是否意味着我的第一次重构是错误的,我应该重复复制?对于函数式语言的程序来说,拥有一个由函数组成的数据结构是否正常?

1 个答案:

答案 0 :(得分:2)

我认为第一个版本更好,因为它将(输入 - >>命令)和(命令 - >移动)阶段分开。这是解释器的一种非常常见的设计,分别对应于解析和执行阶段。

我认为没有任何真正的代码重复,尽管代码最终会变得更长。但是您可以获得一些非常实际的好处,例如能够让编译器验证您的applyState函数处理所有可能的命令。您也可以独立测试每个部分。

此外,正如您已经发现的,如果您首先将输入解析为命令树,您可以对整个树进行一些静态验证,以确保您没有输入任何无效程序。