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