Компактный способ записи (a + b == c или + 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)

но это немного странно.

Ответ 1

Если мы посмотрим на Zen of Python, сделайте упор на моем:

Дзен Питона, Тим Петерс

Красивая лучше, чем уродливая.
Явный лучше, чем неявный.
Простой лучше, чем сложный.
Комплекс лучше, чем сложный.
Плоский лучше, чем вложенный.
Редкий лучше, чем плотный. Показатели удобочитаемости.
Особые случаи не являются достаточно сложными, чтобы нарушать правила.
Хотя практичность превосходит чистоту.
Ошибки никогда не должны проходить молча.
Если явно не отключен.
Перед лицом двусмысленности откажитесь от соблазна угадать.
Должен быть один - и желательно только один - простой способ сделать это.
Хотя этот путь может быть не очевидным, если вы не голландский. Теперь лучше, чем никогда. Хотя никогда не бывает лучше, чем сейчас. Если внедрение сложно объяснить, это плохая идея.
Если внедрение легко объяснить, это может быть хорошей идеей.
Пространства имен - одна хорошая идея - позвольте сделать еще больше!

Наиболее Pythonic-решение является самым ясным, простым и простым объяснением:

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

Даже лучше, вам даже не нужно знать Python, чтобы понять этот код! Это так просто. Это, без оговорок, лучшее решение. Все остальное - интеллектуальная мастурбация.

Кроме того, это, вероятно, также самое эффективное решение, так как оно является единственным из всех предложений, связанных с короткими замыканиями. Если a + b == c, выполняется только одно добавление и сравнение.

Ответ 2

Решение трех равенств для a:

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

Ответ 3

Python имеет функцию any, которая выполняет or для всех элементов последовательности. Здесь я преобразовал ваше утверждение в 3-элементный кортеж.

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

Обратите внимание, что or является коротким замыканием, поэтому, если вычисление отдельных условий является дорогостоящим, лучше сохранить исходную конструкцию.

Ответ 4

Если вы знаете, что имеете дело только с положительными цифрами, это будет работать, и оно довольно чистое:

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()

Это довольно Pythonic, и это вполне возможно, самый эффективный способ сделать это (вызов дополнительной функции в сторону); хотя в любом случае вы не должны слишком беспокоиться о производительности, если это действительно не вызывает проблемы.

Ответ 5

Если вы будете использовать только три переменные, то ваш первоначальный метод:

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*a, 2*b или 2*c. Это будет верно для любого числа переменных.

Таким образом, хороший способ быстро написать это было бы просто иметь список ваших переменных и проверить их сумму на список удвоенных значений.

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

Таким образом, чтобы добавить в уравнение все переменные, все, что вам нужно сделать, это отредактировать список значений с помощью "n" новых переменных, а не писать "n" уравнений

Ответ 6

Следующий код может использоваться для итеративного сравнения каждого элемента с суммой других, которая вычисляется из суммы всего списка, за исключением этого элемента.

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

Ответ 7

Не пытайтесь его упростить. Вместо этого назовите то, что вы делаете, с помощью функции:

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):
  ...

Замените условие тем, что что-то "умное" может сделать его короче, но это не сделает его более читаемым. Оставлять его, как это не очень читаемо, однако, потому что сложно понять, почему вы проверяете эти три условия с первого взгляда. Это делает абсолютно кристально понятным то, что вы проверяете.

Что касается производительности, этот подход добавляет накладные расходы на вызов функции, но никогда не жертвует удобочитаемостью производительности, если вы не нашли узкое место, которое вы абсолютно должны исправить. И всегда измеряйте, поскольку некоторые умные реализации способны оптимизировать и вкратце некоторые вызовы функций в некоторых случаях.

Ответ 8

Python 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

Однако, в общем, я согласен с тем, что, если у вас нет более трех переменных, исходная версия более читаема.

Ответ 9

(a+b-c)*(a+c-b)*(b+c-a) == 0

Если сумма любых двух членов равна третьему члену, то один из коэффициентов будет равен нулю, делая все произведение нулевым.

Ответ 10

Как насчет просто:

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

Обратите внимание, что это не будет работать, если переменные без знака.

С точки зрения оптимизации кода (по крайней мере, на платформе x86) это представляется наиболее эффективным решением.

Современные компиляторы будут вызывать как вызовы функций abs(), так и избегать тестирования знака и последующей условной ветки с помощью умной последовательности команд CDQ, XOR и SUB. Таким образом, вышеупомянутый высокоуровневый код будет представлен только с низкими задержками, высокопроизводительными инструкциями ALU и двумя условными обозначениями.

Ответ 11

Решение, предлагаемое Alex Varga "a in (b + c, bc, cb)", компактно и математически красиво, но на самом деле я бы не написал такой код, потому что следующий разработчик не сразу понял бы цель кода.

Решение Mark Ransom

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

более понятен, но не намного более краткий, чем

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

При написании кода, на который должен смотреть кто-то другой, или что мне придется долго смотреть позже, когда я забыл, о чем думал, когда писал, слишком короткое или умное имеет тенденцию наносить больше вреда чем пользы. Код должен быть читаемым. Настолько лаконично, но не так лаконично, что следующий программист не может этого понять.

Ответ 12

Запрос для более компактного или более pythonic - я пробовал свои руки более компактно.

дано

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

Это на 2 символа меньше оригинала

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))))

Ответ 13

Как старая привычка к моему программированию, я думаю, что размещение сложного выражения в правильном положении в статье может сделать его более читаемым следующим образом:

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))

Ответ 14

Я хочу представить то, что я вижу как самый питонический ответ:

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 

В терминах Zen of Python я думаю, что подчеркнутые утверждения более последовательны, чем из другого ответа:

Дзен Питона, Тим Петерс

Красивая лучше, чем уродливая.
Явный лучше, чем неявный.
Простой лучше, чем сложный.
Комплекс лучше, чем сложный.
Плоский лучше, чем вложенный.
Редкий лучше, чем плотный. Показатели удобочитаемости.
Особые случаи не являются достаточно сложными, чтобы нарушать правила.
Хотя практичность превосходит чистоту.
Ошибки никогда не должны проходить молча.
Если явно не отключен.
Перед лицом двусмысленности откажитесь от соблазна угадать.
Должен быть один - и желательно только один - простой способ сделать это.
Хотя этот путь может быть не очевидным, если вы не голландский. Теперь лучше, чем никогда. Хотя никогда не бывает лучше, чем сейчас. Если внедрение трудно объяснить, это плохая идея.
Если внедрение легко объяснить, это может быть хорошей идеей.
Пространства имен - одна хорошая идея - позвольте сделать еще больше!

Ответ 15

В общем случае

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"

Я избегал разделения, хотя он позволяет использовать, чтобы избежать двух умножений, чтобы избежать ошибок округления. Однако проверьте переполнение

Ответ 16

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