Czy istnieje bardziej zwarty lub pythoniczny sposób zapisu wyrażenia boolowskiego
a + b == c or a + c == b or b + c == a
wymyśliłem
a + b + c in (2*a, 2*b, 2*c)
ale to jest trochę dziwne.
Czy istnieje bardziej zwarty lub pythoniczny sposób zapisu wyrażenia boolowskiego
a + b == c or a + c == b or b + c == a
wymyśliłem
a + b + c in (2*a, 2*b, 2*c)
ale to jest trochę dziwne.
Odpowiedzi:
Jeśli spojrzymy na Zen Pythona, podkreśl moje:
Zen Pythona - Tim Peters
Piękne jest lepsze niż brzydkie.
Jawne jest lepsze niż niejawne.
Proste jest lepsze niż złożone.
Złożone jest lepsze niż skomplikowane.
Płaskie jest lepsze niż zagnieżdżone.
Rzadkie jest lepsze niż gęste.
Liczy się czytelność.
Specjalne przypadki nie są na tyle wyjątkowe, aby łamać zasady.
Chociaż praktyczność przewyższa czystość.
Błędy nigdy nie powinny przejść bezgłośnie.
Chyba że wyraźnie uciszono.
W obliczu niejasności odrzuć pokusę zgadywania.
Powinien być jeden - a najlepiej tylko jeden - oczywisty sposób na zrobienie tego.
Chociaż na początku może to nie być oczywiste, chyba że jesteś Holendrem.
Teraz jest lepiej niż nigdy.
Chociaż nigdy nie jest często lepsze niżprawo teraz.
Jeśli implementacja jest trudna do wyjaśnienia, to zły pomysł.
Jeśli implementacja jest łatwa do wyjaśnienia, może to być dobry pomysł.
Przestrzenie nazw to świetny pomysł - zróbmy ich więcej!
Najbardziej Pythonowe rozwiązanie to takie, które jest najbardziej przejrzyste, najprostsze i najłatwiejsze do wyjaśnienia:
a + b == c or a + c == b or b + c == a
Co więcej, nie musisz nawet znać języka Python, aby zrozumieć ten kod! To takie proste. To bez zastrzeżeń najlepsze rozwiązanie. Wszystko inne to intelektualna masturbacja.
Co więcej, jest to prawdopodobnie również najlepsze rozwiązanie, ponieważ jest to jedyna ze wszystkich propozycji, która powoduje zwarcia. Jeśli a + b == c
wykonuje się tylko jedno dodanie i porównanie.
Rozwiązanie trzech równości dla:
a in (b+c, b-c, c-b)
Python ma any
funkcję, która wykonuje operacje or
na wszystkich elementach sekwencji. Tutaj przekonwertowałem twoje oświadczenie na krotkę 3-elementową.
any((a + b == c, a + c == b, b + c == a))
Zwróć uwagę, że or
jest to zwarcie, więc jeśli obliczenie indywidualnych warunków jest kosztowne, może być lepiej zachować oryginalną konstrukcję.
any()
i all()
zwarcie też.
any
jeszcze przed uruchomieniem.
any
i all
„zewrzyj” proces sprawdzania iterowalności, którą otrzymają; ale jeśli ta iterowalna jest sekwencją, a nie generatorem, to została już w pełni oceniona przed wywołaniem funkcji .
any
, pojedyncze wcięcie ):
w if
instrukcji), co bardzo pomaga w czytelności, gdy w
Jeśli wiesz, że masz do czynienia tylko z liczbami dodatnimi, to zadziała i jest całkiem jasne:
a, b, c = sorted((a, b, c))
if a + b == c:
do_stuff()
Jak powiedziałem, działa to tylko w przypadku liczb dodatnich; ale jeśli wiesz, że będą pozytywne, jest to bardzo czytelne rozwiązanie IMO, nawet bezpośrednio w kodzie, a nie w funkcji.
Mógłbyś to zrobić, co może wykonać trochę powtarzających się obliczeń; ale nie określiłeś skuteczności jako celu:
from itertools import permutations
if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
do_stuff()
Lub bez permutations()
i możliwość wielokrotnych obliczeń:
if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
do_stuff()
Prawdopodobnie umieściłbym to lub jakiekolwiek inne rozwiązanie w funkcji. Następnie możesz po prostu czysto wywołać funkcję w swoim kodzie.
Osobiście, jeśli nie potrzebowałbym większej elastyczności od kodu, użyłbym tylko pierwszej metody w twoim pytaniu. To proste i wydajne. Nadal mógłbym umieścić to w funkcji:
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()
To całkiem Pythonic i jest to prawdopodobnie najbardziej efektywny sposób na zrobienie tego (pomijając dodatkowe wywołanie funkcji); chociaż i tak nie powinieneś martwić się zbytnio o wydajność, chyba że faktycznie powoduje to problem.
Jeśli będziesz używać tylko trzech zmiennych, to Twoja metoda początkowa:
a + b == c or a + c == b or b + c == a
Jest już bardzo pytoniczny.
Jeśli planujesz używać większej liczby zmiennych, zastosuj metodę rozumowania z:
a + b + c in (2*a, 2*b, 2*c)
Jest bardzo sprytny, ale zastanówmy się, dlaczego. Dlaczego to działa?
Cóż, dzięki prostej arytmetyce widzimy, że:
a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c
I to będzie musiał uznać za prawdziwe dla A, B lub C, co oznacza, że tak będzie równa 2*a
, 2*b
lub 2*c
. Będzie to prawdą dla dowolnej liczby zmiennych.
Więc dobrym sposobem na szybkie napisanie tego byłoby po prostu sporządzenie listy swoich zmiennych i porównanie ich sumy z listą podwojonych wartości.
values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])
W ten sposób, aby dodać więcej zmiennych do równania, wszystko, co musisz zrobić, to edytować listę wartości za pomocą „n” nowych zmiennych, a nie pisać „n” równań
a=-1
, b=-1
, c=-2
, a następnie a+b=c
, ale a+b+c = -4
i 2*max(a,b,c)
to-2
abs()
wywołań, jest to Pythonic niż fragment OP (tak naprawdę nazwałbym go znacznie mniej czytelnym).
any(sum(values) == 2*x for x in values)
, w ten sposób nie musiałbyś robić całego podwajania z góry, tak jak jest to konieczne.
Poniższy kod może służyć do iteracyjnego porównywania każdego elementu z sumą pozostałych, która jest obliczana z sumy całej listy, z wyłączeniem tego elementu.
l = [a,b,c]
any(sum(l)-e == e for e in l)
[]
nawiasy z drugiej linii to nawet zwarcie jak oryginał z or
...
any(a + b + c == 2*x for x in [a, b, c])
dość bliskie sugestii PO
Nie próbuj tego upraszczać. Zamiast tego nazwij to, co robisz za pomocą funkcji:
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):
...
Zastąpienie warunku czymś „sprytnym” może skrócić go, ale nie uczyni go bardziej czytelnym. Pozostawienie tego tak, jak jest, również nie jest zbyt czytelne, ponieważ trudno jest wiedzieć, dlaczego sprawdzasz te trzy warunki na pierwszy rzut oka. Dzięki temu jest absolutnie jasne, czego szukasz.
Jeśli chodzi o wydajność, to podejście dodaje narzut wywołania funkcji, ale nigdy nie poświęcaj czytelności dla wydajności, chyba że znalazłeś wąskie gardło, które absolutnie musisz naprawić. I zawsze mierz, ponieważ niektóre sprytne implementacje są w stanie zoptymalizować i wstawić niektóre wywołania funkcji w pewnych okolicznościach.
Python 3:
(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...
Skaluje się do dowolnej liczby zmiennych:
arr = [a,b,c,d,...]
sum(arr)/2 in arr
Jednak generalnie zgadzam się, że jeśli nie masz więcej niż trzech zmiennych, oryginalna wersja jest bardziej czytelna.
[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
Jeśli suma dowolnych dwóch składników jest równa trzeciemu składnikowi, to jeden z czynników będzie wynosił zero, co spowoduje, że cały iloczyn będzie równy zero.
(a+b<>c) && (a+c<>b) && (b+c<>a) == false
Może po prostu:
a == b + c or abs(a) == abs(b - c)
Zauważ, że to nie zadziała, jeśli zmienne są bez znaku.
Z punktu widzenia optymalizacji kodu (przynajmniej na platformie x86) wydaje się to być najbardziej wydajnym rozwiązaniem.
Nowoczesne kompilatory wbudowują wywołania funkcji abs () i unikają testowania znaków i późniejszej gałęzi warunkowej, używając sprytnej sekwencji instrukcji CDQ, XOR i SUB . Powyższy kod wysokiego poziomu będzie zatem reprezentowany tylko za pomocą instrukcji ALU o małym opóźnieniu, dużej przepustowości i tylko dwóch instrukcji warunkowych.
fabs()
można to wykorzystać do float
typów;).
Rozwiązanie dostarczone przez Alexa Vargę „a in (b + c, bc, cb)” jest zwarte i matematycznie piękne, ale nie pisałbym w ten sposób kodu, ponieważ następny programista nie zrozumiałby od razu celu kodu .
Rozwiązanie Mark Ransom dla
any((a + b == c, a + c == b, b + c == a))
jest bardziej jasny, ale niewiele bardziej zwięzły niż
a + b == c or a + c == b or b + c == a
Pisząc kod, któremu ktoś inny będzie musiał przyjrzeć się, lub na który będę musiał przyjrzeć się długo później, kiedy zapomniałem, o czym myślałem, kiedy go pisałem, bycie zbyt krótkim lub sprytnym powoduje więcej szkody niż pożytku. Kod powinien być czytelny. Tak zwięzły jest dobry, ale nie tak zwięzły, żeby następny programista nie mógł tego zrozumieć.
Żądanie jest bardziej zwarte LUB bardziej pythonowe - próbowałem swoich sił w bardziej zwartym.
dany
import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
return x + y == z
To o 2 znaki mniej niż oryginał
any(g(*args) for args in f((a,b,c)))
test z:
assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)
dodatkowo podane:
h = functools.partial(itertools.starmap, g)
To jest równoważne
any(h(f((a,b,c))))
g()
którą musisz zdefiniować, aby to zadziałało. Biorąc to wszystko pod uwagę, powiedziałbym, że jest znacznie większy.
Chcę przedstawić to, co uważam za najbardziej pytoniczną odpowiedź:
def one_number_is_the_sum_of_the_others(a, b, c):
return any((a == b + c, b == a + c, c == a + b))
Ogólny przypadek, niezoptymalizowany:
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
Jeśli chodzi o Zen Pythona, myślę, że podkreślone stwierdzenia są bardziej przestrzegane niż w przypadku innych odpowiedzi:
Zen Pythona - Tim Peters
Piękne jest lepsze niż brzydkie.
Jawne jest lepsze niż niejawne.
Proste jest lepsze niż złożone.
Złożone jest lepsze niż skomplikowane.
Płaskie jest lepsze niż zagnieżdżone.
Rzadkie jest lepsze niż gęste.
Liczy się czytelność.
Specjalne przypadki nie są na tyle wyjątkowe, aby łamać zasady.
Chociaż praktyczność przewyższa czystość.
Błędy nigdy nie powinny przejść bezgłośnie.
Chyba że wyraźnie uciszono.
W obliczu niejasności odrzuć pokusę zgadywania.
Powinien być jeden - a najlepiej tylko jeden - oczywisty sposób na zrobienie tego.
Chociaż na początku może to nie być oczywiste, chyba że jesteś Holendrem.
Teraz jest lepiej niż nigdy.
Chociaż nigdy nie jest często lepsze niżprawo teraz.
Jeśli implementacja jest trudna do wyjaśnienia, to zły pomysł.
Jeśli implementacja jest łatwa do wyjaśnienia, może to być dobry pomysł.
Przestrzenie nazw to świetny pomysł - zróbmy ich więcej!
Zgodnie ze starym nawykiem mojego programowania, myślę, że umieszczanie złożonego wyrażenia po prawej stronie w klauzuli może uczynić ją bardziej czytelną w następujący sposób:
a == b+c or b == a+c or c == a+b
Plus ()
:
((a == b+c) or (b == a+c) or (c == a+b))
Myślę też, że używanie multilinii może wywołać więcej zmysłów, takich jak ten:
((a == b+c) or
(b == a+c) or
(c == a+b))
W ogólny sposób
m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();
jeśli manipulowanie zmienną wejściową jest dla Ciebie OK,
c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();
jeśli chcesz wykorzystać bit hacki, możesz użyć "!", ">> 1" i "<< 1"
Uniknąłem dzielenia, chociaż pozwala to na uniknięcie podwójnego mnożenia, aby uniknąć błędów zaokrągleń. Jednak sprawdź, czy nie ma przepełnień
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