Perché per-each ha due punti invece di "in"?

8

Dalla guida del linguaggio Java 5

:

When you see the colon (:) read it as "in".

Perché non utilizzare in in primo luogo, quindi?

Questo mi ha infastidito per anni. Perché è incoerente con il resto della lingua. Ad esempio, in Java ci sono implements , extends , super per le relazioni tra tipi invece di simboli come in C ++, Scala o Ruby.

In due punti Java utilizzati in 5 contesti . Tre dei quali sono ereditati da C. E altri due sono stati approvati da Joshua Bloch. Almeno, era lui sais durante "La polemica delle chiusure" parlare. Questo emerge quando critica l'uso di due punti per mappare come incoerente con la semantica di ogni semantica. Il che a me sembra strano perché è il modello previsto per abuso. Mi piace list_name/category: elements o laberl/term: meaning .

Ho curiosato su jcp e jsr, ma non ho trovato alcun segno di mailing list. Nessuna discussione su questo argomento è stata trovata da Google. Solo i neofiti sono confusi dal significato dei due punti in for .

Argomenti principali contro in forniti finora:

  • richiede una nuova parola chiave; e
  • complica il lexing.

Esaminiamo le definizioni pertinenti grammatica :

statement
    :   'for' '(' forControl ')' statement
    |   ...
    ;

forControl
    :   enhancedForControl
    |   forInit? ';' expression? ';' forUpdate?
    ;

enhancedForControl
    :   variableModifier* type variableDeclaratorId ':' expression
    ;

Cambia da : a in non portare complessità aggiuntiva o richiede una nuova parola chiave.

    
posta user2418306 16.03.2016 - 23:07
fonte

2 risposte

8

I parser normali come vengono generalmente insegnati hanno uno stadio lexer prima che il parser tocchi l'input. Il lexer (anche "scanner" o "tokenizer") taglia l'input in piccoli token che sono annotati con un tipo. Ciò consente al parser principale di usare token come elementi terminali piuttosto che dover trattare ciascun carattere come un terminale, il che porta a guadagni di efficienza evidenti. In particolare, il lexer può anche rimuovere tutti i commenti e lo spazio bianco. Tuttavia, una fase separata di tokenizer significa che le parole chiave non possono essere utilizzate anche come identificatori (a meno che la lingua supporti stropping che è in qualche modo caduto in disgrazia o prefisso tutti gli identificatori con un sigillo come $foo ).

Perché? Supponiamo di avere un semplice tokenizzatore che comprende i seguenti token:

FOR = 'for'
LPAREN = '('
RPAREN = ')'
IN = 'in'
IDENT = /\w+/
COLON = ':'
SEMICOLON = ';'

Il tokenizer corrisponderà sempre al token più lungo e preferirà le parole chiave agli identificatori. Quindi interesting sarà lexed come IDENT:interesting , ma in sarà lexed come IN , mai come IDENT:interesting . Un frammento di codice come

for(var in expression)

sarà tradotto nello stream di token

FOR LPAREN IDENT:var IN IDENT:expression RPAREN

Finora, funziona. Ma qualsiasi variabile in verrebbe lessicata come la parola chiave IN piuttosto che una variabile, che interromperà il codice. Il lexer non mantiene nessuno stato tra i token e non può sapere che in dovrebbe essere normalmente una variabile tranne quando siamo in un ciclo for. Inoltre, il seguente codice dovrebbe essere legale:

for(in in expression)

Il primo in sarebbe un identificatore, il secondo sarebbe una parola chiave.

Ci sono due reazioni a questo problema:

Le parole chiave contestuali sono confuse, riutilizziamo invece le parole chiave.

Java ha molte parole riservate, alcune delle quali non hanno utilità se non quella di fornire messaggi di errore più utili ai programmatori che passano a Java dal C ++. L'aggiunta di nuove parole chiave interrompe il codice. L'aggiunta di parole chiave contestuali è fonte di confusione per un lettore del codice, a meno che non abbiano una buona evidenziazione della sintassi e rende gli strumenti difficili da implementare perché dovranno utilizzare tecniche di analisi più avanzate (vedi sotto).

Quando vogliamo estendere la lingua, l'unico approccio sano è usare simboli che in precedenza non erano legali nella lingua. In particolare, questi non possono essere identificatori. Con la sintassi del ciclo foreach, Java ha riutilizzato la parola chiave : esistente con un nuovo significato. Con lambdas, Java ha aggiunto una parola chiave -> che in precedenza non poteva verificarsi in nessun programma legale ( --> sarebbe ancora lexed come '--' '>' che è legale e -> potrebbe essere stato precedentemente lexato come '-', '>' , ma quella sequenza verrebbe rifiutata dal parser).

Le parole chiave contestuali semplificano le lingue, implementiamole

I lessici sono indiscutibilmente utili. Ma invece di eseguire un lexer prima del parser, possiamo eseguirli in tandem con il parser. I parser bottom-up conoscono sempre l'insieme di tipi di token che sarebbero accettabili in qualsiasi posizione specifica. Il parser può quindi richiedere al lexer di trovare uno qualsiasi di questi tipi nella posizione corrente. In un ciclo for-each, il parser si troverà nella posizione indicata da · nella grammatica (semplificata) dopo che la variabile è stata trovata:

for_loop = for_loop_cstyle | for_each_loop
for_loop_cstyle = 'for' '(' declaration · ';' expression ';' expression ')'
for_each_loop = 'for' '(' declaration · 'in' expression ')'

In questa posizione, i token legali sono SEMICOLON o IN , ma non IDENT . Una parola chiave in sarebbe completamente non ambigua.

In questo particolare esempio, i parser top-down non avrebbero alcun problema dato che possiamo riscrivere la grammatica di cui sopra

for_loop = 'for' '(' declaration · for_loop_rest ')'
for_loop_rest =  · ';' expression ';' expression
for_loop_rest = · 'in' expression

e tutti i token necessari per la decisione possono essere visualizzati senza retrocedere.

Considera l'usabilità

Java è sempre stato orientato verso la semplicità semantica e sintattica. Ad esempio, la lingua non supporta l'overloading dell'operatore perché renderebbe il codice molto più complicato. Pertanto, quando decidiamo tra in e : per una sintassi del ciclo for-each, dobbiamo considerare quale è meno confuso e più evidente per gli utenti. Il caso estremo sarebbe probabilmente

for (in in in in())
for (in in : in())

(Nota: Java ha spazi dei nomi separati per nomi di tipi, variabili e metodi.Penso che questo sia stato un errore, soprattutto.Questo non significa che la progettazione linguistica successiva debba aggiungere più errori.)

Quale alternativa offre separazioni visive più chiare tra la variabile di iterazione e la collezione iterata? Quale alternativa può essere riconosciuta più rapidamente quando si guarda il codice? Ho scoperto che separare i simboli è meglio di una stringa di parole quando si tratta di questi criteri. Altre lingue hanno valori diversi. Per esempio. Python spiega molti operatori in inglese in modo che possano essere letti in modo naturale e facili da capire, ma quelle stesse proprietà possono rendere difficile la comprensione di un pezzo di Python a colpo d'occhio.

    
risposta data 17.03.2016 - 16:45
fonte
16

La sintassi del ciclo for-each è stata aggiunta in Java 5. Dovresti rendere in una parola chiave della lingua e aggiungere parole chiave a una lingua in un secondo momento è qualcosa che eviti a tutti i costi perché interrompe il codice esistente - improvvisamente tutte le variabili denominate in causano un errore di analisi. enum è stato abbastanza brutto in questo senso.

    
risposta data 16.03.2016 - 23:14
fonte

Leggi altre domande sui tag