Manière d’écrire compacte (a + b == c ou a + c == b ou b + c == a)

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.

Si nous regardons le Zen de Python, je souligne:

Le Zen de Python, par Tim Peters

Beautiful est mieux que moche.
Explicit vaut mieux qu’implicite.
Simple vaut mieux que complexe.
Complexe vaut mieux que compliqué.
Flat est mieux que niché.
La densité est meilleure que la densité.
La lisibilité compte.
Les cas spéciaux ne sont pas assez spéciaux pour enfreindre les règles.
Bien que pratique bat la pureté.
Les erreurs ne devraient jamais passer en silence.
Sauf si explicitement réduit au silence.
Face à l’ambiguïté, refusez la tentation de deviner.
Il devrait y avoir un – et de préférence une seule – une manière évidente de le faire.
Bien que cela ne soit pas évident au premier abord, sauf si vous êtes néerlandais.
Maintenant c’est mieux que jamais.
Bien que jamais, c’est souvent mieux que maintenant.
Si la mise en œuvre 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 la plus claire, la plus simple et la plus facile à expliquer:

 a + b == c or a + c == b or b + c == a 

Encore mieux, 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 rest est la masturbation intellectuelle.

En outre, c’est probablement la solution la plus performante, car c’est la seule parmi toutes les propositions qui court-circuite. Si a + b == c , seule une addition et une comparaison sont effectuées.

Résoudre les trois égalités pour un:

 a in (b+c, bc, cb) 

Python a une fonction any 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 que or est en court-circuit, donc si le calcul des conditions individuelles est coûteux, il peut être préférable de garder votre construction d’origine.

Si vous savez que vous ne traitez que 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 très lisible IMO, même directement dans le code plutôt que dans une fonction.

Vous pourriez le faire, ce qui pourrait faire un peu de calcul répété; mais vous n’avez pas spécifié la performance comme objective:

 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 la 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 la fonction dans votre code.

Personnellement, à moins d’avoir 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 en 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 Pythonic, et c’est probablement le moyen le plus efficace de le faire (l’appel de la fonction supplémentaire); bien que vous ne deviez pas trop vous soucier des performances, à moins que cela ne cause un problème.

Si vous n’utilisez que trois variables, alors 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, alors votre méthode de raisonnement avec:

 a + b + c in (2*a, 2*b, 2*c) 

Est très intelligent, mais pensons à pourquoi. Pourquoi ça marche?
Eh bien, à travers une arithmétique simple, 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 ou c, ce qui signifie que oui, il sera égal à 2*a , 2*b ou 2*c . Cela sera vrai pour un nombre quelconque de variables.

Donc, une bonne façon d’écrire ceci rapidement serait de simplement avoir une liste de vos variables et de vérifier leur sum 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 append plus de variables dans l’équation, il suffit de modifier votre liste de valeurs par «n» nouvelles variables, et non d’écrire des équations «n».

Le code suivant peut être utilisé pour comparer itérativement chaque élément avec la sum des autres, qui est calculée à partir de la sum de la liste entière, à l’exclusion de cet élément.

  l = [a,b,c] any(sum(l)-e == e for e in l) 

N’essayez pas de le simplifier. Au lieu de cela, 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 le raccourcir, mais cela ne le rendrait pas plus lisible. Laisser cela comme tel n’est pas très lisible non plus, car il est difficile de savoir pourquoi vous vérifiez ces trois conditions en un coup d’œil. Cela rend absolument 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é des performances, sauf si vous avez trouvé un goulot d’étranglement que vous devez absolument corriger. Et toujours mesurer, car certaines implémentations intelligentes sont capables d’optimiser et d’inclure des appels de fonctions 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 à un nombre quelconque de variables:

 arr = [a,b,c,d,...] sum(arr)/2 in arr 

Cependant, en général, à moins que vous ayez plus de trois variables, la version originale est plus lisible.

 (a+bc)*(a+cb)*(b+ca) == 0 

Si la sum de deux termes est égale au troisième terme, alors l’un des facteurs sera zéro, rendant le produit entier nul.

Que diriez-vous de:

 a == b + c or abs(a) == abs(b - c) 

Notez que cela ne fonctionnera pas si les variables ne sont pas signées.

Du sharepoint vue de l’optimisation du code (au moins sur la plate-forme x86), cela semble être la solution la plus efficace.

Les compilateurs modernes vont aligner les deux appels de fonction abs () et éviter le test de signe et la twig conditionnelle ultérieure en utilisant une séquence intelligente d’instructions CDQ, XOR et SUB . Le code de haut niveau ci-dessus ne sera donc représenté qu’avec des instructions ALU à haut débit et à faible latence et seulement deux conditions.

La solution proposée par Alex Varga “a in (b + c, bc, cb)” est compacte et mathématiquement belle, mais je n’écrirais pas de code de cette façon car le prochain développeur qui arrive 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 

Lorsque vous écrivez du code que quelqu’un d’autre devra regarder, ou que je devrai regarder longtemps plus tard lorsque j’ai 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 devrait être lisible. Si succinct est bon, mais pas si succinct que le programmeur suivant ne peut pas le comprendre.

La demande est pour plus compact ou plus pythonique – j’ai essayé ma main à plus compact.

donné

 import functools, itertools f = functools.partial(itertools.permutations, r = 3) def g(x,y,z): return x + y == z 

Ceci est 2 caractères de moins que l’original

 any(g(*args) for args in f((a,b,c))) 

test avec:

 assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a) 

en plus, donné:

 h = functools.partial(itertools.starmap, g) 

Ceci est équivalent

 any(h(f((a,b,c)))) 

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 ce qui concerne le Zen de Python, je pense que les affirmations soulignées sont plus suivies que d’autres réponses:

Le Zen de Python, par Tim Peters

Beautiful est mieux que moche.
Explicit vaut mieux qu’implicite.
Simple vaut mieux que complexe.
Complexe vaut mieux que compliqué.
Flat est mieux que niché.
La densité est meilleure que la densité.
La lisibilité compte.
Les cas spéciaux ne sont pas assez spéciaux pour enfreindre les règles.
Bien que pratique bat la pureté.
Les erreurs ne devraient jamais passer en silence.
Sauf si explicitement réduit au silence.
Face à l’ambiguïté, refusez la tentation de deviner.
Il devrait y avoir un – et de préférence une seule – méthode évidente pour le faire.
Bien que cela ne soit pas évident au premier abord, sauf si vous êtes néerlandais.
Maintenant c’est mieux que jamais.
Bien que jamais, c’est souvent mieux que maintenant.
Si la mise en œuvre 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 plusieurs 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+bc; if (m == 0 || m == 2*a || m == 2*b) do_stuff (); 

si, manipuler une variable d’entrée est OK pour vous,

 c = a+bc; if (c==0 || c == 2*a || c == 2*b) do_stuff (); 

Si vous souhaitez exploiter des hacks, vous pouvez utiliser “!”, “>> 1” et “< < 1"

J’ai évité la division, mais cela permet 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