Чи є більш компактний чи пітонічний спосіб написати булевий вираз
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
всіх елементів послідовності. Тут я перетворив ваше твердження в 3-елементний кортеж.
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()
Це досить піфонічно, і це, можливо, найефективніший спосіб це зробити (додаткова функція викликає сторону); хоча ви не повинні занадто сильно турбуватися про продуктивність, якщо це насправді не викликає проблеми.
Якщо ви будете використовувати лише три змінні, то ваш початковий метод:
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()
дзвінків він стає пітонічним, ніж фрагмент ОП (я б фактично назвав його значно менш читабельним).
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):
...
Замініть умову чимось «розумним», можливо, скоротити її, але це не зробить його більш читабельним. Однак залишити його таким, яким він є, не дуже легко читати, тому що складно знати, чому ти перевіряєш ці три умови з першого погляду. Це робить абсолютно кристально зрозумілим, на що ви перевіряєте.
Що стосується продуктивності, то цей підхід додає накладні витрати функціонального виклику, але ніколи не жертвуйте читабельністю для продуктивності, якщо ви не знайшли вузьке місце, яке ви абсолютно повинні виправити. І завжди вимірюйте, так як деякі розумні реалізації здатні в деяких умовах оптимізувати та вбудувати деякі виклики функцій.
Пітон 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