我对将表达式存储在自引用类中感兴趣,这些表达式可能会重复。我正在寻找一种方法的建议。我有几个想法。
示例:
a + b
既然添加是可交换的,如果我们认识到b + a
是相同的表达式,那将会很好。出于我的应用目的,识别和折叠这些案例是可以的。
我一直在做我的面条并提出几种方法来处理它:
1)使用set()
并为所有对象定义__hash__
和__eq__
,这将允许我唯一地定义表达式。一个优点是我可以聪明并使用操作数的xor来更简单地检测交换情况:
class Expr(object):
def __init__(self, op, left, right):
self.op = op
self.left = left
self.right = right
def __hash__(self):
return hash(op) ^ hash(left) ^ hash(right)
def __eq__(self, other):
return self.op == other.op and \
((self.left == other.left and self.right == other.right) \
or (self.left == other.right and self.right == other.left))
这样做的一个缺点是,为了测试给定表达式是否已经存在,我需要构建一个对象来测试成员资格。如果已经在集合中我将丢弃它。
我相信,当你比较两棵你不想要的树时,这个__eq__
函数会引发一个完整的树遍历。理想情况下,记忆树的目的是简单的左/右指针值比较应该唯一地标识一个节点。
2)使用dict()
并使用您传递给构造函数的参数元组。为了处理可交换操作,我可以为('+', 'a', 'b')
和('+', 'b', 'a')
安装密钥,并将其分配给同一个对象。
key = ('+', left, right)
if key in mydict:
expr = mydict[key]
else:
expr = Expr('+', left, right)
mydict[key] = expr
mydict[('+', right, left)] = expr
return expr
我不能在这里使用xor方法,因为这不会产生保证唯一的密钥。
这里的事情有点更多有趣:
如果使用set()/ xor方法,则可以检测关联相等性。因此,如果您选择,(a + b) + c
和a + (b + c)
可以被视为相同的表达式。
答案 0 :(得分:1)
您可以通过缓存1
的实例来改进解决方案Expr
,使得参数的顺序不重要:
class CommutativeExpr(object):
_instances = {}
def __new__(cls, operator, left, right):
key = (operator, frozenset([left, right]))
try:
return cls._instances[key]
except KeyError:
instance = cls._instances[key] = super().__new__(cls)
return instance
def __init__(self, operator, left, right):
self.op = operator
self.left = left
self.right = right
self._hash = hash((operator, frozenset([left, right])))
def __hash__(self):
return self._hash
def __eq__(self, other):
return self is other
这允许您使用身份检查来验证:
>>> CommutativeExpr('+', 'a', 'b') == CommutativeExpr('+', 'b', 'a')
True
这不支持“关联性”,但您也可以通过正确定义__hash__
并让__eq__
进行更复杂的比较来添加该功能。
显然,非交换式表达式需要不同的实现,这将是一个不同的类,或者你必须增加这个类的复杂性以满足这两个需求。