简洁的写作方式(a + b = = c 或 a + c = = b 或 b + c = = a)

有没有更简洁的方法来编写布尔表达式

a + b == c or a + c == b or b + c == a

我想出来的

a + b + c in (2*a, 2*b, 2*c)

但这有点奇怪。

14235 次浏览

如果你知道你只是在处理正数,那么这个方法就会奏效,而且相当简洁:

a, b, c = sorted((a, b, c))
if a + b == c:
do_stuff()

正如我所说,这只适用于正数; ,但如果你的 知道是正数,这是一个非常可读的解决方案 IMO,甚至直接在代码中,而不是在函数中。

您可以这样做,这可能需要进行一些重复的计算; 但是您没有将性能指定为您的目标:

from itertools import permutations


if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
do_stuff()

或者没有 permutations()和重复计算的可能性:

if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
do_stuff()

我可能会把这个或者任何其他的解放到一个函数中。然后您可以在代码中干净利落地调用该函数。

就个人而言,除非我需要从代码中获得更多的灵活性,否则我只会使用您问题中的第一个方法。简单有效。我还是可以把它放进函数里:

def two_add_to_third(a, b, c):
return a + b == c or a + c == b or b + c == a


if two_add_to_third(a, b, c):
do_stuff()

这是非常 Python 化的,而且很可能是最有效的方法(除了额外的函数调用) ; 尽管您无论如何都不应该过于担心性能,除非它实际上造成了问题。

如果你只使用三个变量,那么你的初始方法是:

a + b == c or a + c == b or b + c == a

已经很像蟒蛇了。

如果你打算使用更多的变量,那么你的推理方法是:

a + b + c in (2*a, 2*b, 2*c)

非常聪明,但让我们想想为什么。为什么这个工作?
通过一些简单的算术,我们可以看到:

a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c

这对 a,b,c 都是真的,也就是说它等于 2*a2*b,或者 2*c。对于任何数量的变量都是如此。

所以快速编写这个代码的一个好方法就是简单地列出一个变量列表,然后根据双精度值的列表检查它们的和。

values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])

这样,要在方程中添加更多的变量,你所要做的就是用’n’新变量编辑你的值列表,而不是写’n’方程

要求是更紧凑或更蟒-我尝试我的手在更紧凑。

给予

import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
return x + y == z

这比原版少了两个字符

any(g(*args) for args in f((a,b,c)))

测试:

assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)

此外,给出:

h = functools.partial(itertools.starmap, g)

这是等价的

any(h(f((a,b,c))))

Python 有一个 any函数,它对序列的所有元素执行 or。在这里,我已经将您的语句转换为一个3元素元组。

any((a + b == c, a + c == b, b + c == a))

注意,or是短路,所以如果计算单个条件代价很高,那么最好保留原始构造。

解决 a 的三个等式:

a in (b+c, b-c, c-b)

一般来说,

m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();

如果操作输入变量对您来说是可以的,

c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();

如果希望使用位黑客技术进行攻击,可以使用“ !”、“ > > 1”和“ < < 1”

我避免了除法,尽管它可以避免两次乘法以避免舍入错误

def any_sum_of_others (*nums):
num_elements = len(nums)
for i in range(num_elements):
discriminating_map = map(lambda j: -1 if j == i else 1, range(num_elements))
if sum(n * u for n, u in zip(nums, discriminating_map)) == 0:
return True
return False


print(any_sum_of_others(0, 0, 0)) # True
print(any_sum_of_others(1, 2, 3)) # True
print(any_sum_of_others(7, 12, 5)) # True
print(any_sum_of_others(4, 2, 2)) # True
print(any_sum_of_others(1, -1, 0)) # True
print(any_sum_of_others(9, 8, -4)) # False
print(any_sum_of_others(4, 3, 2)) # False
print(any_sum_of_others(1, 1, 1, 1, 4)) # True
print(any_sum_of_others(0)) # True
print(any_sum_of_others(1)) # False
(a+b-c)*(a+c-b)*(b+c-a) == 0

如果任意两项之和等于第三项,那么其中一个因子为零,使得整个乘积为零。

如果我们看看《 Python 之禅》 ,强调我的观点:

《蟒蛇之禅》作者: Tim Peters

美丽总比丑陋好。
显式比隐式好。
简单比复杂好。
复杂比复杂好。
平坦比嵌套好。
稀疏总比密集好。
可读性很重要。
特殊情况不足以破坏规则。
尽管实用胜过纯洁。
错误永远不会悄无声息地过去。
除非明确地让他闭嘴。
面对模棱两可的情况,拒绝猜测的诱惑。
应该有一种——最好只有一种——显而易见的方法来做到这一点。
虽然一开始可能看不出来,除非你是荷兰人。
现在总比没有好。
虽然现在从来没有比 更好的了。
如果实现很难解释,那就是个坏主意。
如果实现容易解释,那么它可能是一个好主意。
名称空间是一个非常棒的想法——让我们做更多这样的事情吧!

最具 Python 特色的解决方案是最清晰、最简单、最容易解释的解决方案:

a + b == c or a + c == b or b + c == a

更妙的是,您甚至不需要了解 Python 就可以理解这些代码!那个很简单。这是毫无保留的最佳解决方案。其他的都是智力上的自慰。

此外,这可能也是性能最好的解决方案,因为它是所有短路建议中唯一的一个。如果是 a + b == c,则只做一次添加和比较。

下面的代码可以用来迭代地比较每个元素和其他元素的和,后者是从整个列表的和中计算出来的,不包括该元素。

 l = [a,b,c]
any(sum(l)-e == e for e in l)

Alex Varga 提供的解决方案“ a in (b + c,b-c,c-b)”紧凑且精确,但我实际上不会那样编写代码,因为下一个开发人员不会立即理解代码的用途。

Mark Ransom 的解决方案

any((a + b == c, a + c == b, b + c == a))

更清楚,但不比

a + b == c or a + c == b or b + c == a

当我写代码的时候,别人不得不看,或者当我忘记我写代码的时候,我不得不看很长一段时间,太短或者太聪明往往弊大于利。代码应该是可读的。所以简洁是好的,但是不要太简洁以至于下一个程序员不能理解它。

不如这样:

a == b + c or abs(a) == abs(b - c)

注意,如果变量是无符号的,那么这种方法就不起作用。

从代码优化的角度来看(至少在 x86平台上) ,这似乎是最有效的解决方案。

现代编译器将内联 abs ()函数调用,并通过使用 CDQ、 XOR 和 SUB 指令的巧妙顺序避免符号测试和随后的条件分支。因此,上面的高级代码将只用低延迟、高吞吐量的 ALU 指令和两个条件来表示。

巨蟒3:

(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...

它可以扩展到任意数量的变量:

arr = [a,b,c,d,...]
sum(arr)/2 in arr

然而,总的来说,我同意,除非你有三个以上的变量,原始版本是更具可读性。

不要试图简化它,而是使用 姓名函数:

def any_two_sum_to_third(a, b, c):
return a + b == c or a + c == b or b + c == a


if any_two_sum_to_third(foo, bar, baz):
...

用一些“聪明的”代替条件可能会使它更短,但不会使它更易读。不管它是怎样的也不是很可读的,因为知道 为什么是很棘手的,你一眼就可以检查这三个条件。这样你要检查什么就非常清楚了。

关于性能,这种方法确实增加了函数调用的开销,但是绝不能为了性能而牺牲可读性,除非您发现了必须修复的瓶颈。并且总是进行度量,因为一些聪明的实现能够在某些情况下优化掉一些函数调用并内联它们。

作为我编程的一个老习惯,我认为将复杂表达式放在子句的右边可以使它更具可读性,如下所示:

a == b+c or b == a+c or c == a+b

加上 ():

((a == b+c) or (b == a+c) or (c == a+b))

而且我认为使用多行也可以使这样的感觉更好:

((a == b+c) or
(b == a+c) or
(c == a+b))

我想提出我认为最 蟒蛇的答案:

def one_number_is_the_sum_of_the_others(a, b, c):
return any((a == b + c, b == a + c, c == a + b))

一般情况下,没有优化:

def one_number_is_the_sum_of_the_others(numbers):
for idx in range(len(numbers)):
remaining_numbers = numbers[:]
sum_candidate = remaining_numbers.pop(idx)
if sum_candidate == sum(remaining_numbers):
return True
return False

就 Python 的禅而言,我认为强调的陈述比其他答案更容易得到遵循:

《蟒蛇之禅》作者: Tim Peters

美丽总比丑陋好。
显式比隐式好。
简单比复杂好。
复杂比复杂好。
平坦比嵌套好。
稀疏总比密集好。
可读性很重要。
特殊情况不足以破坏规则。
尽管实用胜过纯洁。
错误永远不会悄无声息地过去。
除非明确地让他闭嘴。
面对模棱两可的情况,拒绝猜测的诱惑。
应该有一种——最好只有一种——显而易见的方法来做到这一点。
虽然一开始可能看不出来,除非你是荷兰人。
现在总比没有好。
虽然现在从来没有比 更好的了。
如果实现很难解释,那就是个坏主意。
如果实现容易解释,那么它可能是一个好主意。
名称空间是一个非常棒的想法——让我们做更多这样的事情吧!

使用这么小的表达式没有什么好处,但是使用一个函数来避免重复求和和比较可能是一种选择。当想要将操作更改为类似于 a + b == c * 2的操作时,它使其更具可维护性。

def equals_sum(a, b, c):
return a + b == c


if (equals_sum(a, b, c)
or equals_sum(a, c, b)
or equals_sum(b, c, a)):
...