我正在构建一个自定义的小型解释脚本语言,除了范围之外,一切正常。 对于实际执行,我使用访问者模式:
我修改了模式以通过变量表:
public void visit(ProgrammTree proTree){
VariableTable vt = new VariableTable();
foreach (var t in proTree.getChildren()) {
t.accept(this, vt);
}
}
这就是问题的起点:
public void visit(WhileTree whiletree, VariableTable vt) {
var cond = (ConditionTree)whiletree.getChild(0);
while (cond.accept(this, vt).toBoolean()) {
var clonedSubTable = new VariableTable(vt)
foreach (Tree t in whiletree.getChildren()) {
t.accept(this, clonedSubTable );
}
}
}
问题是循环内的更改不在外部作用域中执行。 你有一个聪明的方法来实现这个吗?
答案 0 :(得分:0)
你留下了一些含糊不清的东西,所以我要做出以下假设(请指出任何错误):
因此,在这些假设下,问题是即使在原始表中已经存在已分配的变量的情况下,使用克隆表完成的任何分配也不会在原始表中可见。
要解决此问题,有多种方法:
您可以将表映射变量设置为内存位置而不是值。然后,您将拥有另一个将内存位置映射到值的表(或只是一个普通数组)。这样,变量的表项永远不会改变:一旦定义了变量,它就会获得一个内存地址,并且该地址不会发生变化,直到变量消失。只有地址的值可能会改变。
这种方法的快速而又脏的替代方法可以让您不必管理自己的内存,就是在值周围添加一个可变的包装器,即ValueWrapper
类{{1} }} 方法。假设您的克隆表是原始的浅表副本,这意味着您可以在克隆表的条目上调用setValue
,如果该条目已存在于原始表中,则更改也将反映出来在原始表格中。
与您当前代码保持最接近的解决方案是将您的表格转换为链接结构。然后setValue
实际上不会复制任何内容,而只是创建一个新的空表,其父链接指向原始表。任何新条目都将插入到新表中,但对旧条目的访问将简单地传播到父表。
即使您选择使用选项1或2来解决当前问题,出于性能原因,使用父链接而不是不断复制表也是一个好主意。
仅使用解决方案3的缺点是,当您实现闭包时,您将再次遇到类似的问题。所以它真的只能解决你当前的确切问题。
解决方案1的优点是它允许您完全控制内存,因此您可以随心所欲地实现与内存相关的功能。不利方面是一样的。