【问题标题】:Relations between special vectors in pythonpython中特殊向量之间的关系
【发布时间】:2019-02-02 09:03:22
【问题描述】:

我想知道是否有办法在一些特殊的“向量”之间建立关系。

例子:

假设这些是我的向量(我只会选择 3 个特殊向量):

   a=[1,2,[1]]
   b=[1,2,[2]]
   c=[1,3,[1]]

并且我想在比较它们时添加以下规则(字典顺序):

我想说

   a<b 

因为

  a[0]=b[0] and a[1]=b[1] but *a[2]<b[2]*

但我也想说

  a<c

因为

  a[0]=b[0] and a[1]<c[1] and a[2]<=c[2]

但请注意,“b”和“c”的情况有些不同,因为它们无法比较,因为即使

  b[0]=c[0] and b[1]<c[1], the last term changes everything since b[2]>c[2]

换句话说,我应用的规则,将首先比较两个向量 x 和 y 的“正常”条目,如果向量 x 的某个条目大于向量 y 的某个条目,我们看看最后一个条目。如果向量x的最后一项大于或等于,那么我们说x>y,如果不是这样,那么x和y不具有可比性。

如果 x 和 y 的所有“正常”条目都相同,我们将比较最后一个条目。如果 x 的最后一项大于 y 的最后一项,我们也说 x>y。

我认为这与 while 循环有关。

【问题讨论】:

  • 是否需要在自己的单项列表中包含第三个值?
  • 那么您空间中的某些两个向量可能不可比较?听起来怪怪的。不保证在您空间中的向量上定义的集合是可订购的。
  • 那么你想让b &lt; cc &lt; b 为假吗?您可能想要定义一个可以覆盖 __lt__ 等的类。来表示您的向量,而不仅仅是隐式使用列表。
  • 是的,b 和 c 不可比较,所以 b

标签: python python-3.x function vector relation


【解决方案1】:

您可以轻松编写一个函数来执行您所描述的操作。

def special_lt(vec1, vec2):

您已将“正常”值定义为除最后一个之外的所有值,并将“特殊”值定义为最后一个,所以这很容易:

    normal1, normal2 = vec1[:-1], vec2[:-1]
    special1, special2 = vec1[-1], vec2[-1]

现在,如果我理解正确,您想对正常值进行字典比较,然后在它们相等时遵循特殊值……

    if normal1 == normal2:
        return special1 < special2

...但在其他情况下使用特殊值作为检查以确保它们的排序方式与正常值相同:

    elif normal1 < normal2:
        if special1 <= special2:
            return True
        raise ValueError('not comparable')
    else:
        if special2 <= special1:
            return False
        raise ValueError('not comparable')

请注意,为了比较正常值列表和特殊值列表,我没有使用循环;我只是比较了列表。这是因为列表已经按字典顺序进行比较(当然,在内部,这是通过循环完成的,但我不必编写它)。

【讨论】:

  • 谢谢。这真的很有帮助。
  • 我对此有最后一个问题。如果可比,是否可以从最大到最小对向量进行排序?例如,如果我有 [v1,v2] 并且它们都具有可比性,但是 v1
  • @Stiven if v1 &lt; v2: return [v2, v1] 但是你会返回两种类型(布尔和列表)。那时,您应该重新考虑设计。示例:您可以在可比较时返回列表,在不可比较时引发异常。
  • @StivenG 对于排序,您只能传递一个转换每个值的key 函数。如果您编写旧式 cmp 函数(返回 -1、0 或 1)而不是 lt 函数,则可以使用 cmp_to_key 从您的 cmp 创建一个 key 函数。但是,如果您主要想将其用于排序,那么您最好首先创建一个 key 函数,或者像 blhsing 的回答那样创建一个包装器类型。
【解决方案2】:

您可以使vector 成为list 的子类并重载__lt____gt__ 方法,以便在默认行为之前检查最后一项。为了完整性,还要重载__le____ge__ 方法:

class vector(list):
    def __lt__(self, other):
        lt = super().__lt__(other)
        if lt and self[-1] > other[-1]:
            return False
        return lt
    def __gt__(self, other):
        gt = super().__gt__(other)
        if gt and self[-1] < other[-1]:
            return False
        return gt
    def __le__(self, other):
        if self == other:
            return True
        return self < other
    def __ge__(self, other):
        if self == other:
            return True
        return self > other

这样:

a=vector([1,2,[1]])
b=vector([1,2,[2]])
c=vector([1,3,[1]])
print(a<b)
print(a<c)
print(b<c)
print(c<b)
print(b>c)
print(c>b)
print(b<=c)
print(c<=b)

将输出:

True
True
False
False
False
False
False
False

编辑:鉴于下面的 cmets,我还想指出,由于 OP 所需的非典型逻辑,functools.total_ordering 不起作用,其中一个对象可以同时不小于、不大于和不等于另一个对象。

所以如果我们只为vector 类定义__lt__ 方法并应用total_ordering 装饰器:

from functools import total_ordering
@total_ordering
class vector(list):
    def __lt__(self, other):
        lt = super().__lt__(other)
        if lt and self[-1] > other[-1]:
            return False
        return lt

上面的测试代码会产生以下错误输出:

True
True
False
False
False
True
True
False

【讨论】:

  • 最好只定义__lt__(和__eq__,如果需要),然后使用@functools.total_ordering。然后你不必重复逻辑四次。 (有时出于性能原因你不得不重复它们,但在这种情况下,对于非平凡的类,我通常仍然编写代码来生成方法,只是在类定义时而不是在运行中。)
  • 不,@functools.total_ordering 没有魔法。 @functools.total_ordering 依赖于对象的逻辑关系,因此如果我只定义 __lt____gt__ 的返回值始终是 __lt__ 的否定,在 OP 的情况下不是。
  • total_ordering 确实取决于你给它的东西,但__gt__(self, other) 不是not __lt__(self, other),它是__lt__(other, self)。更一般地说,它适用于全序和偏序,其中一切都与全序相同或异常。它不一定适用于奇怪的 NaN 风格的排序。如果这就是 OP 想要的,那么是的,你可能无法使用它。
  • 由于所需的逻辑与直观的total_ordering 相悖,我建议将此“特殊排序”作为单独的类混入普通的Vector 类中。参考github.com/python/cpython/blob/…
  • @pylang 对于它的价值,编写一个 @weird_ordering 装饰器非常简单(我在 GitHub 上的某个地方有一个 @nan_ordering)——但除非你计划使用多种这样的类型,或者你可以想出一个名称,使规则的含义比代码更清晰,我不会为这种抽象而烦恼;毕竟我只是像blhsing那样手动编写它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-17
相关资源
最近更新 更多