¿Por qué los idiomas requieren paréntesis alrededor de las expresiones cuando se usan con "if" y "while"?

67

Los lenguajes como C, Java y C ++ requieren paréntesis alrededor de una expresión completa cuando se usan en un if , while o switch .

if (true) {
    // Do something
}

a diferencia de

if true {
    // Do something
}

Esto me parece extraño porque los paréntesis son redundantes. En este ejemplo, true es una sola expresión por sí sola. El paréntesis no transforma su significado de ninguna manera que yo sepa. ¿Por qué existe esta extraña sintaxis y por qué es tan común? ¿Hay algún beneficio que no conozca?

    
pregunta Velovix 07.11.2016 - 03:26

10 respuestas

155

Debe haber alguna forma de saber dónde termina la condición y comienza la rama. Hay muchas maneras diferentes de hacerlo.

En algunos idiomas, no hay condicionales en absoluto , por ejemplo. en Smalltalk, Self, Newspeak, Io, Ioke, Seph y Fancy. La ramificación condicional se implementa simplemente como un método normal como cualquier otro método. El método se implementa en objetos booleanos y se llama en un booleano. De esa manera, la condición es simplemente el receptor del método, y las dos ramas son dos argumentos, por ejemplo. en Smalltalk:

aBooleanExpression ifTrue: [23] ifFalse: [42].

En el caso, usted está más familiarizado con Java, esto es equivalente a lo siguiente:

aBooleanExpression.ifThenElse(() -> 23, () -> 42);

En la familia de lenguajes Lisp, la situación es similar: los condicionales son solo funciones normales (en realidad, macros) y el primer argumento es la condición, el segundo y tercer argumento son las ramas, por lo que son solo argumentos de función normal, y no hay nada especial necesario para delimitarlos:

(if aBooleanExpression 23 42)

Algunos idiomas usan palabras clave como delimitadores, por ejemplo, Algol, Ada, BASIC, Pascal, Modula-2, Oberon, Oberon-2, Oberon activo, Pascal del componente, Zonnon, Modula-3:

IF aBooleanExpression THEN RETURN 23 ELSE RETURN 42;

En Ruby, puedes usar una palabra clave o un separador de expresión (punto y coma o nueva línea):

if a_boolean_expression then 23 else 42 end

if a_boolean_expression; 23 else 42 end

# non-idiomatic, the minimum amount of whitespace required syntactically
if a_boolean_expression
23 else 42 end

# idiomatic, although only the first newline is required syntactically
if a_boolean_expression
  23
else
  42
end

Go requiere que las ramas sean bloques y no permite expresiones o declaraciones, lo que hace que las llaves sean obligatorias. Por lo tanto, los paréntesis no son necesarios, aunque puede agregarlos si lo desea; Perl6 y Rust son similares en este sentido:

if aBooleanExpression { return 23 } else { return 42 }

Algunos idiomas usan otros caracteres no alfanuméricos para delimitar la condición, por ejemplo, Python:

if aBooleanExpression: return 23
else: return 42

La conclusión es: necesita alguna forma de saber dónde termina la condición y comienza la rama. Hay muchas maneras de hacerlo, los paréntesis son solo una de ellas.

    
respondido por el Jörg W Mittag 07.11.2016 - 10:38
70

Los paréntesis solo son innecesarios si utiliza llaves.

if true ++ x;

Por ejemplo, se vuelve ambiguo sin ellos.

    
respondido por el Telastyn 07.11.2016 - 03:51
21

Los paréntesis en una declaración if no tienen el mismo significado que los paréntesis usados dentro de una expresión aritmética. Los paréntesis en una expresión aritmética se utilizan para agrupar expresiones. Los paréntesis en una declaración if se usan para delimitar la expresión booleana; es decir, para diferenciar la expresión booleana del resto de la declaración if .

En una declaración if , los paréntesis no realizan una función de agrupación (aunque, dentro de la declaración if , todavía puedes usar paréntesis para agrupar expresiones aritméticas. El conjunto exterior de paréntesis sirve para delimitar la expresión booleana completa ). Hacerlos necesarios simplifica el compilador, ya que el compilador puede confiar en que esos paréntesis siempre estén allí.

    
respondido por el Robert Harvey 07.11.2016 - 03:38
16

Como otros ya lo han señalado parcialmente, esto se debe al hecho de que las expresiones también son declaraciones válidas, y en el caso de un bloque con solo una instrucción, puede dejar llaves. Esto significa que lo siguiente es ambiguo:

if true
    +x;

Porque podría interpretarse como:

if (true + x) {}

en lugar de:

if (true) {+x;}

Varios idiomas (por ejemplo, Python) te permiten evitar el paréntesis pero aún tienen un marcador de condición final:

if True: +x

Sin embargo, tiene razón en que podríamos definir un idioma en el que nunca se requiera el paréntesis: un idioma en el que una expresión es no una declaración válida no tendrá este problema.

Lamentablemente, esto significa que cosas como:

 ++x;
 functionCall(1,2,3);

no serían declaraciones válidas, por lo que tendrías que introducir una sintaxis extraña para poder realizar tales acciones sin crear expresiones. Una forma sencilla de hacer esto es simplemente anteponer la expresión con un marcador como [statement] :

[statement] ++x;
[statement] functionCall(1,2,3);

Ahora la ambigüedad desaparece, ya que tendrías que escribir:

if true
    [statement] ++x;

Pero como puede ver, no veo que un lenguaje de este tipo esté muy extendido, ya que poner el paréntesis alrededor de una condición if (o un : al final) es mucho mejor que poner un marcador para cada expresión declaración.

Nota : el uso de un marcador [statement] es la sintaxis más simple que se me ocurre. Sin embargo, podría tener dos sintaxis completamente distintas para expresiones y declaraciones sin ambigüedad entre ellas, lo que no requeriría tal marcador. El problema es que el lenguaje sería extremadamente extraño ya que hacer las mismas cosas en una expresión o en una declaración tendría que usar una sintaxis completamente diferente.

Una cosa que viene a la mente es tener dos sintaxis separadas sin un marcador tan explícito, por ejemplo: forzar sentencias para usar símbolos Unicode (por lo tanto, en lugar de for , usarías una variación Unicode de las letras f , o y r ), mientras que las expresiones son solo ASCII.

    
respondido por el Bakuriu 07.11.2016 - 10:49
10

Es común que los lenguajes de la familia C requieran estos paréntesis, pero no universales.

Uno de los cambios sintácticos más notables de Perl 6 es que modificaron la gramática para que no tenga que dar a los paréntesis alrededor de if , for y condiciones similares a las de las declaraciones. Así que algo como esto es perfectamente válido en Perl 6:

if $x == 4 {
    ...
}

tal como está

while $queue.pop {
    ...
}

Sin embargo, como son solo expresiones, puede poner paréntesis a su alrededor si así lo desea, en cuyo caso son solo agrupaciones comunes en lugar de una parte requerida de la sintaxis como están en C, C #, Java, etc.

Rust tiene una sintaxis similar a Perl 6 en este departamento:

if x == 4 {
    ...
}

Me parece que una característica de los lenguajes más modernos inspirados en C es mirar cosas como esta y preguntarse acerca de eliminarlas.

    
respondido por el Matthew Walton 07.11.2016 - 09:49
6

Hay un aspecto en el que me sorprende que ninguna de las respuestas existentes haya surgido.

C, y muchos derivados y parecidos de C, tienen la particularidad de que el valor de una asignación es el valor asignado. Una consecuencia de esto es que se puede usar una asignación donde se espera un valor.

Esto te permite escribir cosas como

if (x = getValue() == 42) { ... }

o

if (x == y = 47) { ... }

o

unsigned int n = 0 /* given m == SOME_VALUE */;
while (n < m && *p1++ = *p2++) { n++; }

(que se trata de forma implícita como while (n < m && *p1++ = *p2++ != 0) { n++; } porque C trata el no cero como verdadero; por cierto, creo que eso se trata de strncpy () en la biblioteca estándar de C)

o incluso

if (x = 17);

y todo es válido. No todas las combinaciones sintácticamente válidas son necesariamente útiles (y los compiladores modernos advierten específicamente sobre las asignaciones dentro de los condicionales, porque es un error común), pero en realidad es útil .

El análisis de tales declaraciones probablemente sería mucho más difícil si no hubiera una forma inequívoca de determinar dónde comienza y dónde termina la expresión condicional.

Los paréntesis ya se utilizaron para delimitar los nombres de las funciones de los argumentos de las funciones, por lo que creo que parecían una opción natural también para delimitar las palabras clave de los argumentos de las palabras clave.

Claro, se podrían definir sintaxis alternativas para hacer lo mismo. Pero hacerlo aumentaría la complejidad, especialmente en el analizador, que luego tendría que tratar con dos conjuntos diferentes de sintaxis para la misma cosa. Cuando se diseñó C, el poder de cómputo (tanto en términos de capacidad de procesamiento de números, memoria de trabajo y capacidad de almacenamiento) era extremadamente limitado; cualquier cosa que redujera la complejidad con poco o ningún costo para la legibilidad fue casi con certeza un cambio bienvenido.

El uso de paréntesis puede parecer un poco arcaico hoy en día, pero no es así dado que alguien con cierta familiaridad con el idioma, afecta la legibilidad en comparación con otra sintaxis que es capaz de expresar las mismas cosas.

    
respondido por el a CVn 08.11.2016 - 11:40
5

El motivo es principalmente historia.

En el momento en que se escribió el primer compilador de C, las computadoras tienen RAM, CPU y compiladores muy limitados donde se escribieron "a mano" con pocas herramientas para ayudar a los escritores de compiladores. Por lo tanto, las reglas complejas eran costosas para implementarlas en un compilador. C ++, C #, Java, etc. fueron diseñados para que los programadores de C puedan aprenderlos fácilmente, por lo tanto, no se realizaron cambios "innecesarios".

En los condicionales de "‘ c like '( if, while, etc ) no se requiere un código explícito de block , solo puede usar una declaración simple.

if (a == d) doIt()

o puedes combinar las declaraciones en un compound statement al ponerlas en {}

Nos gusta que el compilador encuentre el error que cometemos y que sea un mensaje de error que podamos entender.

    
respondido por el Ian 08.11.2016 - 14:34
3

Java y C ++ se desarrollaron después de que C se convirtiera Un lenguaje de programación muy popular. Una consideración en el diseño de cada uno de esos idiomas fue que atraería a los programadores de C y cortejar a esos programadores para que usen el nuevo lenguaje. (Fui uno de los programadores de C que lograron atraer). C ++, además, fue diseñado para ser (casi) intercambiable con el código C. Para apoyar estos objetivos, tanto C ++ como Java adoptaron gran parte de La sintaxis de C, incluyendo los paréntesis alrededor de las condiciones. de if , while y switch .

De ahí la razón por la cual todos estos idiomas requieren paréntesis alrededor Las condiciones de esas declaraciones se deben a que C hace, y la pregunta es realmente por qué C requiere esos paréntesis.

Los orígenes del lenguaje C se describen en este artículo de Dennis Ritchie, uno de los autores principales de su desarrollo (Algunos podrían incluso decir el autor principal de su desarrollo). Como se dijo en ese artículo, C se desarrolló originalmente en principios de la década de 1970 como un lenguaje de programación del sistema para computadoras con Espacio extremadamente limitado en la memoria principal. Se deseaba tener un lenguaje de nivel superior al de lenguaje ensamblador, pero dados los recursos disponibles para trabajar, La facilidad para analizar el lenguaje también fue importante. Requerir los paréntesis haría que sea relativamente fácil de identificar el código condicional.

También se podría inferir que la capacidad de escribir programas usando menos caracteres se consideró una ventaja, y dos paréntesis ocupan menos espacio que la palabra clave THEN que se usó en FORTRAN y otros idiomas de alto nivel en ese momento; de hecho, como los paréntesis también podrían reemplazar los espacios como delimitadores de símbolos, if(a==b) tenía cuatro caracteres completos más cortos que IF a==b THEN .

En cualquier caso, hubo que encontrar un equilibrio entre la facilidad con la que los humanos los seres podrían leer, escribir y comprender programas escritos en C, la facilidad con la que un compilador puede analizar y compilar programas escritos en C, y cuántos kilobytes (!) serían necesarios tanto para el programa Fuente y el compilador en sí. Y paréntesis alrededor de las condiciones de if , while y switch Las declaraciones fueron cómo las personas optaron por lograr ese equilibrio en el diseño de C.

Como se evidencia en varias otras respuestas, una vez que quitas el Circunstancias particulares en las que se desarrolló C, de todo tipo. Se han usado formas de sintaxis alternativas para los condicionales. de varios lenguajes de programación. Así que los paréntesis realmente se reducen a una decisión de diseño que se tomó por unas pocas personas con ciertas restricciones en un momento determinado de la historia.

    
respondido por el David K 08.11.2016 - 04:34
3

Muchos aquí razonan que sin los paréntesis la sintaxis sería ambigua e implicaría silenciosamente que esto sería de alguna manera una situación mala o incluso imposible.

De hecho, los idiomas tienen muchas formas de lidiar con las ambigüedades. La prioridad del operador es solo una instancia de este tema.

No, la ambigüedad no es la razón de los paréntesis. Supongo que uno podría simplemente crear una versión de C que no requiera los paréntesis en torno a la condición (por lo tanto, hacerlos opcionales) y que todavía cree un código válido en todos los casos. El ejemplo de if a ++ b; podría interpretarse como equivalente a if (a) ++b; o if (a++) b; , lo que parezca más apropiado.

La pregunta de por qué Dennis Ritchie eligió hacer el () obligatorio (y, por lo tanto, acuñar este meme para muchos idiomas derivados) es más bien lingüística. Supongo que la noción de indicar claramente que la condición es una expresión en lugar de un comando fue el padre del pensamiento.

Y, de hecho, C fue diseñado para ser analizable utilizando un analizador de una pasada. El uso de una sintaxis con paréntesis obligatorios alrededor de la condición es compatible con este aspecto.

    
respondido por el Alfe 09.11.2016 - 17:59
0

No se requieren paréntesis alrededor de if de condiciones en Fortran, Cobol, PL / 1, Algol, Algo-68, Pascal, Modula, XPL, PL / M, MPL, ... o cualquier otro idioma que tenga un then palabra clave. then sirve para delimitar el condition del siguiente statement .

El paréntesis de cierre en C, etc., funciona como then , y el de apertura es formalmente redundante.

Las observaciones anteriores se aplican a los idiomas tradicionalmente analizados.

    
respondido por el user207421 08.11.2016 - 01:06

Lea otras preguntas en las etiquetas