¿Hay alguna forma más compacta o pitónica de escribir la expresión booleana?
a + b == c or a + c == b or b + c == a
se me ocurrio
a + b + c in (2*a, 2*b, 2*c)
Pero eso es un poco extraño.
¿Hay alguna forma más compacta o pitónica de escribir la expresión booleana?
a + b == c or a + c == b or b + c == a
se me ocurrio
a + b + c in (2*a, 2*b, 2*c)
Pero eso es un poco extraño.
Respuestas:
Si miramos el Zen de Python, el énfasis es mío:
El zen de Python, por Tim Peters
Hermoso es mejor que feo.
Explícito es mejor que implícito.
Simple es mejor que complejo.
Complejo es mejor que complicado.
Plano es mejor que anidado.
Escaso es mejor que denso.
La legibilidad cuenta.
Los casos especiales no son lo suficientemente especiales como para romper las reglas.
Aunque la practicidad supera la pureza.
Los errores nunca deben pasar en silencio.
A menos que sea silenciado explícitamente.
Ante la ambigüedad, rechaza la tentación de adivinar.
Debe haber una, y preferiblemente solo una, forma obvia de hacerlo.
Aunque esa manera puede no ser obvia al principio a menos que seas holandés.
Ahora es mejor que nunca.
Aunque nunca es mejor quederecho ahora.
Si la implementación es difícil de explicar, es una mala idea.
Si la implementación es fácil de explicar, puede ser una buena idea.
Los espacios de nombres son una gran idea, ¡hagamos más de eso!
La solución más pitónica es la más clara, simple y fácil de explicar:
a + b == c or a + c == b or b + c == a
¡Aún mejor, ni siquiera necesita saber Python para entender este código! Es que fácil. Esta es, sin reservas, la mejor solución. Cualquier otra cosa es masturbación intelectual.
Además, esta es probablemente la mejor solución, ya que es la única de todas las propuestas que provocan cortocircuitos. Si a + b == c
, solo se realiza una sola adición y comparación.
Resolviendo las tres igualdades para a:
a in (b+c, b-c, c-b)
Python tiene una any
función que hace un or
sobre todos los elementos de una secuencia. Aquí he convertido tu declaración en una tupla de 3 elementos.
any((a + b == c, a + c == b, b + c == a))
Tenga en cuenta que or
es un cortocircuito, por lo que si calcular las condiciones individuales es costoso, podría ser mejor mantener su construcción original.
any()
y all()
cortocircuito también.
any
incluso antes de que se ejecute.
any
y all
"cortocircuito" el proceso de examinar los iterables que reciben; pero si ese iterable es una secuencia en lugar de un generador, entonces ya se ha evaluado completamente antes de que ocurra la llamada a la función .
any
, sangría simple ):
en la if
declaración), lo que ayuda mucho a la legibilidad cuando las matemáticas están involucradas
Si sabe que solo está tratando con números positivos, esto funcionará y está bastante limpio:
a, b, c = sorted((a, b, c))
if a + b == c:
do_stuff()
Como dije, esto solo funciona para números positivos; pero si sabe que van a ser positivos, esta es una solución IMO muy legible, incluso directamente en el código en lugar de en una función.
Podrías hacer esto, lo que podría hacer un poco de cálculo repetido; pero no especificó el rendimiento como su objetivo:
from itertools import permutations
if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
do_stuff()
O sin permutations()
y la posibilidad de cálculos repetidos:
if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
do_stuff()
Probablemente pondría esto, o cualquier otra solución, en una función. Entonces puede llamar a la función limpiamente en su código.
Personalmente, a menos que necesite más flexibilidad del código, solo usaría el primer método en su pregunta. Es simple y eficiente. Todavía podría ponerlo en una función:
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()
Eso es bastante Pythonic, y posiblemente sea la forma más eficiente de hacerlo (dejando de lado la función adicional); aunque no debería preocuparse demasiado por el rendimiento de todos modos, a menos que realmente esté causando un problema.
Si solo usará tres variables, entonces su método inicial:
a + b == c or a + c == b or b + c == a
Ya es muy pitónico.
Si planea usar más variables, entonces su método de razonamiento con:
a + b + c in (2*a, 2*b, 2*c)
Es muy inteligente pero pensemos por qué. ¿Por qué funciona esto?
Bueno, a través de una aritmética simple, vemos que:
a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c
Y esto tendrá que ser verdad para A, B, o C, lo que significa que sí, va a ser igual a 2*a
, 2*b
o 2*c
. Esto será cierto para cualquier número de variables.
Entonces, una buena manera de escribir esto rápidamente sería simplemente tener una lista de sus variables y verificar su suma con una lista de los valores duplicados.
values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])
De esta manera, para agregar más variables a la ecuación, todo lo que tiene que hacer es editar su lista de valores con 'n' nuevas variables, no escribir ecuaciones 'n'
a=-1
, b=-1
, c=-2
y, a continuación a+b=c
, pero a+b+c = -4
y 2*max(a,b,c)
es-2
abs()
llamadas, es Pythonic que el fragmento del OP (en realidad lo llamaría significativamente menos legible).
any(sum(values) == 2*x for x in values)
, de esa manera no tendrías que duplicar por adelantado, tan necesario.
El siguiente código se puede usar para comparar iterativamente cada elemento con la suma de los otros, que se calcula a partir de la suma de la lista completa, excluyendo ese elemento.
l = [a,b,c]
any(sum(l)-e == e for e in l)
[]
corchetes de la segunda línea, esto incluso or
any(a + b + c == 2*x for x in [a, b, c])
, bastante cerca de la sugerencia del OP
No intentes simplificarlo. En cambio, nombra lo que estás haciendo con una función:
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):
...
Reemplazar la condición con algo "inteligente" podría acortarlo, pero no lo hará más legible. Sin embargo, dejarlo así no es muy fácil de leer, porque es difícil saber por qué estás revisando esas tres condiciones de un vistazo. Esto hace que sea absolutamente claro lo que estás buscando.
Con respecto al rendimiento, este enfoque agrega la sobrecarga de una llamada a la función, pero nunca sacrifica la legibilidad por el rendimiento a menos que haya encontrado un cuello de botella que absolutamente debe corregir. Y siempre mida, ya que algunas implementaciones inteligentes son capaces de optimizar y alinear algunas llamadas de función en algunas circunstancias.
Python 3:
(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...
Se escala a cualquier número de variables:
arr = [a,b,c,d,...]
sum(arr)/2 in arr
Sin embargo, en general, estoy de acuerdo en que, a menos que tenga más de tres variables, la versión original es más legible.
[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 suma de dos términos cualquiera es igual al tercer término, entonces uno de los factores será cero, haciendo que todo el producto sea cero.
(a+b<>c) && (a+c<>b) && (b+c<>a) == false
¿Qué tal solo:
a == b + c or abs(a) == abs(b - c)
Tenga en cuenta que esto no funcionará si las variables no están firmadas.
Desde el punto de vista de la optimización del código (al menos en la plataforma x86), esta parece ser la solución más eficiente.
Los compiladores modernos alinearán ambas llamadas a funciones abs () y evitarán la prueba de signos y la ramificación condicional posterior mediante el uso de una secuencia inteligente de instrucciones CDQ, XOR y SUB . Por lo tanto, el código de alto nivel anterior se representará con solo instrucciones ALU de baja latencia y alto rendimiento y solo dos condicionales.
fabs()
se puede usar para float
tipos;).
La solución proporcionada por Alex Varga "a in (b + c, bc, cb)" es compacta y matemáticamente hermosa, pero en realidad no escribiría el código de esa manera porque el próximo desarrollador que viene no entendería de inmediato el propósito del código .
La solución de Mark Ransom de
any((a + b == c, a + c == b, b + c == a))
es más claro pero no mucho más sucinto que
a + b == c or a + c == b or b + c == a
Cuando escribo un código que alguien más tendrá que mirar, o que tendré que mirar mucho después, cuando haya olvidado lo que estaba pensando cuando lo escribí, ser demasiado corto o inteligente tiende a hacer más daño que bien. El código debe ser legible. Tan sucinto es bueno, pero no tan sucinto que el próximo programador no pueda entenderlo.
La solicitud es más compacta O más pitónica: probé con una mano más compacta.
dado
import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
return x + y == z
Esto es 2 caracteres menos que el original
any(g(*args) for args in f((a,b,c)))
prueba con:
assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)
adicionalmente, dado:
h = functools.partial(itertools.starmap, g)
Esto es equivalente
any(h(f((a,b,c))))
g()
que debe definir para que esto funcione. Dado todo eso, diría que es significativamente más grande.
Quiero presentar lo que veo como la respuesta más pitónica :
def one_number_is_the_sum_of_the_others(a, b, c):
return any((a == b + c, b == a + c, c == a + b))
El caso general, no optimizado:
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 términos del Zen de Python, creo que las declaraciones enfatizadas son más seguidas que de otra respuesta:
El zen de Python, por Tim Peters
Hermoso es mejor que feo.
Explícito es mejor que implícito.
Simple es mejor que complejo.
Complejo es mejor que complicado.
Plano es mejor que anidado.
Escaso es mejor que denso.
La legibilidad cuenta.
Los casos especiales no son lo suficientemente especiales como para romper las reglas.
Aunque la practicidad supera la pureza.
Los errores nunca deben pasar en silencio.
A menos que sea silenciado explícitamente.
Ante la ambigüedad, rechaza la tentación de adivinar.
Debe haber una, y preferiblemente solo una, forma obvia de hacerlo.
Aunque esa manera puede no ser obvia al principio a menos que seas holandés.
Ahora es mejor que nunca.
Aunque nunca es mejor quederecho ahora.
Si la implementación es difícil de explicar, es una mala idea.
Si la implementación es fácil de explicar, puede ser una buena idea.
Los espacios de nombres son una gran idea, ¡hagamos más de eso!
Como un viejo hábito de mi programación, creo que colocar una expresión compleja a la derecha en una cláusula puede hacerlo más legible de esta manera:
a == b+c or b == a+c or c == a+b
Más ()
:
((a == b+c) or (b == a+c) or (c == a+b))
Y también creo que usar líneas múltiples también puede tener más sentidos como este:
((a == b+c) or
(b == a+c) or
(c == a+b))
De manera genérica,
m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();
si, manipular una variable de entrada está bien para ti,
c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();
si quieres explotar usando hacks de bits, puedes usar "!", ">> 1" y "<< 1"
Evité la división, aunque permite el uso para evitar dos multiplicaciones para evitar errores de redondeo. Sin embargo, verifique si hay desbordamientos
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