最外层访问策略在 Rascal 中到底做了什么?
What exactly does the outermost visit strategy do in Rascal?
我写了下面的 Rascal 代码,它应该从节点名称到节点的映射中构建一棵树,从 "top" 映射的节点开始。它应该重复将 result
中具有字符串作为子节点的所有节点的子节点替换为 nodeMap
映射到的节点,直到不再发生任何变化(固定点)。
getNode
returns 节点 a map[str,node]
将它映射到,或者键本身,如果它不作为键出现在映射中。这很好用,证明了这个问题底部的其他代码确实有效。然而,下面的代码似乎 运行 即使在非常小的输入上也是如此。
node nodeMapToNode(map[str, node] nodeMap) {
node result = nodeMap["top"];
return outermost visit(result) {
case node n: {
if ([*str children] := getChildren(n)) {
insert makeNode(getName(n), [getNode(child, nodeMap) | child <- children]);
}
}
}
}
下面的代码确实有效,并且 returns 如我所料,在输入小的时候瞬间完成。然而,这正是我从流氓导师那里了解到的最外层访问应该做的。
任何人都可以向我解释这些代码片段之间的区别是什么(除了它们的编写方式之外)以及我因此误解了 outermost visit
的效果吗?另外,我想知道是否存在一种更短 and/or 更好的编写此代码的方法 - 使用诸如最外层访问之类的东西而不是手动编写固定点 - 是否存在。
node nodeMapToNode(map[str, node] nodeMap) {
node result = nodeMap["top"];
node lastResult;
do {
lastResult = result;
result = visit(lastResult) {
case node n: {
if ([*str children] := getChildren(n)) {
insert makeNode(getName(n),
[getNode(child, nodeMap) | child <- children]);
}
}
}
} while (result != lastResult);
return result;
}
最外层是什么?
流氓导师的解释非常紧凑,但让我们从那里开始吧。
repeat a top-down traversal as long as the traversal changes the resulting value (compute a fixed-point).
这在流氓术语中意味着:
r = outermost visit(x) {
case str s => s + "."
when size(s) < 3
};
是语法糖:
r = x;
solve(r) {
r = top-down visit(r) {
case str s => s + "."
when size(s) < 3
};
}
我认为有两种常见情况outermost/innermost是有道理的:
- 你的替换应该在同一个节点上重复多次
- 您的替换生成与其他模式匹配的新节点
你的具体例子
关于你问题中的例子。另一个手动改写的outermost
实际上是一个innermost
。默认的访问策略是bottom-up
.
一般来说,树的自下而上访问比自上而下更快。特别是当你重写它的时候,因为 Rascal 是不可变的,自下而上构建一个新的树会更快。
那么,也许用 innermost
访问而不是最外层访问替换您的代码?
我写了下面的 Rascal 代码,它应该从节点名称到节点的映射中构建一棵树,从 "top" 映射的节点开始。它应该重复将 result
中具有字符串作为子节点的所有节点的子节点替换为 nodeMap
映射到的节点,直到不再发生任何变化(固定点)。
getNode
returns 节点 a map[str,node]
将它映射到,或者键本身,如果它不作为键出现在映射中。这很好用,证明了这个问题底部的其他代码确实有效。然而,下面的代码似乎 运行 即使在非常小的输入上也是如此。
node nodeMapToNode(map[str, node] nodeMap) {
node result = nodeMap["top"];
return outermost visit(result) {
case node n: {
if ([*str children] := getChildren(n)) {
insert makeNode(getName(n), [getNode(child, nodeMap) | child <- children]);
}
}
}
}
下面的代码确实有效,并且 returns 如我所料,在输入小的时候瞬间完成。然而,这正是我从流氓导师那里了解到的最外层访问应该做的。
任何人都可以向我解释这些代码片段之间的区别是什么(除了它们的编写方式之外)以及我因此误解了 outermost visit
的效果吗?另外,我想知道是否存在一种更短 and/or 更好的编写此代码的方法 - 使用诸如最外层访问之类的东西而不是手动编写固定点 - 是否存在。
node nodeMapToNode(map[str, node] nodeMap) {
node result = nodeMap["top"];
node lastResult;
do {
lastResult = result;
result = visit(lastResult) {
case node n: {
if ([*str children] := getChildren(n)) {
insert makeNode(getName(n),
[getNode(child, nodeMap) | child <- children]);
}
}
}
} while (result != lastResult);
return result;
}
最外层是什么?
流氓导师的解释非常紧凑,但让我们从那里开始吧。
repeat a top-down traversal as long as the traversal changes the resulting value (compute a fixed-point).
这在流氓术语中意味着:
r = outermost visit(x) {
case str s => s + "."
when size(s) < 3
};
是语法糖:
r = x;
solve(r) {
r = top-down visit(r) {
case str s => s + "."
when size(s) < 3
};
}
我认为有两种常见情况outermost/innermost是有道理的:
- 你的替换应该在同一个节点上重复多次
- 您的替换生成与其他模式匹配的新节点
你的具体例子
关于你问题中的例子。另一个手动改写的outermost
实际上是一个innermost
。默认的访问策略是bottom-up
.
一般来说,树的自下而上访问比自上而下更快。特别是当你重写它的时候,因为 Rascal 是不可变的,自下而上构建一个新的树会更快。
那么,也许用 innermost
访问而不是最外层访问替换您的代码?