对Rascal树的连续累积操作

时间:2015-01-26 15:46:32

标签: rascal

鉴于Rascal数据的不变性,如果以后的操作取决于早期的结果,那么这些数据的首选方法是什么?

例如,将注释值分配给树中的每个节点,其中较高节点的值取决于较低节点的值。如果您编写具有多个案例的单个访问语句,则较低级别的插入语句不会更改树,因此更高级别的操作可能没有任何操作。另一方面,使用visit语句包围每个case语句 - 并在每次访问后将树变量重新绑定到新树 - 是很麻烦的,更糟糕​​的是,似乎使结果取决于语句的顺序。

1 个答案:

答案 0 :(得分:0)

微妙的问题:-) visit语句有一个微妙的语义,特别是从OO角度来看它。虽然它正在积极地遍历一个价值,但它正在重建一个新的价值。取决于其案例陈述中的遍历顺序匹配的策略(顺序)"参见"不同的东西。

有时候想象一下访问作为代码生成器会产生一组相互递归的函数,这些函数将被访问的部分作为参数并在返回时返回一个新值。访问的主体(案例)变成了switch语句,这是这些生成函数的核心。然后,根据遍历顺序,递归步骤位于(bottom-up)之前或此切换语句(top-down)之后:

// bottom-up visit
x f(value x) {
  newChildren = map(f, children of x);
  x = newX(x, newChildren);
  switch (x) {
    case y : return whatever(y);
  }
}

因此,bottom-up次访问的切换案例中的代码会看到递归调用生成的树(尽管实际上没有更新树)。

以下是一个例子:

rascal>data T = tee(T, T) | i(int i);
data T = tee(T, T) | i(int i);
ok
rascal>t = tee(tee(i(1),i(2)),tee(i(3),i(4)));
t = tee(tee(i(1),i(2)),tee(i(3),i(4)));
T: tee(
  tee(
    i(1),
    i(2)),
  tee(
    i(3),
    i(4)))
rascal>visit(t) { 
>>>>>>>  case i(x)        => i(x+1) 
>>>>>>>  case tee(i(x),y) => tee(i(x+1),y) 
>>>>>>>}
T: tee(
  tee(
    i(3),   // here you see how 1 turned into 3 by incrementing twice
    i(3)),  // increment happened once here
  tee(
    i(5),   // increment happened twice here too
    i(5)))  // increment happened once here

您可以看到某些节点有两次增量,因为第二种情况匹配 已经访问了一个tee之后的i个子节点,以返回已经递增的另一个i节点。

尝试其他策略会为您提供其他结果,请参阅http://tutor.rascal-mpl.org/Rascal/Rascal.html#/Rascal/Expressions/Visit/Visit.html。请注意,访问语句范围内的变量由所有访问级别的所有访问共享,这样可以模拟类似拉链的行为(您可以始终将以前访问过的节点存储在临时)。

顺便说一句,语言设计试图避免需要更多涉及的功能性编程设计模式"比如拉链,因为它们使类型系统和人们与它交互的方式变得复杂。为了使这些事情在访问中正确地工作,在一个异构数据类型上进行递归,你需要一个多态学博士来理解它的类型是否正确。秘密地说,访问语句模拟了一组内置的类型安全的rank-2高阶多态函数,但这些都在幕后。

相关问题