Existe-t-il une manière plus compacte ou pythonique d'écrire l'expression booléenne
a + b == c or a + c == b or b + c == a
Je suis venu avec
a + b + c in (2*a, 2*b, 2*c)
mais c'est un peu étrange.
Existe-t-il une manière plus compacte ou pythonique d'écrire l'expression booléenne
a + b == c or a + c == b or b + c == a
Je suis venu avec
a + b + c in (2*a, 2*b, 2*c)
mais c'est un peu étrange.
Réponses:
Si nous regardons le Zen de Python, soulignons le mien:
Le Zen de Python, par Tim Peters
Beau est mieux que laid.
L'explicite vaut mieux que l'implicite.
Le simple vaut mieux que le complexe.
Complexe vaut mieux que compliqué.
Plat vaut mieux que niché.
Clairsemé vaut mieux que dense.
La lisibilité compte.
Les cas spéciaux ne sont pas assez spéciaux pour enfreindre les règles.
Bien que l'aspect pratique l'emporte sur la pureté.
Les erreurs ne devraient jamais passer silencieusement.
À moins d'être explicitement réduit au silence.
Face à l'ambiguïté, refusez la tentation de deviner.
Il devrait y avoir une - et de préférence une seule - façon évidente de le faire.
Bien que cette manière ne soit pas évidente au début, sauf si vous êtes néerlandais.
C'est mieux que jamais.
Bien que jamais ne soit souvent mieux quedès maintenant.
Si l'implémentation est difficile à expliquer, c'est une mauvaise idée.
Si la mise en œuvre est facile à expliquer, cela peut être une bonne idée.
Les espaces de noms sont une excellente idée - faisons-en plus!
La solution la plus pythonique est celle qui est la plus claire, la plus simple et la plus facile à expliquer:
a + b == c or a + c == b or b + c == a
Mieux encore, vous n'avez même pas besoin de connaître Python pour comprendre ce code! C'est aussi simple que ça . C'est, sans réserve, la meilleure solution. Tout le reste est de la masturbation intellectuelle.
De plus, il s'agit probablement de la solution la plus performante, car c'est la seule parmi toutes les propositions qui court-circuite. Si a + b == c
, une seule addition et comparaison est effectuée.
Résoudre les trois égalités pour un:
a in (b+c, b-c, c-b)
Python a une any
fonction qui fait un or
sur tous les éléments d'une séquence. Ici, j'ai converti votre déclaration en un tuple à 3 éléments.
any((a + b == c, a + c == b, b + c == a))
Notez qu'il or
s'agit d'un court-circuit, donc si le calcul des conditions individuelles est coûteux, il peut être préférable de conserver votre conception d'origine.
any()
et all()
court-circuit aussi.
any
même de s'exécuter.
any
et all
«court-circuiter» le processus d' examen de l'itérable qui leur est donné; mais si cet itérable est une séquence plutôt qu'un générateur, alors il a déjà été entièrement évalué avant que l'appel de fonction ne se produise .
any
, simple indentation ):
dans l' if
instruction), ce qui aide beaucoup à la lisibilité lorsque les mathématiques sont impliquées
Si vous savez que vous ne traitez qu'avec des nombres positifs, cela fonctionnera et est assez propre:
a, b, c = sorted((a, b, c))
if a + b == c:
do_stuff()
Comme je l'ai dit, cela ne fonctionne que pour les nombres positifs; mais si vous savez qu'ils vont être positifs, c'est une solution IMO très lisible, même directement dans le code plutôt que dans une fonction.
Vous pourriez faire ceci, ce qui pourrait faire un peu de calcul répété; mais vous n'avez pas spécifié les performances comme objectif:
from itertools import permutations
if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
do_stuff()
Ou sans permutations()
et possibilité de calculs répétés:
if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
do_stuff()
Je mettrais probablement ceci, ou toute autre solution, dans une fonction. Ensuite, vous pouvez simplement appeler proprement la fonction dans votre code.
Personnellement, à moins que j'aie besoin de plus de flexibilité du code, j'utiliserais simplement la première méthode de votre question. C'est simple et efficace. Je pourrais encore le mettre dans une fonction:
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()
C'est assez pythonique, et c'est probablement le moyen le plus efficace de le faire (l'appel de fonction supplémentaire de côté); bien que vous ne devriez pas trop vous soucier des performances de toute façon, à moins que cela ne cause réellement un problème.
Si vous n'utilisez que trois variables, votre méthode initiale:
a + b == c or a + c == b or b + c == a
Est déjà très pythonique.
Si vous prévoyez d'utiliser plus de variables, votre méthode de raisonnement avec:
a + b + c in (2*a, 2*b, 2*c)
Est très intelligent mais réfléchissons à pourquoi. Pourquoi ça marche?
Eh bien, grâce à une simple arithmétique, nous voyons que:
a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c
Et cela devra être vrai pour a, b, c, ce qui signifie que oui il sera égal 2*a
, 2*b
ou 2*c
. Cela sera vrai pour n'importe quel nombre de variables.
Donc, une bonne façon d'écrire cela rapidement serait d'avoir simplement une liste de vos variables et de vérifier leur somme par rapport à une liste des valeurs doublées.
values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])
De cette façon, pour ajouter plus de variables dans l'équation, tout ce que vous avez à faire est de modifier votre liste de valeurs par 'n' nouvelles variables, et non d'écrire 'n' équations
a=-1
, b=-1
, c=-2
, puis a+b=c
, mais a+b+c = -4
et 2*max(a,b,c)
est-2
abs()
appels, c'est Pythonic que l'extrait de l'OP (je l'appellerais en fait beaucoup moins lisible).
any(sum(values) == 2*x for x in values)
, de cette façon, vous n'auriez pas à faire tout le doublement à l'avant, tout aussi nécessaire.
Le code suivant peut être utilisé pour comparer de manière itérative chaque élément avec la somme des autres, qui est calculée à partir de la somme de la liste entière, à l'exclusion de cet élément.
l = [a,b,c]
any(sum(l)-e == e for e in l)
[]
supports de la deuxième ligne, cela va même court-circuiter comme l'original avec or
...
any(a + b + c == 2*x for x in [a, b, c])
assez proche de la suggestion du PO
N'essayez pas de le simplifier. À la place, nommez ce que vous faites avec une fonction:
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):
...
Remplacer la condition par quelque chose de "intelligent" pourrait la raccourcir, mais cela ne la rendra pas plus lisible. Le laisser tel quel n'est pas non plus très lisible, car il est difficile de savoir pourquoi vous vérifiez ces trois conditions en un coup d'œil. Cela rend parfaitement clair ce que vous vérifiez.
En ce qui concerne les performances, cette approche ajoute la surcharge d'un appel de fonction, mais ne sacrifie jamais la lisibilité pour les performances, sauf si vous avez trouvé un goulot d'étranglement que vous devez absolument corriger. Et mesurez toujours, car certaines implémentations intelligentes sont capables d'optimiser et d'incorporer certains appels de fonction dans certaines circonstances.
Python 3:
(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...
Il s'adapte à n'importe quel nombre de variables:
arr = [a,b,c,d,...]
sum(arr)/2 in arr
Cependant, en général, je conviens qu'à moins que vous n'ayez plus de trois variables, la version originale est plus lisible.
[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
Si la somme de deux termes est égale au troisième terme, l'un des facteurs sera égal à zéro, ce qui rendra le produit entier nul.
(a+b<>c) && (a+c<>b) && (b+c<>a) == false
Que diriez-vous simplement:
a == b + c or abs(a) == abs(b - c)
Notez que cela ne fonctionnera pas si les variables ne sont pas signées.
Du point de vue de l'optimisation du code (au moins sur la plate-forme x86), cela semble être la solution la plus efficace.
Les compilateurs modernes intégreront les deux appels de fonction abs () et éviteront les tests de signe et la branche conditionnelle ultérieure en utilisant une séquence intelligente d'instructions CDQ, XOR et SUB . Le code de haut niveau ci-dessus sera donc représenté avec uniquement des instructions ALU à faible latence, à haut débit et seulement deux conditions.
fabs()
peut être utilisé pour les float
types;).
La solution fournie par Alex Varga "a in (b + c, bc, cb)" est compacte et mathématiquement belle, mais je n'écrirais pas réellement le code de cette façon parce que le prochain développeur à venir ne comprendrait pas immédiatement le but du code .
La solution de Mark Ransom de
any((a + b == c, a + c == b, b + c == a))
est plus clair mais pas beaucoup plus succinct que
a + b == c or a + c == b or b + c == a
Lors de l'écriture de code que quelqu'un d'autre devra regarder, ou que je devrai regarder longtemps plus tard quand j'aurai oublié ce que je pensais quand je l'ai écrit, être trop court ou intelligent a tendance à faire plus de mal que de bien. Le code doit être lisible. Si succinct est bon, mais pas si succinct que le prochain programmeur ne peut pas le comprendre.
La demande est plus compacte OU plus pythonique - j'ai essayé ma main à plus compacte.
donné
import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
return x + y == z
C'est 2 caractères de moins que l'original
any(g(*args) for args in f((a,b,c)))
tester avec:
assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)
de plus, étant donné:
h = functools.partial(itertools.starmap, g)
C'est équivalent
any(h(f((a,b,c))))
g()
vous devez définir pour que cela fonctionne. Compte tenu de tout cela, je dirais que c'est beaucoup plus important.
Je veux présenter ce que je considère comme la réponse la plus pythonique :
def one_number_is_the_sum_of_the_others(a, b, c):
return any((a == b + c, b == a + c, c == a + b))
Le cas général, non optimisé:
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
En termes de Zen de Python, je pense que les déclarations soulignées sont plus suivies que d'autres réponses:
Le Zen de Python, par Tim Peters
Beau est mieux que laid.
L'explicite vaut mieux que l'implicite.
Le simple vaut mieux que le complexe.
Complexe vaut mieux que compliqué.
Plat vaut mieux que niché.
Clairsemé vaut mieux que dense.
La lisibilité compte.
Les cas spéciaux ne sont pas assez spéciaux pour enfreindre les règles.
Bien que l'aspect pratique l'emporte sur la pureté.
Les erreurs ne devraient jamais passer silencieusement.
À moins d'être explicitement réduit au silence.
Face à l'ambiguïté, refusez la tentation de deviner.
Il devrait y avoir une - et de préférence une seule - façon évidente de le faire.
Bien que cette manière ne soit pas évidente au début, sauf si vous êtes néerlandais.
C'est mieux que jamais.
Bien que jamais ne soit souvent mieux quedès maintenant.
Si l'implémentation est difficile à expliquer, c'est une mauvaise idée.
Si la mise en œuvre est facile à expliquer, cela peut être une bonne idée.
Les espaces de noms sont une excellente idée - faisons-en plus!
En tant que vieille habitude de ma programmation, je pense que placer une expression complexe à droite dans une clause peut la rendre plus lisible comme ceci:
a == b+c or b == a+c or c == a+b
Plus ()
:
((a == b+c) or (b == a+c) or (c == a+b))
Et aussi je pense que l'utilisation de multi-lignes peut aussi faire plus de sens comme ceci:
((a == b+c) or
(b == a+c) or
(c == a+b))
De manière générique,
m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();
si, manipuler une variable d'entrée est OK pour vous,
c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();
si vous souhaitez exploiter en utilisant des hacks de bits, vous pouvez utiliser "!", ">> 1" et "<< 1"
J'ai évité la division bien que cela permette d'éviter deux multiplications pour éviter les erreurs d'arrondi. Cependant, vérifiez les débordements
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