使用Python dict / set来记忆代数表达式

时间:2017-10-05 20:19:06

标签: python dictionary set

我对将表达式存储在自引用类中感兴趣,这些表达式可能会重复。我正在寻找一种方法的建议。我有几个想法。

示例:

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) + ca + (b + c) 可以被视为相同的表达式。

1 个答案:

答案 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__进行更复杂的比较来添加该功能。

显然,非交换式表达式需要不同的实现,这将是一个不同的类,或者你必须增加这个类的复杂性以满足这两个需求。