Esiste un modo più compatto o pitonico per scrivere l'espressione booleana
a + b == c or a + c == b or b + c == a
Mi è venuta in mente
a + b + c in (2*a, 2*b, 2*c)
ma è un po 'strano.
Esiste un modo più compatto o pitonico per scrivere l'espressione booleana
a + b == c or a + c == b or b + c == a
Mi è venuta in mente
a + b + c in (2*a, 2*b, 2*c)
ma è un po 'strano.
Risposte:
Se guardiamo allo Zen di Python, enfatizza il mio:
Lo Zen di Python, di Tim Peters
Bello è meglio che brutto.
Esplicito è meglio che implicito.
Semplice è meglio di complesso.
Complesso è meglio che complicato.
Flat è meglio di nidificato.
Sparse è meglio che denso.
La leggibilità conta.
I casi speciali non sono abbastanza speciali da infrangere le regole.
Sebbene la praticità superi la purezza.
Gli errori non dovrebbero mai passare silenziosamente.
A meno che non sia esplicitamente messo a tacere.
Di fronte all'ambiguità, rifiuta la tentazione di indovinare.
Dovrebbe esserci uno - e preferibilmente solo un - modo obsoleto di farlo.
Anche se in quel modo all'inizio potrebbe non essere ovvio a meno che tu non sia olandese.
Adesso è meglio che mai.
Anche se spesso non è mai meglio diproprio ora
Se l'implementazione è difficile da spiegare, è una cattiva idea.
Se l'implementazione è facile da spiegare, potrebbe essere una buona idea.
Gli spazi dei nomi sono un'ottima idea per suonare il clacson: facciamo di più!
La soluzione più Pythonic è quella più chiara, semplice e facile da spiegare:
a + b == c or a + c == b or b + c == a
Ancora meglio, non hai nemmeno bisogno di conoscere Python per capire questo codice! È così facile Questa è, senza riserve, la migliore soluzione. Qualcos'altro è la masturbazione intellettuale.
Inoltre, questa è probabilmente anche la soluzione più performante, in quanto è l'unica tra tutte le proposte a cortocircuitare. Se a + b == c
viene eseguita solo una singola aggiunta e confronto.
Risolvere le tre uguaglianze per un:
a in (b+c, b-c, c-b)
Python ha una any
funzione che fa una or
su tutti gli elementi di una sequenza. Qui ho convertito la tua dichiarazione in una tupla a 3 elementi.
any((a + b == c, a + c == b, b + c == a))
Si noti che or
è un corto circuito, quindi se il calcolo delle singole condizioni è costoso, potrebbe essere meglio mantenere il costrutto originale.
any()
e anche in all()
corto circuito.
any
anche prima dell'esecuzione.
any
e all
"cortocircuito" il processo di esame dell'iterabile che viene loro dato; ma se quell'iterabile è una sequenza piuttosto che un generatore, allora è già stato completamente valutato prima che si verifichi la chiamata di funzione .
any
, single-trattino l' ):
nel if
comunicato), che aiuta molto per la leggibilità quando si tratta di matematica
Se sai che hai a che fare solo con numeri positivi, funzionerà ed è abbastanza pulito:
a, b, c = sorted((a, b, c))
if a + b == c:
do_stuff()
Come ho detto, questo funziona solo con numeri positivi; ma se sai che saranno positivi, questa è una soluzione IMO molto leggibile, anche direttamente nel codice invece che in una funzione.
Potresti farlo, il che potrebbe fare un po 'di calcolo ripetuto; ma non hai specificato le prestazioni come obiettivo:
from itertools import permutations
if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
do_stuff()
O senza permutations()
e la possibilità di calcoli ripetuti:
if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
do_stuff()
Probabilmente metterei questa o qualsiasi altra soluzione in una funzione. Quindi puoi semplicemente chiamare chiaramente la funzione nel tuo codice.
Personalmente, a meno che non avessi bisogno di maggiore flessibilità dal codice, utilizzerei solo il primo metodo nella tua domanda. È semplice ed efficiente. Potrei ancora metterlo in una funzione:
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()
Questo è piuttosto Pythonic, ed è probabilmente il modo più efficiente per farlo (la funzione extra chiama a parte); anche se non dovresti preoccuparti troppo delle prestazioni, a meno che non stia effettivamente causando un problema.
Se utilizzerai solo tre variabili, il tuo metodo iniziale:
a + b == c or a + c == b or b + c == a
È già molto pitonico.
Se prevedi di utilizzare più variabili, il tuo metodo di ragionamento con:
a + b + c in (2*a, 2*b, 2*c)
È molto intelligente ma pensiamo al perché. Perché funziona?
Bene attraverso qualche semplice aritmetica vediamo che:
a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c
E questo dovrà valere sia per a, b, c, nel senso che sì, sarà uguale 2*a
, 2*b
o 2*c
. Questo sarà vero per qualsiasi numero di variabili.
Quindi un buon modo per scrivere rapidamente sarebbe semplicemente avere un elenco delle variabili e verificare la loro somma con un elenco dei valori raddoppiati.
values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])
In questo modo, per aggiungere più variabili all'equazione, tutto ciò che devi fare è modificare la tua lista di valori con 'n' nuove variabili, non scrivere 'n' equazioni
a=-1
, b=-1
, c=-2
, quindi a+b=c
, ma a+b+c = -4
e 2*max(a,b,c)
è-2
abs()
chiamate, è Pythonic rispetto allo snippet dell'OP (in realtà lo definirei significativamente meno leggibile).
any(sum(values) == 2*x for x in values)
, in questo modo non dovresti fare tutto il raddoppio in anticipo, se necessario.
Il seguente codice può essere usato per confrontare iterativamente ogni elemento con la somma degli altri, che viene calcolata dalla somma dell'intero elenco, escludendo quell'elemento.
l = [a,b,c]
any(sum(l)-e == e for e in l)
[]
parentesi dalla seconda riga, questo potrebbe anche cortocircuitare come l'originale con or
...
any(a + b + c == 2*x for x in [a, b, c])
, abbastanza vicino al suggerimento del PO
Non provare a semplificarlo. Invece, dai un nome a cosa stai facendo con una funzione:
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):
...
Sostituire la condizione con qualcosa di "intelligente" potrebbe renderla più breve, ma non renderla più leggibile. Lasciarlo così com'è non è comunque molto leggibile, perché è difficile sapere il perché stai controllando queste tre condizioni a colpo d'occhio. Questo rende assolutamente cristallino ciò che stai cercando.
Per quanto riguarda le prestazioni, questo approccio aggiunge l'overhead di una chiamata di funzione, ma non sacrifica mai la leggibilità per le prestazioni a meno che non sia stato trovato un collo di bottiglia che è assolutamente necessario correggere. E misura sempre, poiché alcune implementazioni intelligenti sono in grado di ottimizzare e integrare alcune chiamate di funzione in alcune circostanze.
Python 3:
(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...
Si adatta a qualsiasi numero di variabili:
arr = [a,b,c,d,...]
sum(arr)/2 in arr
Tuttavia, in generale, sono d'accordo sul fatto che, a meno che tu non abbia più di tre variabili, la versione originale è più leggibile.
[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
Se la somma di due termini qualsiasi è uguale al terzo termine, uno dei fattori sarà zero, rendendo l'intero prodotto zero.
(a+b<>c) && (a+c<>b) && (b+c<>a) == false
Che ne dici di:
a == b + c or abs(a) == abs(b - c)
Nota che questo non funzionerà se le variabili non sono firmate.
Dal punto di vista dell'ottimizzazione del codice (almeno sulla piattaforma x86) questa sembra essere la soluzione più efficiente.
I compilatori moderni incorporeranno entrambe le chiamate alle funzioni abs () ed eviteranno il test dei segni e il successivo ramo condizionale utilizzando una sequenza intelligente di istruzioni CDQ, XOR e SUB . Il suddetto codice di alto livello verrà quindi rappresentato con solo istruzioni ALU a bassa latenza e alta produttività e solo due condizionali.
fabs()
possa essere usato per i float
tipi;).
La soluzione fornita da Alex Varga "a in (b + c, bc, cb)" è compatta e matematicamente bella, ma in realtà non scriverei il codice in questo modo perché il prossimo sviluppatore che arriva non capirà immediatamente lo scopo del codice .
La soluzione di Mark Ransom di
any((a + b == c, a + c == b, b + c == a))
è più chiaro ma non molto più conciso di
a + b == c or a + c == b or b + c == a
Quando scrivo un codice che qualcun altro dovrà guardare, o che dovrò guardare molto tempo dopo, quando ho dimenticato cosa stavo pensando quando l'ho scritto, essere troppo basso o intelligente tende a fare più male che bene. Il codice dovrebbe essere leggibile. Così succinto è buono, ma non così succinto che il prossimo programmatore non può capirlo.
La richiesta è più compatta O più pitonica - ho provato a essere più compatta.
dato
import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
return x + y == z
Questo è di 2 caratteri in meno rispetto all'originale
any(g(*args) for args in f((a,b,c)))
prova con:
assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)
inoltre, dato:
h = functools.partial(itertools.starmap, g)
Questo è equivalente
any(h(f((a,b,c))))
g()
devi definire affinché funzioni. Detto questo, direi che è significativamente più grande.
Voglio presentare quella che vedo come la risposta più pitonica :
def one_number_is_the_sum_of_the_others(a, b, c):
return any((a == b + c, b == a + c, c == a + b))
Il caso generale, non ottimizzato:
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
In termini di Zen di Python, penso che le affermazioni enfatizzate siano più seguite che da altre risposte:
Lo Zen di Python, di Tim Peters
Bello è meglio che brutto.
Esplicito è meglio che implicito.
Semplice è meglio di complesso.
Complesso è meglio che complicato.
Flat è meglio di nidificato.
Sparse è meglio che denso.
La leggibilità conta.
I casi speciali non sono abbastanza speciali da infrangere le regole.
Sebbene la praticità superi la purezza.
Gli errori non dovrebbero mai passare silenziosamente.
A meno che non sia esplicitamente messo a tacere.
Di fronte all'ambiguità, rifiuta la tentazione di indovinare.
Dovrebbe esserci uno - e preferibilmente solo un - modo obsoleto di farlo.
Anche se in quel modo potrebbe non essere ovvio all'inizio a meno che tu non sia olandese.
Adesso è meglio che mai.
Anche se spesso non è mai meglio diproprio ora
Se l'implementazione è difficile da spiegare, è una cattiva idea.
Se l'implementazione è facile da spiegare, potrebbe essere una buona idea.
Gli spazi dei nomi sono un'ottima idea per suonare il clacson: facciamo di più!
Come una vecchia abitudine della mia programmazione, penso che posizionare un'espressione complessa a destra in una clausola possa renderla più leggibile in questo modo:
a == b+c or b == a+c or c == a+b
Inoltre ()
:
((a == b+c) or (b == a+c) or (c == a+b))
E penso anche che l'uso di linee multiple possa anche fare più sensi in questo modo:
((a == b+c) or
(b == a+c) or
(c == a+b))
In modo generico,
m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();
se, manipolare una variabile di input è giusto per te,
c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();
se vuoi sfruttare i bit hack, puoi usare "!", ">> 1" e "<< 1"
Ho evitato la divisione anche se mi consente di evitare due moltiplicazioni per evitare errori di arrotondamento. Tuttavia, controllare gli overflow
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