web-dev-qa-db-fra.com

Écriture compacte (a + b == c ou a + c == b ou b + c == a)

Existe-t-il une méthode plus compacte ou pythonique pour é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.

136
qwr

Si nous regardons le zen de Python, soulignons le mien:

Le zen de python, de Tim Peters

Beau vaut mieux que moche.
Explicite vaut mieux qu'implicite.
Simple vaut mieux que complexe.
Complexe vaut mieux que compliqué.
Flat vaut mieux que niché.
La clarté vaut mieux que la densité.
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 doivent jamais passer silencieusement.
À moins d’être 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 - manière évidente de le faire.
Bien que cela puisse ne pas être évident au début, à moins que vous ne soyez néerlandais.
Maintenant, c'est mieux que jamais.
Bien que ne soit jamais mieux que à droite 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 bonne 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 savoir Python pour comprendre ce code! C'est que facile. C'est , sans réserve, la meilleure solution.Tout le reste est de la masturbation intellectuelle.

De plus, c'est probablement la solution la plus performante, car c'est la seule parmi toutes les propositions qui court-circuitent. Si a + b == c, une seule addition et comparaison est effectuée.

204
Barry

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

a in (b+c, b-c, c-b)
101
Alex Varga

Python a une fonction any qui effectue 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 un court-circuit. Par conséquent, si le calcul des conditions individuelles est coûteux, il peut être préférable de conserver votre construction d'origine.

54
Mark Ransom

Si vous savez que vous avez uniquement affaire à 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 ils vont être positifs, c'est une solution très lisible à l'OMI, même directement dans le code par opposition à une fonction.

Vous pouvez faire ceci, ce qui pourrait faire un peu de calcul répété; mais vous n'avez pas spécifié la performance 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 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 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 joli Pythonic, et c'est probablement le moyen le plus efficace de le faire (l'appel de fonction supplémentaire mis à part); De toute façon, ne vous inquiétez pas trop pour la performance, à moins que cela ne cause réellement un problème.

40
Cyphase

Si vous n'utilisez que trois variables, votre méthode initiale est la suivante:

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

Est déjà très pythonique.

Si vous envisagez d’utiliser plus de variables, votre méthode de raisonnement est la suivante:

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

Est très intelligent mais laisse penser à pourquoi. Pourquoi ça marche?
Bien, à travers quelques calculs simples, 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 n'importe quel nombre de variables.

Donc, un bon moyen d'écrire cela rapidement serait simplement d'avoir une liste de vos variables et de comparer leur somme à une liste de 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, il vous suffit d'éditer votre liste de valeurs en "n" nouvelles variables, sans écrire les équations "n".

17
ThatGuyRussell

Le code suivant peut être utilisé pour comparer de manière itérative chaque élément à la somme des autres, calculée à partir de la somme de la liste complète, à l'exclusion de cet élément.

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

N'essayez pas de le simplifier. Au lieu de cela, name 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 d '"intelligent" pourrait la raccourcir, mais cela ne la rendrait pas plus lisible. Cependant, il n’est pas très lisible de le laisser ainsi, 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 recherchez.

En ce qui concerne les performances, cette approche ajoute la surcharge d’un appel de fonction, mais ne sacrifiez jamais la lisibilité pour la performance, 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’aligner certains appels de fonction dans certaines circonstances.

10
Jack

Python 3:

(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...

Il évolue à un nombre quelconque de variables:

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

Cependant, en général, je conviens que, sauf si vous avez plus de trois variables, la version originale est plus lisible.

9
Vitalii Fedorenko

Que diriez-vous juste:

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 (du 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 les branches conditionnelles ultérieures en utilisant une séquence séquence astucieuse d'instructions CDQ, XOR et SUB . Le code de haut niveau ci-dessus sera donc représenté avec uniquement des instructions ALU à haut débit et à faible temps de latence et seulement deux conditions.

6
Igor Levicki
(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 zéro, ce qui rend le produit entier zéro.

6
mbeckish

La solution fournie par Alex Varga "a in (b + c, bc, cb)" est compacte et belle du point de vue mathématique, mais je n'écrirais pas réellement le code de cette façon, car le prochain développeur 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

En écrivant du 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'écrivis, être trop court ou trop malin a tendance à faire plus de mal que de bien. Le code devrait être lisible. Si succinct est bon, mais pas si succinct que le prochain programmeur ne peut pas le comprendre.

4
Paul J Abernathy

La demande concerne plus compact OR plus Pythonic - j’ai essayé plus compact.

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)))

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, étant donné:

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

C'est équivalent

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

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))
1
shA.t

Je veux présenter ce que je considère comme la réponse la plus Pythonic :

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 déclarations soulignées sont plus suivies que d'une autre réponse:

Le zen de python, de Tim Peters

Beau vaut mieux que moche.
Explicite vaut mieux qu'implicite.
Simple vaut mieux que complexe.
Complexe vaut mieux que compliqué.
Flat vaut mieux que niché.
La clarté vaut mieux que la densité.
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 doivent jamais passer silencieusement.
À moins d’être explicitement réduit au silence.
Face à l’ambiguïté, refusez la tentation de deviner.
Il devrait y avoir un - et de préférence un seul - moyen évident de le faire.
Bien que cela puisse ne pas être évident au début, à moins que vous ne soyez néerlandais.
Maintenant, c'est mieux que jamais.
Bien que ne soit jamais mieux que à droite 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 bonne idée: faisons-en plus!

1
sevenforce
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
0
Hammerite

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 vous convient,

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

si vous voulez exploiter des bits, vous pouvez utiliser "!", ">> 1" et "<< 1"

J'ai évité la division même si cela permet d'utiliser deux multiplications pour éviter des erreurs d'arrondis. Cependant, vérifiez les débordements

0
Padmabushan