【发布时间】:2017-10-05 20:19:05
【问题描述】:
我对将表达式存储在自引用类中很感兴趣,这些表达式可能会重复。我正在寻找一种方法的建议。我有几个想法。
例子:
a + b
现在由于加法是可交换的,如果我们认识到b + a 是同一个表达式就更好了。就我的申请而言,识别和折叠这些案例是可以的。
我一直在用这个做面条,想出了几种处理方法:
1) 使用set() 并为所有对象定义__hash__ 和__eq__,这将允许我唯一地定义表达式。一个优点是我可以很聪明并使用操作数的异或来简化检测交换情况:
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) 可以如果您选择,则可以视为相同的表达式。
【问题讨论】:
-
您的真正问题是什么?无论如何,重要的是要知道
Exprs 是否不可变......如果它们是不可变的,您可以缓存__hash__甚至__eq__的结果。另外:您可以实现__new__并使其创建实例Expr('+', 'a', 'b')返回与Expr('+', 'b', 'a')的现有实例相同的实例。拥有这些“单身人士”将允许进行身份比较以检查平等......这就是您要找的吗? -
你可以有一个 normalize 函数,这样每个相同的表达式最终都是相同的。例如,按字母顺序对顺序无关紧要的事物进行排序。这增加了一些计算,而存储所有表示(例如 +ab 和 +ba)会增加一些内存。
-
不要单独存储参数;将它们存储为元组
(left, right)用于非交换运算符和冻结集frozenset([a, b])用于交换运算符。 -
是的@Bakuriu,表达式应该是不可变的。问题或多或少是1或2更好。或者如果是其他,那也值得考虑。
标签: python dictionary set