Есть ли более компактный или питонический способ написать логическое выражение
a + b == c or a + c == b or b + c == a
Я придумал
a + b + c in (2*a, 2*b, 2*c)
но это немного странно.
Есть ли более компактный или питонический способ написать логическое выражение
a + b == c or a + c == b or b + c == a
Я придумал
a + b + c in (2*a, 2*b, 2*c)
но это немного странно.
Ответы:
Если мы посмотрим на дзен Python, акцент мой:
Дзен Питона, Тим Питерс
Красивое лучше уродливого.
Явное лучше, чем неявное.
Лучше простое, чем сложное.
Сложный лучше, чем сложный.
Плоский лучше, чем вложенный.
Лучше разреженное, чем плотное.
Читаемость имеет значение.
Особых случаев недостаточно, чтобы нарушать правила.
Хотя практичность лучше чистоты.
Ошибки никогда не должны проходить незаметно.
Если явно не отключен.
Перед лицом двусмысленности откажитесь от соблазна угадать.
Должен быть один - а желательно только один - очевидный способ сделать это.
Хотя сначала этот способ может быть не очевиден, если вы не голландец.
Лучше сейчас, чем никогда.
Хотя никогда не бывает лучше, чемпрямо сейчас.
Если реализацию трудно объяснить, это плохая идея.
Если реализацию легко объяснить, это может быть хорошей идеей.
Пространства имен - одна отличная идея - давайте сделаем их больше!
Самое питоническое решение - самое ясное, простое и легкое для объяснения:
a + b == c or a + c == b or b + c == a
Более того, вам даже не нужно знать Python, чтобы понять этот код! Это что легко. Это, безоговорочно, лучшее решение. Все остальное - интеллектуальная мастурбация.
Кроме того, это, вероятно, также лучшее решение, так как это единственное из всех предложений, которое вызывает короткое замыкание. Если a + b == c
выполняется только одно сложение и сравнение.
Решая три равенства для a:
a in (b+c, b-c, c-b)
В Python есть any
функция, которая воздействует or
на все элементы последовательности. Здесь я преобразовал ваше утверждение в кортеж из трех элементов.
any((a + b == c, a + c == b, b + c == a))
Обратите внимание, что or
это короткое замыкание, поэтому, если расчет индивидуальных условий дорогостоящий, может быть лучше сохранить исходную конструкцию.
any()
и all()
короткое замыкание тоже.
any
того, как запустится.
any
и all
"закоротить" процесс проверки итерации, которую они дают; но если эта итерация является последовательностью, а не генератором, то она уже была полностью оценена перед вызовом функции .
any
, одинарный отступ ):
в if
операторе), что очень помогает для удобочитаемости, когда задействована математика
Если вы знаете, что имеете дело только с положительными числами, это сработает и довольно чисто:
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, и, возможно, это наиболее эффективный способ сделать это (не говоря уже о дополнительном вызове функции); хотя в любом случае не стоит слишком беспокоиться о производительности, если только она не вызывает проблемы.
Если вы будете использовать только три переменные, тогда ваш исходный метод:
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
И это будет держать верно для любого а, Ь, или с, что означает , что да она будет равна 2*a
, 2*b
или 2*c
. Это будет верно для любого количества переменных.
Поэтому хороший способ быстро написать это - просто иметь список ваших переменных и сравнивать их сумму со списком удвоенных значений.
values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])
Таким образом, чтобы добавить больше переменных в уравнение, все, что вам нужно сделать, это отредактировать список значений, добавив n новых переменных, а не писать n уравнений.
a=-1
, b=-1
, c=-2
, тогда a+b=c
, но a+b+c = -4
и 2*max(a,b,c)
это-2
abs()
вызовов это Pythonic, чем фрагмент OP (на самом деле я бы назвал его значительно менее читаемым).
any(sum(values) == 2*x for x in values)
, чтобы вам не пришлось делать все дублирование заранее, когда это необходимо.
Следующий код можно использовать для итеративного сравнения каждого элемента с суммой других, которая вычисляется из суммы всего списка, исключая этот элемент.
l = [a,b,c]
any(sum(l)-e == e for e in l)
[]
скобки из второй строки, это приведет к короткому замыканию, как в оригинале, с or
...
any(a + b + c == 2*x for x in [a, b, c])
, довольно близко к предложению ОП
Не пытайтесь его упростить. Вместо этого назовите то, что вы делаете с функцией:
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):
...
Замена условия на что-нибудь «умное» может сделать его короче, но не сделает его более читабельным. Однако оставить его в таком виде тоже не очень удобно, потому что сложно сразу понять, почему вы проверяете эти три условия. Это делает абсолютно ясным, что вы проверяете.
Что касается производительности, этот подход действительно увеличивает накладные расходы на вызов функции, но никогда не жертвует удобочитаемостью ради производительности, если вы не нашли узкое место, которое необходимо исправить. И всегда измеряйте, поскольку некоторые умные реализации могут оптимизировать и встраивать некоторые вызовы функций в некоторых случаях.
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
Однако в целом я согласен с тем, что исходная версия более читабельна, если у вас не более трех переменных.
[x for x in range(pow(2,30)) if x != ((x * 2)/ pow(2,1))]
(a+b-c)*(a+c-b)*(b+c-a) == 0
Если сумма любых двух членов равна третьему члену, то один из множителей будет равен нулю, в результате чего весь продукт будет равен нулю.
(a+b<>c) && (a+c<>b) && (b+c<>a) == false
Как насчет всего:
a == b + c or abs(a) == abs(b - c)
Обратите внимание, что это не сработает, если переменные беззнаковые.
С точки зрения оптимизации кода (по крайней мере, на платформе x86) это кажется наиболее эффективным решением.
Современные компиляторы встраивают вызовы функций abs () и избегают проверки знаков и последующего условного перехода, используя продуманную последовательность инструкций CDQ, XOR и SUB . Таким образом, приведенный выше высокоуровневый код будет представлен только инструкциями ALU с низкой задержкой, высокой пропускной способностью и всего двумя условными выражениями.
fabs()
можно использовать для float
типов;).
Решение, предоставленное Алексом Варгой «a in (b + c, bc, cb)», компактно и математически красиво, но на самом деле я бы не стал писать код таким образом, потому что следующий разработчик не сразу поймет цель кода. .
Решение Марка Рэнсома
any((a + b == c, a + c == b, b + c == a))
яснее, но не намного кратче, чем
a + b == c or a + c == b or b + c == a
При написании кода, на который придется взглянуть кому-то другому или на который мне придется взглянуть еще долгое время, когда я забуду, о чем думал, когда писал его, слишком короткий или умный подход приносит больше вреда, чем пользы. Код должен быть читабельным. Столь сжато - это хорошо, но не настолько кратко, чтобы следующий программист не мог этого понять.
Запрос на более компактный ИЛИ более питонический - я пробовал более компактный.
дано
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))))
g()
вы должны определить, чтобы это работало. Учитывая все это, я бы сказал, что он значительно больше.
Я хочу представить то, что считаю наиболее питоническим ответом:
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, я думаю, что подчеркнутые утверждения более следуют, чем из другого ответа:
Дзен Питона, Тим Питерс
Красивое лучше уродливого.
Явное лучше, чем неявное.
Лучше простое, чем сложное.
Сложный лучше, чем сложный.
Плоский лучше, чем вложенный.
Лучше разреженное, чем плотное.
Читаемость имеет значение.
Особых случаев недостаточно, чтобы нарушать правила.
Хотя практичность лучше чистоты.
Ошибки никогда не должны проходить незаметно.
Если явно не отключен.
Перед лицом двусмысленности откажитесь от соблазна угадать.
Должен быть один - а желательно только один - очевидный способ сделать это.
Хотя сначала этот способ может быть не очевиден, если вы не голландец.
Лучше сейчас, чем никогда.
Хотя никогда не бывает лучше, чемпрямо сейчас.
Если реализацию трудно объяснить, это плохая идея.
Если реализацию легко объяснить, это может быть хорошей идеей.
Пространства имен - одна отличная идея - давайте сделаем их больше!
Как старая привычка моего программирования, я считаю, что размещение сложного выражения справа в предложении может сделать его более читаемым, например:
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))
В общем,
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