2011-09-08 61 views
4

我有一个python库,它构建出nested function calls以外的特殊迭代器(行为树)。虽然API具有相当好的和轻量级的语法(由于它是python),但它确实可以使用声明式DSL。将声明式DSL翻译为嵌套式函数调用

这里就是我设想的草图:

的DSL(使用YAML):

tree: 
    - sequence: 
    - do_action1 
    - do_action2 
    - select: 
     - do_action3 
     - sequence: 
     - do_action4 
     - do_action5 
     - do_action6 

会导致以下嵌套函数调用:

visit(
    sequence(
     do_action1(), 
     do_action2(), 
     select(
      do_action3(), 
      sequence(
       do_action4(), 
       do_action5(), 
       ), 
      do_action6(), 
      ) 
     ) 
    ) 

我很难直观地看到如何做到这一点。由于DSL必须表示一棵树,所以简单的深度优先遍历似乎是合适的。但是为了构建嵌套的函数调用,我不得不以某种方式将其从内部转移出去。它可能涉及一些聪明的中间堆栈或一些,但我不能完全理解它。执行这种转换的正确方法是什么?

+0

“它真的可以用一个声明DSL”?真?你的Python代码比你的DSL更可读。 DSL如何提供帮助? –

+0

在这个*非常简单*的例子中,括号尤其是bug我。在一个更复杂的树中,特别是当涉及到关键字参数时,需要子选项('** kwargs')会严重降低可读性。 [链接到现实生活中的示例](https://github.com/eykd/owyl/blob/master/examples/boids.py#L281)演示了这一点 - “parallel”节点的'policy'参数很容易错过。 –

+1

如果“真实生活中的例子”实际上表明了问题,那么也许你应该在这里包括类似的东西。 DSL是一个有吸引力的麻烦。我还没有看到一个提供任何价值。如果你有一个例子,请解决这个问题,以显示增加复杂性的一些令人信服的理由。 –

回答

3

我想你可以让Python跟踪函数调用和参数,而不是用堆栈自己做。

假设您有一个YAML分析树,其中每个节点表示一个函数调用,并且此节点的每个子节点都是一个参数(也是一个函数调用,因此它可能有自己的参数)。

然后定义功能evaluate,其评估该树的节点,如下(伪代码):

def evaluate(node): 
    # evaluate parameters of the call 
    params = [] 
    for child in node: 
     params.append(evaluate(child)) 

    # now make the call to whatever function this node represents, 
    # passing the parameters 
    return node.function.call(*params) 

最后,调用evaluate传递YAML树的根作为参数,你应该得到的期望的行为。


稍有不同EVAL-应用结构

def evaluate(node): 
    # evaluate parameters of the call 
    params = [ evaluate(child) for child in node ] 

    # apply whatever function this node represents 
    return node.function.call(*params) 
+0

为什么会,这可能就是这样做的。我不知道为什么这很难形象化。 –

+1

“为什么这很难形象化”。又一个DSL为什么没有多大帮助的例子。 Python易于可视化。坚持简单。 –

+0

我确信你对DSL的看法很低,我真的希望你永远不会觉得自己需要一个。只要说我想把我的自行车棚涂成蓝色就行了,我现在就去涂料店了。 –