lambda 树与可区别联合树
Tree of lambdas vs tree of discriminated unions
我想将语法树编译成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
lambda。这是否意味着我的第一次重构是错误的,我应该 return 重复?对于函数式语言的程序来说,具有由函数组成的数据结构是否正常?
我认为第一个版本更好,因为它将(输入 -> 命令)和(命令 -> 移动)阶段分开了。那是解释器很常见的设计,分别对应解析和执行阶段。
我不认为有任何真正的代码重复,尽管代码最终确实有点长。但是您会得到一些非常实际的好处,例如能够让编译器验证您的 applyState 函数是否处理了所有可能的命令。您也可以独立测试每个部分。
此外,正如您已经发现的那样,如果您首先将输入解析为命令树,则可以对整个树进行一些静态验证,以确保您没有输入任何无效程序。
我想将语法树编译成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
lambda。这是否意味着我的第一次重构是错误的,我应该 return 重复?对于函数式语言的程序来说,具有由函数组成的数据结构是否正常?
我认为第一个版本更好,因为它将(输入 -> 命令)和(命令 -> 移动)阶段分开了。那是解释器很常见的设计,分别对应解析和执行阶段。
我不认为有任何真正的代码重复,尽管代码最终确实有点长。但是您会得到一些非常实际的好处,例如能够让编译器验证您的 applyState 函数是否处理了所有可能的命令。您也可以独立测试每个部分。
此外,正如您已经发现的那样,如果您首先将输入解析为命令树,则可以对整个树进行一些静态验证,以确保您没有输入任何无效程序。