Perché le lingue richiedono parentesi attorno alle espressioni se utilizzate con "if" e "while"?


67

Linguaggi come C, Java e C ++ tutti richiedono parentesi intorno ad una intera espressione quando viene utilizzato in una if, whileo switch.

if (true) {
    // Do something
}

al contrario di

if true {
    // Do something
}

Mi sembra strano perché le parentesi sono ridondanti. In questo esempio, trueè una singola espressione a sé stante. La parentesi non trasforma il suo significato in alcun modo che io conosca. Perché esiste questa strana sintassi e perché è così comune? C'è un vantaggio ad esso di cui non sono a conoscenza?


20
Pascal non richiede parentesi (perché richiede a THEN).
JimmyB,

30
Python, Ruby no.
smci,

31
Credo che C usi le parentesi perché le parentesi graffe sono opzionali per un corpo a singola istruzione. O forse un modo migliore per dirlo è che le parentesi graffe non fanno parte ifdell'istruzione, ma creano semplicemente un'istruzione composta.
Fred Larson,

7
Va, interessante, richiede parentesi graffe ma non parentesi.
Kos,

25
La domanda è un po 'tautologica. Perché i tombini rotondi sono tutt'intorno? Perché tutti i fratelli sono maschi? Perché le lingue che richiedono i genitori richiedono tutti i genitori? I tombini rotondi sono rotondi per definizione; i fratelli sono maschi per definizione; le lingue che richiedono parentesi richiedono parentesi per definizione.
Eric Lippert,

Risposte:


155

Ci deve essere un modo per dire dove finisce la condizione e inizia il ramo. Ci sono molti modi diversi per farlo.

In alcune lingue, non ci sono condizionali a tutti , ad esempio in Smalltalk, Sé, neolingua, Io, Ioke, Seph, e Fancy. La ramificazione condizionale viene semplicemente implementata come un metodo normale come qualsiasi altro metodo. Il metodo è implementato su oggetti booleani e viene chiamato su un valore booleano. In questo modo, la condizione è semplicemente il destinatario del metodo e i due rami sono due argomenti, ad esempio in Smalltalk:

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

Nel caso in cui tu abbia più familiarità con Java, questo equivale al seguente:

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

Nella famiglia di lingue Lisp, la situazione è simile: i condizionali sono solo funzioni normali (in realtà, macro) e il primo argomento è la condizione, il secondo e il terzo argomento sono i rami, quindi sono solo normali argomenti di funzione, e c'è niente di speciale necessario per delimitarli:

(if aBooleanExpression 23 42)

Alcune lingue usano parole chiave come delimitatori, ad esempio Algol, Ada, BASIC, Pascal, Modula-2, Oberon, Oberon-2, Active Oberon, Component Pascal, Zonnon, Modula-3:

IF aBooleanExpression THEN RETURN 23 ELSE RETURN 42;

In Ruby, puoi utilizzare una parola chiave o un separatore di espressioni (punto e virgola o newline):

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 richiede che i rami siano blocchi e non consente espressioni o dichiarazioni, il che rende obbligatorie le parentesi graffe. Pertanto, le parentesi non sono necessarie, anche se è possibile aggiungerle se lo si desidera; Perl6 e Rust sono simili a questo proposito:

if aBooleanExpression { return 23 } else { return 42 }

Alcune lingue usano altri caratteri non alfanumerici per delimitare la condizione, ad esempio Python:

if aBooleanExpression: return 23
else: return 42

La linea di fondo è: è necessario un modo per dire dove finisce la condizione e inizia il ramo. Ci sono molti modi per farlo, le parentesi sono solo una di queste.


8
Ottima panoramica.
Peter - Ripristina Monica il

2
ovviamente in una lingua in cui le espressioni nude non sono un'istruzione (ad esempio qualcosa come BASIC precedente in cui un valore calcolato deve essere assegnato a una variabile o passato a un'altra istruzione) o in cui non sono presenti operatori prefisso, dovresti sempre essere in grado identificare comunque la fine di un'espressione e l'inizio di una frase. Ho potuto sicuramente vedere una variante BASIC gestire senza un delimitatore alla fine di un'istruzione IF.
Periata Breatta,

4
Inoltre, poiché C è stato progettato negli anni '70 e il calcolo era costoso, l'aggiunta di una piccola parentesi renderebbe probabilmente il parser un po 'più facile da scrivere.
Machado,

4
Ri: Lisp: "in realtà, macro". In pratica IF è un modulo speciale in Scheme e CL (solo per completezza).
coredump,

1
@Leushenko: E, ad esempio in MISC, che è pigro per impostazione predefinita, tutte le forme condizionali sono solo funzioni normali, né macro né forme speciali. (Anzi, AFAIR, MISC ha zero forme speciali?)
Jörg W Mittag

70

Le parentesi non sono necessarie solo se si utilizzano le parentesi graffe.

if true ++ x;

Ad esempio diventa ambiguo senza di loro.


28
@RobertHarvey - Le parentesi sono richieste praticamente da tutte le lingue di cui sono a conoscenza. Certamente C e i suoi parenti. L'OP sta chiedendo perché sono richiesti - ed è perché altrimenti la lingua diventerebbe ambigua.
Telastyn,

25
Appena fuori dalla cima della mia testa, le parentesi non sono richieste per ifin: basic, assembly, python, bash / zsh, tcl, batch, brainfuck o code machine. La mancanza di parentesi rende ifambiguo solo se la lingua è stata progettata per dipendere da loro.
candied_orange

12
Sono sorpreso che nessuno abbia menzionato la versione più logica e leggibile - in Pascal (incl. Delphi) lo è if Condition then ....
Ulrich Gerhardt,

18
Go è un buon esempio di fare il contrario. Rende le parentesi graffe {}obbligatorie e quindi non richiede parentesi attorno all'espressione. Non solo le parentesi non sono necessarie, ma se ricordo che aggiungere correttamente le parentesi causerebbe un errore di compilazione - sono vietate
slebetman

10
@Eiko Lasciami riformulare. L'esempio nella risposta è sintatticamente ambiguo, anche se semanticamente non ambiguo (come hai notato). Ma poiché la fase di analisi avviene prima dell'analisi semantica, il parser incontrerà l'ambiguità - e deve fare un'ipotesi non informata o fallire. Se (per qualsiasi motivo) il parser sceglie di non fallire, l'analizzatore semantico lavorerà con l'albero risultante, qualunque esso sia. Non ho visto un compilatore in cui l'analizzatore semantico è disposto a chiedere al parser di analizzare il sottostruttura e fare una scelta diversa nel costrutto sintatticamente ambiguo.
Theodoros Chatzigiannakis,

21

Le parentesi in ifun'istruzione non hanno lo stesso significato delle parentesi usate all'interno di un'espressione aritmetica. Le parentesi in un'espressione aritmetica vengono utilizzate per raggruppare espressioni. Le parentesi in ifun'istruzione sono usate per delimitare l'espressione booleana; cioè, per differenziare l'espressione booleana dal resto ifdell'affermazione.

In ifun'istruzione, le parentesi non svolgono una funzione di raggruppamento (sebbene, all'interno ifdell'istruzione, è ancora possibile utilizzare le parentesi per raggruppare espressioni aritmetiche. L'insieme esterno di parentesi serve quindi a delimitare l'intera espressione booleana). Renderli necessari semplifica il compilatore, poiché il compilatore può fare affidamento su quelle parentesi che sono sempre lì.


Non vedo come semplifichi il compilatore. La regola `IF '(' espressione ')' istruzione` non è più semplice di IF primary_expression statement. Si noti che quest'ultimo è ugualmente inequivocabile.
user58697

@ user58697: No, solo quest'ultimo ha l'ambiguità in cui un operatore postfix su primary_expressionnon può essere distinto da un operatore prefisso su un'istruzione-espressione. Per copiare la risposta di Telastyn, if true ++ x;. Inoltre, se esistono istruzioni vuote, if a & f;può essere un'istruzione vuota e binaria &all'interno della condizione o unaria &all'inizio dell'istruzione. Ma quando si corrispondono le parentesi, c'è esattamente una corrispondenza per l'apertura (
MSalters

@MSalters Un operatore postfix non analizza come primario. Un'espressione primaria è una delle IDENTIFIER, CONSTANT, STRING_LITERALe '(' expression ')'.
user58697

@ user58697: sembra che tu abbia in mente un linguaggio particolare. E sembra avere una regola secondo cui le parentesi non sono necessarie se e solo se le condizioni sono un "IDENTIFICATORE, COSTANTE o STRING_LITERAL". Non sono sicuro che renda le cose più facili.
MSalters il

16

Come altri hanno già parzialmente sottolineato, ciò è dovuto al fatto che anche le espressioni sono dichiarazioni valide e nel caso di un blocco con una sola istruzione è possibile eliminare parentesi graffe. Ciò significa che è ambiguo quanto segue:

if true
    +x;

Perché potrebbe essere interpretato come:

if (true + x) {}

invece di:

if (true) {+x;}

Diverse lingue (ad es. Python) consentono di evitare la parentesi ma hanno ancora un indicatore delle condizioni finali:

se Vero : + x

Comunque hai ragione che noi potremmo definire un linguaggio in cui la parentesi non sono mai richiesto: un linguaggio in cui l'espressione è non una dichiarazione valida non avrà questo problema.

Purtroppo questo significa che cose come:

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

sarebbe non essere dichiarazioni valide, in modo che avrebbe dovuto introdurre qualche sintassi strano per essere in grado di eseguire tali azioni senza creare espressioni. Un modo semplice per farlo è semplicemente anteporre l'espressione di un marcatore come [statement]:

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

Ora l'ambiguità scompare poiché dovresti scrivere:

if true
    [statement] ++x;

Ma come puoi vedere, non vedo che un linguaggio del genere sia diffuso poiché mettere la parentesi attorno a una condizione if(o una :alla sua fine) è molto meglio che mettere un tale indicatore per ogni affermazione di espressione.


Nota : l'uso di un [statement]marker è solo la sintassi più semplice che mi venga in mente. Tuttavia si potrebbe avere due sintassi completamente distinti per le espressioni e le dichiarazioni senza ambiguità tra di loro, che non richiederebbe un tale marcatore. Il problema è: il linguaggio sarebbe estremamente strano dal momento che per fare le stesse cose in un'espressione o un'affermazione dovresti usare una sintassi completamente diversa.

Una cosa che viene in mente di avere due sintassi separate senza un marcatore così esplicito sarebbe, ad esempio: forzare le istruzioni a usare simboli unicode (quindi invece di forusare una variazione unicode delle lettere f, oe r), mentre le espressioni devono essere Solo ASCII.


2
In realtà esiste un linguaggio che utilizza un tale indicatore di dichiarazione di espressione: se si desidera valutare un'espressione per i suoi effetti collaterali, è necessario esplicitamente il discardsuo valore in Nim . Tuttavia, ciò è stato fatto solo per la sicurezza dei tipi, non per motivi sintattici.
amon,

@amon Nice, non lo sapevo. Comunque, come ho detto, il marker non è davvero necessario, è solo un modo semplice per raggiungere questa distinzione senza inventare sintassi non intuitive.
Bakuriu,

1
@amon - molte varianti BASIC hanno anche una stretta separazione tra espressioni e dichiarazioni. Le espressioni sono consentite solo nei luoghi in cui verrà effettivamente utilizzato il valore (ad es. Assegnazioni di variabili, istruzioni come PRINT che eseguono un'azione e così via). Le procedure che non vengono utilizzate per calcolare un valore sono invocate da una parola chiave (in genere "CALL", sebbene almeno una variante che conosca usi "PROC") che precede il loro nome. E così via. Di solito BASIC delimita la fine dell'espressione in un'istruzione IF con "THEN", ma non vedo alcun motivo tecnico per cui il requisito non possa essere eliminato.
Periata Breatta,

1
C'è un linguaggio molto popolare negli anni '80 che ha blocchi senza parentesi, parentesi graffe, due punti o un altro marcatore e accetta espressioni come dichiarazioni ovunque e alcune istruzioni si comportano come espressioni (operatori composti come + = e ++). È peggio, c'è un pre-elaboratore stupido prima del compilatore ( ?simbol in realtà è una funzione dopo PP). Non v'è alcuna ;. Certo, ha bisogno di un marcatore per la linea di continuazione, ma questo è scoraggiato. harbour.github.io/doc/clc53.html#if-cmd . Il compilatore è veloce e semplice (creato con Bison / Flex).
Maniero,

@bigown Raggiungono che, utilizzando una sintassi separato per logiche-condizioni, quindi, in sostanza, le condizioni per if, whileecc sono limitati rispetto alle espressioni generici utilizzati in altre lingue. Certo: se hai più di due categorie sintattiche (come l'affermazione, l'espressione, l'espressione logica, l'espressione del caffè, ...) puoi scambiare un po 'di libertà.
Bakuriu,

10

È comune per le lingue della famiglia C richiedere queste parentesi, ma non universale.

Uno dei più evidenti cambiamenti sintattici di Perl 6 è che modificato la grammatica in modo che non si deve dare le parentesi intorno if, fore le condizioni di dichiarazioni simili. Quindi qualcosa di simile è perfettamente valido in Perl 6:

if $x == 4 {
    ...
}

come è

while $queue.pop {
    ...
}

Tuttavia, poiché sono solo espressioni, è possibile racchiuderle tra parentesi, se lo si desidera, nel qual caso sono solo normali raggruppamenti anziché una parte richiesta della sintassi come in C, C #, Java ecc.

Rust ha una sintassi simile a Perl 6 in questo dipartimento:

if x == 4 {
    ...
}

Mi sembra che una caratteristica dei più moderni linguaggi ispirati alla C sia guardare cose come questa e chiedersi di rimuoverle.


Mentre la tua risposta fornisce alcune informazioni in altre lingue, non risponde "Perché esiste questa strana sintassi e perché è così comune?"

Hai ragione. Stavo davvero cercando di aggiungere un contesto alla domanda, che non è qualcosa di facile da fare in questa piattaforma. Immagino che alla fine se fornisco una risposta alla domanda è "solo perché, dato che non ci sono motivi tecnici, la grammatica non avrebbe potuto accomodarsi senza averli". Ma questa non è una risposta molto utile.
Matthew Walton,

In Perl 5 ci sono entrambi. Per un costrutto normale if o ad anello con BLOCCHI, sono necessari i parentesi, ad esempio in if ( $x == 4 ) { ... }o foreach my $foo ( @bar ) { ... }. Quando viene utilizzata la notazione postfix, le parentesi sono opzionali, come in return unless $foo;o ++$x while s/foo/bar/g;.
simbabque

6

C'è un aspetto che mi sorprende che nessuna delle risposte esistenti abbia sollevato.

C, e molti derivati ​​C e sosia, ha una peculiarità in quanto il valore di un'assegnazione è il valore assegnato. Una conseguenza di ciò è che un'assegnazione può essere utilizzata dove è previsto un valore.

Questo ti permette di scrivere cose come

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

o

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

o

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

(che è implicitamente trattato come while (n < m && *p1++ = *p2++ != 0) { n++; }perché C considera diverso da zero come vero; per inciso, penso che sia solo strncpy () nella libreria standard C)

o anche

if (x = 17);

ed è tutto valido. Non tutte le combinazioni sintatticamente valide sono necessariamente utili (e i compilatori moderni avvertono specificamente delle assegnazioni all'interno dei condizionali, perché è un errore comune), ma alcune di esse sono effettivamente utili.

L'analisi di tali affermazioni sarebbe probabilmente molto più difficile se non ci fosse un modo inequivocabile per determinare dove inizia e finisce l'espressione condizionale.

Le parentesi erano già utilizzate per delimitare i nomi delle funzioni dagli argomenti delle funzioni, quindi immagino che sembrassero una scelta naturale anche per delimitare le parole chiave dagli argomenti delle parole chiave.

Certo, si potrebbero definire sintassi alternative per fare la stessa cosa. Ma così facendo aumenterebbe la complessità, in particolare nel parser che dovrebbe quindi gestire due diversi insiemi di sintassi per la stessa cosa. All'epoca della progettazione di C, la potenza di calcolo (sia in termini di capacità di riduzione del numero, memoria di lavoro e capacità di archiviazione) era estremamente limitata; tutto ciò che riduceva la complessità a costo ridotto o nullo per la leggibilità era quasi certamente un gradito cambiamento.

L'uso della parentesi può sembrare un po 'arcaico oggi, ma non è come se qualcuno avesse una certa familiarità con la lingua, compromette la leggibilità rispetto ad altre sintassi che sono in grado di esprimere le stesse cose.


5

Il motivo è principalmente storia.

Al tempo in cui è stato scritto il primo compilatore C, i computer hanno ram, cpu e compilatori molto limitati, scritti "a mano" con pochi strumenti per aiutare gli autori di compilatori. Pertanto , le regole complesse erano costose da implementare in un compilatore. C ++, C #, Java, ecc. Sono stati tutti progettati per essere facili da imparare per i programmatori C, quindi non sono state apportate modifiche "non necessarie".

In linguaggi 'c like' i condizionali ( if, while, etc) non richiedono un blockcodice esplicito off, puoi semplicemente usare una semplice istruzione.

if (a == d) doIt()

oppure puoi combinare insieme le istruzioni in a compound statementinserendole con in{}

Ci piace il compilatore per trovare l'errore che facciamo e per dare come un messaggio di errore che possiamo capire.


3

Java e C ++ furono entrambi sviluppati dopo che C era diventato un linguaggio di programmazione molto popolare. Una considerazione nella progettazione di ognuna di quelle lingue era che avrebbe attirato i programmatori C e corteggiare quei programmatori per usare il nuovo linguaggio. (Ero uno dei programmatori C che hanno corteggiato con successo.) Inoltre C ++ è stato progettato per essere (quasi) intercambiabile con il codice C. Per sostenere questi obiettivi, sia C ++ e Java hanno adottato gran parte della sintassi del C, comprese le parentesi che racchiudono le condizioni di if, whilee switchle dichiarazioni.

Quindi il motivo per cui tutte queste lingue richiedono parentesi attorno alle condizioni di quelle affermazioni è perché C lo fa, e la domanda è proprio perché C richiede quelle parentesi.

Le origini del linguaggio C sono descritte in questo articolo da Dennis Ritchie, uno dei principali autori del suo sviluppo (alcuni potrebbero addirittura dire l' autore principale del suo sviluppo). Come detto in quell'articolo, C è stato originariamente sviluppato nei primi anni '70 come linguaggio di programmazione del sistema per computer con uno spazio estremamente limitato nella memoria principale. Si desiderava avere un linguaggio di livello superiore rispetto al linguaggio assembly, ma date le risorse disponibili con cui lavorare, la facilità di analisi del linguaggio era anche importante. Richiedere le parentesi renderebbe relativamente semplice l'identificazione del codice condizionale.

Si potrebbe anche dedurre che la capacità di scrivere programmi usando meno caratteri era considerata un vantaggio, e due parentesi occupano meno spazio della parola chiave THENche era stata usata in FORTRAN e in altre lingue di alto livello in quel momento; infatti, poiché le parentesi potevano anche sostituire gli spazi come delimitatori di simboli, if(a==b)quattro caratteri interi erano più brevi di IF a==b THEN.

In ogni caso, è stato necessario trovare un certo equilibrio tra la facilità con cui gli esseri umani sarebbero in grado di leggere, scrivere e comprendere i programmi scritti in C, la facilità con cui un compilatore potrebbe analizzare e compilare i programmi scritti in C e quanti kilobyte (!) sarebbe richiesto sia per l'origine del programma che per il compilatore stesso. E le parentesi attorno alle condizioni di if, whilee le switch dichiarazioni erano il modo in cui le persone scelsero di raggiungere quell'equilibrio nella progettazione di C.

Come evidenziato in diverse altre risposte, una volta eliminate le circostanze particolari in cui è stata sviluppata la C, sono stati usati tutti i tipi di forme alternative di sintassi per i condizionali di vari linguaggi di programmazione. Quindi le parentesi si basano proprio su una decisione progettuale che è stata presa da alcune persone sotto determinati vincoli in un determinato momento della storia.


Non sono sicuro che sia giusto dire che il C ++ è stato progettato come "per convincere quei programmatori a usare un nuovo linguaggio". Ricordi C con le classi ?
un CVn

@ MichaelKjörling Certo, gli sviluppatori Java erano molto più espliciti sul "corteggiamento". Ma nota che l'articolo collegato cita, come uno dei motivi per cui Stroustrup ha scelto di iniziare con C come base del suo linguaggio, che C era ampiamente usato. Un modo in cui ciò ha fornito una motivazione per stare vicino a C era perché il codice esistente poteva essere facilmente adattato (come ho già detto) - ma anche i programmatori esistenti potevano facilmente adattarsi.
David K,

@ MichaelKjörling Suppongo che la formulazione originale della mia risposta suggerisse che il "corteggiamento" fosse un fattore maggiore nel design del linguaggio di quanto non fosse in realtà. Ho modificato la risposta per cercare di chiarire che era semplicemente una cosa che ha preso in considerazione il design del linguaggio.
David K,

3

Molti qui sostengono che senza le parentesi la sintassi sarebbe ambigua e implicherebbe in silenzio che ciò sarebbe in qualche modo negativo o addirittura impossibile.

In effetti, le lingue hanno molti modi per gestire le ambiguità. La precedenza dell'operatore è solo un'istanza di questo argomento.

No, l'ambiguità non è il motivo delle parentesi. Immagino che si potrebbe semplicemente creare una versione di C che non richiede le parentesi attorno alla condizione (rendendole così facoltative) e che crea comunque un codice valido in tutti i casi. L'esempio di if a ++ b;potrebbe essere interpretato come equivalente a , if (a) ++b;o if (a++) b;qualunque cosa sembri più appropriata.

La domanda sul perché Dennis Ritchie abbia scelto di rendere obbligatorio () (e quindi coniare questo meme per molte lingue derivate) è piuttosto linguistica. Immagino che l'idea di affermare chiaramente che la condizione sia un'espressione piuttosto che un comando fosse il padre del pensiero.

E in effetti, C è stato progettato per essere analizzabile utilizzando un parser one-pass. L'uso di una sintassi con parentesi obbligatorie attorno alla condizione supporta questo aspetto.


Vedo un downvote nella mia risposta. Sii così gentile da spiegare in un commento cosa non ti è piaciuto. Forse posso migliorarlo allora.
Alfe,

0

Le parentesi attorno alle ifcondizioni non sono richieste in Fortran, Cobol, PL / 1, Algol, Algo-68, Pascal, Modula, XPL, PL / M, MPL, ... o in qualsiasi altra lingua che abbia una thenparola chiave. thenserve per delimitare conditionquanto segue statement.

La parentesi di chiusura in C ecc. Funziona come then, e quella di apertura è formalmente ridondante.

Le osservazioni sopra si applicano alle lingue tradizionalmente analizzate.


Fortran non richiede parentesi in tutte le versioni del suo IF, tra cui uno strutturale.
Netch
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.