Perché le lingue richiedono parentesi intorno alle espressioni quando vengono utilizzate con "if" e "while"?

67

Lingue come C, Java e C ++ richiedono tutte le parentesi attorno a un'intera espressione se utilizzate in if , while o switch .

if (true) {
    // Do something
}

al contrario di

if true {
    // Do something
}

Questo 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 sintassi dispari e perché è così comune? C'è un vantaggio a cui non sono a conoscenza?

    
posta Velovix 07.11.2016 - 03:26
fonte

10 risposte

155

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

In alcune lingue, non ci sono condizioni affatto , ad es. in Smalltalk, Self, Newspeak, 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 richiamato su un booleano. In questo modo, la condizione è semplicemente il destinatario del metodo e i due rami sono due argomenti, ad es. in Smalltalk:

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

Nel caso in cui tu abbia più familiarità con Java, questo è equivalente 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 non c'è nulla di speciale necessario per delimitarli:

(if aBooleanExpression 23 42)

Alcune lingue usano parole chiave come delimitatori, ad es. Algol, Ada, BASIC, Pascal, Modula-2, Oberon, Oberon-2, Oberon attivo, 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 nuova riga):

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

Vai richiede che i rami siano blocchi e non consentano espressioni o istruzioni, il che rende le parentesi graffe obbligatorie. Pertanto, le parentesi non sono obbligatorie, sebbene sia possibile aggiungerle se lo si desidera; Perl6 e Rust sono simili in questo senso:

if aBooleanExpression { return 23 } else { return 42 }

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

if aBooleanExpression: return 23
else: return 42

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

    
risposta data 07.11.2016 - 10:38
fonte
70

Le parentesi non sono necessarie se usi le parentesi.

if true ++ x;

Ad esempio diventa ambiguo senza di loro.

    
risposta data 07.11.2016 - 03:51
fonte
21

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

In un'istruzione if , le parentesi non eseguono una funzione di raggruppamento (sebbene, all'interno dell'istruzione if , è comunque possibile utilizzare le parentesi per raggruppare le espressioni aritmetiche. L'insieme esterno di parentesi serve quindi a delimitare l'intera espressione booleana ). Rendendoli richiesti semplifica il compilatore, dal momento che il compilatore può contare su quelle parentesi sempre presenti.

    
risposta data 07.11.2016 - 03:38
fonte
16

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

if true
    +x;

Perché potrebbe essere interpretato come:

if (true + x) {}

invece di:

if (true) {+x;}

Un certo numero di lingue (ad es. Python) ti permettono di evitare la parentesi ma hanno ancora un indicatore di condizione finale:

if True: +x

Tuttavia hai ragione che potrebbe definire una lingua in cui le parentesi non sono mai richieste: una lingua in cui un'espressione è non un'istruzione valida non avrà questo problema.

Sfortunatamente questo significa cose come:

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

sarebbe non essere dichiarazioni valide, quindi dovresti introdurre qualche sintassi strana per essere in grado di eseguire tali azioni senza creare espressioni. Un modo semplice per farlo è semplicemente anteporre l'espressione a un indicatore come [statement] :

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

Ora l'ambiguità scompare dal momento che dovresti scrivere:

if true
    [statement] ++x;

Ma come puoi vedere, non vedo un tale linguaggio diffuso da quando mettere la parentesi attorno a una if -condition (o a : alla sua fine) è molto meglio di mettere un tale marker per ogni espressione comunicato.

Nota : l'uso di un indicatore [statement] è solo la sintassi più semplice a cui potrei pensare. Tuttavia potrebbe avere due sintassi completamente distinte per espressioni e istruzioni senza ambiguità tra loro che non richiederebbero un tale indicatore. Il problema è: il linguaggio sarebbe estremamente strano dal momento che per fare le stesse cose in un'espressione o in un'istruzione dovresti usare una sintassi completamente diversa.

Una cosa che ti viene in mente di avere due sintassi separate senza un tale marker esplicito sarebbe, ad esempio: forzare le istruzioni ad usare simboli unicode (quindi invece di for dovresti usare qualche variazione unicode delle lettere f , o e r ), mentre le espressioni devono essere solo ASCII.

    
risposta data 07.11.2016 - 10:49
fonte
10

È comune che i linguaggi della famiglia C richiedano queste parentesi, ma non universali.

Una delle modifiche sintattiche più evidenti di Perl 6 è che hanno modificato la grammatica in modo da non dover dare le parentesi attorno alle condizioni di if , for e dichiarazioni simili. Quindi qualcosa del genere è perfettamente valido in Perl 6:

if $x == 4 {
    ...
}

come è

while $queue.pop {
    ...
}

Tuttavia, poiché sono solo espressioni puoi mettere le parentesi intorno a loro se vuoi, nel qual caso sono solo ordinarie di raggruppamento invece di una parte necessaria della sintassi come sono in C, C #, Java ecc.

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

if x == 4 {
    ...
}

Mi sembra che una caratteristica di linguaggi più moderni ispirati a C sia di guardare cose come questa e mi chiedo come rimuoverli.

    
risposta data 07.11.2016 - 09:49
fonte
6

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

C, e molti derivati C e look-alikes, ha una particolarità nel fatto che il valore di un'assegnazione è il valore assegnato Una conseguenza di ciò è che un incarico può essere usato 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 non zero come vero, incidentalmente, penso che sia solo su 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 in modo specifico sulle assegnazioni all'interno dei condizionali, perché è un errore comune), ma alcune sono effettivamente utili .

L'analisi di tali dichiarazioni sarebbe probabilmente molto più difficile se non esistesse un modo univoco per determinare dove inizia e dove finisce l'espressione condizionale.

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

Certo, potrebbero essere definite sintassi alternative per fare la stessa cosa. Ma così facendo aumenterebbe la complessità, in particolare nel parser che avrebbe quindi bisogno di gestire due diversi set di sintassi per la maggior parte della stessa cosa. Al momento della progettazione di C, la potenza di calcolo (sia in termini di capacità di calcolo del numero, di memoria di lavoro e capacità di archiviazione) era estremamente limitata; qualsiasi cosa che riducesse la complessità con un costo minimo o nullo di leggibilità era quasi certamente un cambiamento positivo.

L'uso della parentesi potrebbe sembrare un po 'arcaico oggi, ma non è così dato a qualcuno con una certa familiarità con il linguaggio, compromette la leggibilità rispetto ad altre sintassi che sono in grado di esprimere le stesse cose.

    
risposta data 08.11.2016 - 11:40
fonte
5

Il motivo è principalmente cronologico.

Nel momento 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 scrittori 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"

Nelle condizioni linguistiche "c like" ( if, while, etc ) non è richiesto un esplicito block off code, puoi semplicemente usare una semplice istruzione.

if (a == d) doIt()

oppure puoi combinare le istruzioni insieme in compound statement inserendole con {}

Ci piace che il compilatore trovi l'errore che creiamo e da dare come messaggio di errore che possiamo capire.

    
risposta data 08.11.2016 - 14:34
fonte
3

Java e C ++ sono stati sviluppati entrambi dopo che C era diventato un linguaggio di programmazione molto popolare. Una considerazione nel design di ciascuna di quelle lingue era che piacerebbe ai programmatori C e corteggiare quei programmatori per usare la nuova lingua. (Ero uno dei programmatori C che corteggiavano con successo.) Inoltre, C ++ è stato progettato per essere (quasi) intercambiabile con il codice C. Al fine di supportare questi obiettivi, sia C ++ che Java hanno adottato gran parte di questi Sintassi di C, comprese le parentesi intorno alle condizioni di if , while e switch dichiarazioni.

Di qui il motivo per cui tutte queste lingue richiedono tra parentesi le condizioni di tali affermazioni sono perché C fa, e la domanda è in realtà solo perché C richiede quelle parentesi.

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

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

In ogni caso, era necessario trovare un equilibrio tra quanto facilmente umani gli esseri sarebbero in grado di leggere, scrivere e capire i programmi scritti in C, con quanta facilità un compilatore può analizzare e compilare programmi scritti in C, e quanti kilobyte (!) sarebbero richiesti sia per il programma fonte e il compilatore stesso. E parentesi intorno alle condizioni di if , while e switch le dichiarazioni erano come le persone hanno scelto di trovare un equilibrio nel design di C.

Come evidenziato in diverse altre risposte, una volta che togli il circostanze particolari in cui è stato sviluppato C, tutti i tipi di forme alternative di sintassi sono state usate per i condizionali di vari linguaggi di programmazione. Quindi le parentesi sono appena arrivate a una decisione progettuale che è stata presa da alcune persone sotto determinati vincoli in un determinato momento della storia.

    
risposta data 08.11.2016 - 04:34
fonte
3

Molti qui spiegano che senza le parentesi la sintassi sarebbe ambigua e implicherebbe silenziosamente che questa sarebbe in qualche modo una situazione negativa o addirittura impossibile.

In effetti, le lingue hanno molti modi per affrontare le ambiguità. La precedenza degli operatori è solo un'istanza di questo argomento.

No, l'ambiguità non è la ragione delle parentesi. Immagino che si possa semplicemente creare una versione di C che non richiede le parentesi attorno alla condizione (rendendole così opzionali) e che crea comunque 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; , qualsiasi cosa sia più appropriata.

La domanda sul perché Dennis Ritchie ha scelto di rendere il () obbligatorio (e quindi coniando questo meme per molte lingue derivate) è piuttosto linguistico. Immagino che la nozione di affermare chiaramente che la condizione sia un'espressione piuttosto che un comando era il padre del pensiero.

E infatti, C è stato progettato per essere analizzabile usando un parser a un passaggio. L'uso di una sintassi con parentesi obbligatorie attorno alla condizione supporta questo aspetto.

    
risposta data 09.11.2016 - 17:59
fonte
0

Le parentesi attorno alle condizioni di if 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 then parola chiave. then serve a delimitare condition dal seguente statement .

La parentesi chiusa in C ecc funziona come then , e quella iniziale è formalmente ridondante.

Le osservazioni di cui sopra si applicano alle lingue tradizionalmente analizzate.

    
risposta data 08.11.2016 - 01:06
fonte

Leggi altre domande sui tag