提问人:MAK 提问时间:12/22/2009 最后编辑:MAK 更新时间:12/23/2009 访问量:425
对在编码表达式解析树/计算器时使用多态性有疑问
Doubt on using polymorphism in coding an expression parse tree/evaluator
问:
我正在研究一种玩具语言的解释器,只是为了学习如何构建解释器。我已经为生成解析树的语法实现了一个简单的解析器。现在,评估解析树的常用方法是在解析树的节点上使用多态性,其代码如下所示(在 Python 中):
class Node:
def __init__(self,left,right):
self.left=left
self.right=right
def evaluate(self):
print "throw an exception"
class AddNode(Node):
def evaluate(self):
return evaluate(self.left)+evaluate(self.right)
class SubNode(Node):
def evaluate(self):
return evaluate(self.left)-evaluate(self.right)
在 Node 的每个后代中都会覆盖 evaluate 方法,并根据需要进行防御。表达式的值可以通过简单地在根上调用 evaluate() 来找到。
另一种方法是在节点中没有任何 evaluate 函数,而是生成一个解析树,该树只是为节点分配一个类型。评估代码在一个单独的函数中完成,该函数以递归方式检查每个节点并决定如何评估它。这将导致一个函数具有大型“if else”构造、开关或跳转表。
在我所看到的几乎每一个地方,以及我在大学里学到的东西,似乎都表明第一种方法总是更好。我不太完全理解的是为什么第一种方法应该优于第二种方法。为什么在这种情况下多态性必然更好?带有大型“if else”表的函数不存在,但基本上相同的代码仍然存在,但移动到了不同的地方并分散在许多不同的类中。
这引起了我的注意,因为我认为我以后可能需要更改某些运算符的含义,甚至可能允许在运行时重新定义运算符。我也像对待运算符一样对待函数调用,所以有一个可以在运行时添加的跳转表之类的东西会很好。
第一种方法可能有一些我现在没有看到的显着优势。有人可以指出这一点吗?
答:
这是一个有趣的问题。在我看来,多态性仍然是一个不错的选择,即使你有运算符重载。
使用多态类定义的是语言中支持的原始操作。运算符重载不会为语言添加任何更原始的功能。重载运算符实际上等同于函数调用,应该以类似的方式实现。
在我看来,将运算符的语义本地化在运算符类中将使您的设计更简洁。这是因为要实现新的基元操作,您只需要定义类,然后在解析过程中创建一个实例。另一种方法是需要更改解析器和计算器。我只是有一种感觉,赋值器可以成为 if 语句的大巢,用于处理运算符的各种特殊情况,而这些特殊情况可以很好地隐藏在运算符类中。
我很难真正相信我在这里说的话,因为我过去使用过这两种技术,而且它们都运行良好。;-)
评论