¿Por qué más idiomas no tienen la capacidad de comparar un valor con más de otro valor? [cerrado]

10

Considera lo siguiente:

if(a == b or c)

En la mayoría de los idiomas, esto debería escribirse como:

if(a == b or a == c)

que es un poco engorroso y repite información.

Sé que mi sintaxis de muestra anterior es un poco torpe, pero estoy seguro de que hay mejores maneras de transmitir la idea.

¿Por qué no lo ofrecen más idiomas? ¿Hay problemas de rendimiento o sintaxis?

Zeroth
fuente
66
SQL ofrece eso: donde A IN (B, C)
jueves
44
No estaba preguntando por los idiomas que lo ofrecen, o puedo tenerlo, pero ¿por qué no lo ofrecen más idiomas? ¿Hay problemas de rendimiento o sintaxis?
Zeroth
8
para generalizar la respuesta de @ thursdaysgeek, en la mayoría de los idiomas, generalmente lo haces con la contención establecida. (O una lista o tupla si eso es más fácil). Funciona igual y evita algunos problemas de sintaxis potencialmente complicados. A partir de su ejemplo, "b o c" significa el conjunto "{b, c}" o es o un operador como || ? En python "b o c" significa "el valor de b si es verdadero, o de lo contrario el valor de c"
Rob
44
Esencialmente, este es un problema de sintaxis. El problema en cuestión es tener una forma intuitiva de desambiguar la diferencia entre "b o c" y "b or'd with c".
YoungJohn
2
Es bastante extraño para un caso especial a == b or c, y ni siquiera se ve bien en mi humilde opinión.

Respuestas:

24

El problema de la sintaxis es que requiere sintaxis.

Cualquiera sea la sintaxis que tenga su idioma, las personas que lo usan tienen que aprenderlo. De lo contrario, corren el riesgo de ver el código y no saber lo que hace. Por lo tanto, generalmente se considera algo bueno si un lenguaje tiene una sintaxis simple que maneja limpiamente muchos casos.

En su ejemplo específico, está tratando de tomar un operador infijo (una función que toma dos argumentos pero está escrita Argument1 Operator Argument2) y está tratando de extenderlo a múltiples argumentos. Eso no funciona de manera muy limpia porque el objetivo de los operadores de infijo, en la medida en que haya uno, es colocar el operador justo entre los 2 argumentos. Extender a (Argument1 Operator Argument2 MagicallyClearSymbol Argument3...)no parece agregar mucha claridad Equals(Arg1,Arg2,...). Infix también se usa típicamente para emular convenciones matemáticas con las que las personas están familiarizadas, lo que no sería cierto para una sintaxis alternativa.

No habría problemas de rendimiento particulares asociados con su idea, aparte de que el analizador tendría que lidiar con una gramática con otra o dos reglas de producción, lo que podría tener un ligero efecto en la velocidad del análisis. Esto podría hacer alguna diferencia para un lenguaje interpretado o compilado JIT, pero probablemente no sea una gran diferencia.

El mayor problema con la idea es que hacer muchos casos especiales en un idioma tiende a ser una mala idea .

psr
fuente
1
Aparte: Scala tiene operadores infix con un número arbitrario de argumentos, ya que los operadores infix son solo llamadas a métodos sin a .. Entonces se escribirían como arg1 op (arg2, arg3). No es exactamente hermoso, pero es necesario en algunos lugares en el contexto de ese idioma.
amon
¿qué pasa if my_var in (a, b)entonces? ¿No se trata más de utilizar la herramienta adecuada para el trabajo?
Grandes puntos La sintaxis del idioma debe ser esencial para el idioma, y ​​luego construir bibliotecas sobre él. Si el lenguaje está demasiado abarrotado de azúcar sintáctico "útil", se vuelve más difícil de usar. No todos necesitan a == b or cmientras que otros quieren a == b or c but not d. OMI es donde las funciones de utilidad / bibliotecas vienen al rescate.
Allan
Quizás lo que se necesita es un medio a través del cual un método podría especificar que una llamada con un número arbitrario de argumentos debe manejarse como llamadas múltiples, con los resultados combinados de alguna manera. Si f().Equals(a,b,c); podría evaluarse ya (var temp=f(); temp.Equals(a)||temp.Equals(b)||temp.Equals(c))que esa sintaxis sería perfecta, pero si se evalúa así int[] arr = {a,b,c}; f().Equals(arr);no sería tan buena, especialmente si se tuviera que crear una nueva matriz para cada llamada.
supercat
6

Debido a que no es un problema, y ​​resolverlo conlleva básicamente un beneficio cero, pero implementarlo conlleva un costo distinto de cero.

Las funciones basadas en rangos existentes y tal que prácticamente todos los idiomas ofrecen pueden funcionar perfectamente bien en esta situación si se escala a un tamaño en el a == b || a == cque no se corta.

DeadMG
fuente
2
+1, pero creo que la respuesta mejoraría al mostrar una o dos de esas "funciones basadas en rangos existentes que prácticamente todos los idiomas [ofrecen]", solo para que esta alternativa sea más clara.
Avner Shahar-Kashtan
¿Puede demostrar que "trae beneficios básicamente cero, pero su implementación conlleva un costo distinto de cero"?
Darek Nędza
3
@ DarekNędza La segunda mitad no debe ser polémica: cada característica debe ser pensada, implementada, probada, documentada y respaldada. Ninguno de esos pasos es gratuito bajo una métrica razonable (tiempo de las personas, costo de oportunidad, complejidad, costo monetario si a alguien se le paga por trabajar en él, etc.).
@ AvnerShahar-Kashtan De acuerdo: para mí, no es obvio cómo se vería en, por ejemplo, java, o sh, o zsh. Ok, puede haber implicado lenguaje 'moderno'. Groovy?
Volker Siegel
En PHP, se vería así in_array($a, [$b, $c, $d, $e, $f]). : P
cHao
6

Algunos idiomas tienen tales características. Por ejemplo, en Perl6 podemos utilizar uniones , que son “superposiciones” de dos valores:

if $a == any($b, $c) {
    say "yes";
}

# syntactic sugar for the above
if $a == $b | $c {
    say "yes";
}

Las uniones nos permiten expresar operaciones en un conjunto de datos de manera bastante sucinta, de forma similar a la forma en que las operaciones escalares se distribuyen sobre colecciones en algunos idiomas. Por ejemplo, usando Python con numpy, la comparación se puede distribuir entre todos los valores:

import numpy as np
2 == np.array([1, 2, 3])
#=> np.array([False, True, False], dtype=np.bool)
(2 == np.array([1, 2, 3])).any()
#=> True

Sin embargo, esto solo funciona para tipos primitivos seleccionados.

¿Por qué las uniones son problemáticas? Debido a que las operaciones en una unión se distribuyen sobre los valores contenidos, el objeto de unión en sí se comporta como un proxy para las llamadas a métodos, algo que pocos sistemas de tipos, aparte de la escritura de pato, pueden manejar.

Los problemas del sistema de tipos se pueden evitar si tales uniones solo se permiten como sintaxis especial alrededor de los operadores de comparación. Pero en este caso, están tan limitados que no agregan suficiente valor para agregarse a cualquier lenguaje sensato. El mismo comportamiento podría expresarse utilizando operaciones de conjunto o deletreando todas las comparaciones manualmente, y la mayoría de los idiomas no creen en agregar sintaxis redundante si ya existe una solución perfectamente buena.

amon
fuente
Ese ejemplo numpy particular podría reescribirse más claramente como 2 in [1, 2, 3]. Por otro lado, si numpy tiene un .all()o algo, la pitón simple equivalente no es tan concisa.
Izkata
@Izkata No utilicé específicamente las operaciones de configuración. Si bien mi ejemplo usó el ==operador, también podemos usarlo <, ¿dónde está inahora? Las uniones son más generales que las pruebas de membresía establecidas, porque las operaciones en la unión se distribuyen entre todos los miembros, (x|y).fooes decir x.foo|y.foo, hasta que la unión finalmente se colapsa en un solo valor. El código NumPy proporcionado muestra una traducción exactamente equivalente pero más detallada de las uniones Perl6, suponiendo tipos primitivos.
amon
2

En idiomas con macros, es fácil agregar algo así si aún no está allí. Considera la raqueta

(define-syntax-rule (equal-any? a b ...)
  (or (equal? a b) ...))
(equal-any? "a" "b" "a")
> #t

En otros idiomas sin metaprogramación, tal vez pueda reformular eso como comprobación de membresía set / list tal vez:

if a ∈ {b, c}
Phil
fuente
2
Los dos primeros verifican si todos los argumentos son iguales; OP quiere verificar si el primer argumento es igual a cualquiera de los siguientes. Curiosamente, el tercer fragmento que muestra respeta eso.
@delnan Lo siento, entendí mal las cosas. Lo he editado
Phil
2

En algunos idiomas (populares) el ==operador no es transitivo. Por ejemplo, en JavaScript 0es igual a ambos ''y '0', pero luego ''y '0'no son iguales entre sí. Más de tales peculiaridades en PHP.

Significa que a == b == cagregaría otra ambigüedad, porque podría producir un resultado diferente dependiendo de si se interpreta como (a == b) & (a == c)o (a == b) & (a == c) & (b == c).

Konrad Morawski
fuente
2

En la mayoría de los idiomas, esto debería lograrse de manera trivial escribiendo una Infunción, entonces, ¿por qué hacerla parte del lenguaje real?

Linq, por ejemplo, tiene Contains().

Muy bien, para todos ustedes pedantes, aquí está mi implementación en C #:

public static bool In<T>(this T obj, params T[] values)
{
    for(int i=0; i < values.Length; i++)
    {
        if (object.Equals(obj, values[i]))
            return true;
    }
    return false;
}
Robert Harvey
fuente
Eso opera en un rango de valores de tiempo de ejecución, no en una tupla, ya que el código del OP podría expresarse como.
DeadMG
Parece que, solo porque es fácil, no significa que no deba hacerse. Es ... considere la construcción. ¿Por qué siempre tenemos que escribir manualmente todas estas piezas básicas de funcionalidad y algoritmos, una y otra y otra vez?
Zeroth
55
@Zeroth tal vez está escribiendo lo mismo una y otra vez, pero otros tienden a utilizar los mecanismos de abstracción que ofrece su lenguaje. Si te ves escribiendo a == b || a == cvarias veces, tal vez sea hora deequals_any(a, {b, c})
amon
Una implementación "contiene" no se extiende fácilmente para cubrir cosas como if (a > (b or c))y if (a mod (b or c) == 2).
tobyink
1
¿Alguien dijo pedantes? :) Es un bucle foreach, por lo que no hay ivariable. Y, en general, parece estar escrito después de haber tenido un largo día :) Porque poner ambos return truey return falsedentro del bucle aquí significa que no hay forma de que vaya más allá de la primera iteración. Solo estás comparando con el primero value. Por cierto, ¿por qué no usarlo Anycomo sugirió @Bob y simplificarloreturn values.Any(value => Object.Equals(obj, value));
Konrad Morawski
1

"if (a == b o c)" funciona en la mayoría de los idiomas: si a == b o si c no es negativo, nulo o cero.

Quejarse de que es detallado pierde el punto: no deberías apilar una docena de cosas en un condicional. Si necesita comparar un valor con un número arbitrario de otros valores, cree una subrutina.

Satanicpuppy
fuente
3
¿Qué idiomas componen "más"?
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner, bueno, si se cevalúa como booleano, entonces casi cualquier idioma puede manejar a == b || c:)
Brian S
@BrianS: asumí que el OP significaba la sintaxis literal if(a == b or c). Necesito tomar un descanso, creo ...: P
FrustratedWithFormsDesigner
@FrustratedWithFormsDesigner Lisp! ... ¿eh? ... :)
Volker Siegel
3
Esto realmente pierde el punto de la pregunta. if (a == b or c)es un pseudocódigo para verificar si aes igual bo aes igual a c. No está destinado a verificar que cno sea cero.
hvd
1

Por lo general, desea mantener su sintaxis al mínimo y, en su lugar, permitir que dichas construcciones se definan en el lenguaje mismo.

Por ejemplo, en Haskell, puede convertir cualquier función con dos o más argumentos en un operador infijo mediante backticks. Esto te permite escribir:

if a `elem` [b, c] then ... else ...

donde elemes solo una función normal que toma dos argumentos, un valor y una lista de valores, y comprueba si el primero es un elemento del segundo.

¿Qué pasa si quieres usar en andlugar de or? En Haskell, puede usar lo siguiente en lugar de esperar a que el proveedor del compilador implemente una nueva característica:

 if all (== a) [b, c] then ... else ...
Tobias Brandt
fuente
1
¿Por qué querría mantener la sintaxis al mínimo? ¿Qué es exactamente lo que está pasando allí? No haga proclamas así sin respaldar argumentos. ;)
Zeroth
1

Algunos idiomas ofrecen esto, hasta cierto punto.

Tal vez no como su ejemplo específico , pero tome por ejemplo una línea de Python:

def minmax(min, max):
    def answer(value):
        return max > value > min
    return answer

inbounds = minmax(5, 15)
inbounds(7) ##returns True
inbounds(3) ##returns False
inbounds(18) ##returns False

Por lo tanto, algunos idiomas están bien con comparaciones múltiples, siempre que lo exprese correctamente.

Desafortunadamente, no funciona como esperarías para las comparaciones.

>>> def foo(a, b):
...     def answer(value):
...         return value == a or b
...     return answer
... 
>>> tester = foo(2, 4)
>>> tester(3)
4
>>> tester(2)
True
>>> tester(4)
4
>>> 

"¿Qué quieres decir con que devuelve True o 4?" - el alquiler después de ti

Una solución en este caso, al menos con Python, es usarlo de manera ligeramente diferente:

>>> def bar(a, b):
...     def ans(val):
...             return val == a or val == b
...     return ans
... 
>>> this = bar(4, 10)
>>> this(5)
False
>>> this(4)
True
>>> this(10)
True
>>> this(9)
False
>>> 

EDITAR: Lo siguiente también haría algo similar, nuevamente en Python ...

>>> def bar(a, b):
...     def answer(val):
...             return val in (a, b)
...     return answer
... 
>>> this = bar(3, 5)
>>> this(3)
True
>>> this(4)
False
>>> this(5)
True
>>> 

Por lo tanto, sea cual sea el idioma que esté utilizando, puede que no sea que no pueda hacerlo, solo que primero debe echar un vistazo más de cerca a cómo funciona realmente la lógica. Por lo general, es solo una cuestión de saber qué es lo que realmente está 'pidiendo' que le diga el idioma.


fuente
1

El método indexOf, utilizado en una matriz, que tienen todos los lenguajes, permite comparar un valor con varios otros, por lo que supongo que un operador especial no tiene mucho sentido.

En javascript eso escribiría:

if ( [b, c].indexOf(a) != -1 ) { ....  }
GameAlchemist
fuente
0

Usted pregunta por qué no podemos hacer esto: if(a == b or c)

Python hace esto de manera muy eficiente, de hecho, más eficientemente con set:

if a in set([b, c]):
    then_do_this()

Para la prueba de membresía, 'set' verifica que los valores hash del elemento sean los mismos y solo entonces se compara para la igualdad, por lo que los elementos, byc, deben ser utilizables, de lo contrario, una lista compara directamente la igualdad:

if a in [b, c]:
    then_do_this()
Aaron Hall
fuente
0

Los lenguajes de estilo APL le permiten comparar un escalar con cada elemento en un vector en una sola operación. Esto produce un vector booleano. Por ejemplo, me gustaría promocionar descaradamente mi calculadora apl mínimamente presentada, inca ( intérprete en línea ).

   a<5
5 
   b<4
4 
   c<5
5 
   a=b c
0 1 

Para reducir esto a un solo valor, podemos hacer una inclusión o sumando y verificando que no sea cero.

   0!+/a=b c
1 
   c<6
6 
   0!+/a=b c
0

Entonces, como dicen las otras respuestas, el problema es la sintaxis. Hasta cierto punto, se han encontrado soluciones de sintaxis , a un costo quizás elevado de aprender el paradigma de la matriz.

luser droog
fuente