No guia da linguagem Java 5 :
Quando vir os dois pontos (:), leia-os como "em".
Por que não usar in
em primeiro lugar, então?
Isso me incomoda há anos. Porque é inconsistente com o resto do idioma. Por exemplo, em Java há implements
, extends
, super
para as relações entre os tipos em vez de símbolos como em C ++, Scala ou Ruby.
Em Java dois pontos utilizados em 5 contextos . Três dos quais são herdados de C. E outros dois foram endossados por Joshua Bloch. Pelo menos, foi o que ele falou durante a palestra "A controvérsia do fechamento" . Isso ocorre quando ele critica o uso de dois pontos no mapeamento como inconsistente com a semântica de cada uma. O que para mim parece estranho, porque são os padrões esperados para cada abuso. Como list_name/category: elements
ou laberl/term: meaning
.
Eu bisbilhotei o jcp e o jsr, mas não encontrei sinal de lista de discussão. Nenhuma discussão sobre este assunto foi encontrada pelo google. Apenas novatos confusos com o significado de dois pontos em for
.
Principais argumentos contra in
fornecidos até agora:
- requer nova palavra-chave; e
- complica lexing.
Vejamos as definições gramaticais relevantes :
declaração : declaração 'for' '(' forControl ')' | ... ; forControl : EnhancedForControl | forInit? ';' expressão? ';' forUpdate? ; EnhancedForControl : variableModifier * tipo variableDeclaratorId ':' expression ;
Mude de :
para in
não trazer complexidade adicional ou requer nova palavra-chave.
Respostas:
Analisadores normais, como geralmente são ensinados, têm um estágio de lexer antes que o analisador toque na entrada. O lexer (também "scanner" ou "tokenizer") divide a entrada em pequenos tokens anotados com um tipo. Isso permite que o analisador principal use tokens como elementos terminais em vez de precisar tratar cada caractere como um terminal, o que leva a ganhos de eficiência perceptíveis. Em particular, o lexer também pode remover todos os comentários e espaços em branco. No entanto, uma fase separada do tokenizer significa que as palavras-chave também não podem ser usadas como identificadores (a menos que o idioma suporte stropping que tenha caído em desuso, ou prefixe todos os identificadores com um símbolo
$foo
).Por quê? Vamos supor que temos um tokenizador simples que compreende os seguintes tokens:
O tokenizer sempre corresponderá ao token mais longo e preferirá palavras-chave a identificadores. Assim
interesting
será lexado comoIDENT:interesting
, masin
será lexado comoIN
, nunca comoIDENT:interesting
. Um trecho de código comoserá traduzido para o fluxo de token
Até agora, isso funciona. Mas qualquer variável
in
seria lexada como a palavra-chave emIN
vez de uma variável, o que quebraria o código. O lexer não mantém nenhum estado entre os tokens e não pode saber quein
geralmente deve ser uma variável, exceto quando estamos em um loop for. Além disso, o código a seguir deve ser legal:O primeiro
in
seria um identificador, o segundo seria uma palavra-chave.Há duas reações a esse problema:
Palavras-chave contextuais são confusas, vamos reutilizar palavras-chave.
Java possui muitas palavras reservadas, algumas das quais não têm utilidade, exceto por fornecer mensagens de erro mais úteis aos programadores que mudam para C ++ em Java. A adição de novas palavras-chave quebra o código. A adição de palavras-chave contextuais é confusa para o leitor do código, a menos que ele tenha um bom destaque de sintaxe e dificulte a implementação de ferramentas, pois elas terão que usar técnicas de análise mais avançadas (veja abaixo).
Quando queremos estender o idioma, a única abordagem sensata é usar símbolos que antes não eram legais no idioma. Em particular, estes não podem ser identificadores. Com a sintaxe do loop foreach, o Java reutilizou a
:
palavra-chave existente com um novo significado. Com as lambdas, o Java adicionou uma->
palavra - chave que não poderia ocorrer anteriormente em nenhum programa jurídico (-->
ainda seria lexada como'--' '>'
legal e->
pode ter sido lexada como'-', '>'
, mas essa sequência seria rejeitada pelo analisador).Palavras-chave contextuais simplificam idiomas, vamos implementá-los
Lexers são indiscutivelmente úteis. Mas, em vez de executar um lexer antes do analisador, podemos executá-lo em conjunto com o analisador. Os analisadores de baixo para cima sempre sabem o conjunto de tipos de token que seriam aceitáveis em qualquer local. O analisador pode solicitar ao lexer que corresponda a qualquer um desses tipos na posição atual. Em um loop for-each, o analisador estaria na posição indicada
·
na gramática (simplificada) depois que a variável fosse encontrada:Nessa posição, os tokens legais são
SEMICOLON
ouIN
, mas nãoIDENT
. Uma palavra-chavein
seria totalmente inequívoca.Neste exemplo em particular, os analisadores de cima para baixo também não teriam problemas, pois podemos reescrever a gramática acima para
e todos os tokens necessários para a decisão podem ser vistos sem retroceder.
Considere a usabilidade
O Java sempre tendeu à simplicidade semântica e sintática. Por exemplo, o idioma não suporta sobrecarga do operador, pois isso tornaria o código muito mais complicado. Portanto, ao decidir entre
in
e:
para uma sintaxe de loop para cada um, precisamos considerar qual é menos confuso e mais aparente para os usuários. O caso extremo provavelmente seria(Nota: Java possui espaços de nomes separados para nomes de tipos, variáveis e métodos. Acho que isso foi um erro, principalmente. Isso não significa que o design posterior da linguagem precise adicionar mais erros.)
Qual alternativa fornece separações visuais mais claras entre a variável de iteração e a coleção iterada? Qual alternativa pode ser reconhecida mais rapidamente quando você olha o código? Descobri que os símbolos de separação são melhores do que uma sequência de palavras quando se trata desses critérios. Outros idiomas têm valores diferentes. Por exemplo, o Python explica muitos operadores em inglês para que possam ser lidos naturalmente e fáceis de entender, mas essas mesmas propriedades podem dificultar bastante a compreensão de um pedaço do Python de relance.
fonte
A sintaxe para cada loop foi adicionada no Java 5. Você precisaria criar
in
uma palavra-chave de linguagem e adicionar palavras-chave a uma linguagem posteriormente é algo que você evita a todo custo, pois quebra o código existente - de repente todas as variáveis nomeadasin
causam uma análise erro.enum
foi ruim o suficiente a esse respeito.fonte
in
significaria introduzir uma nova palavra-chave, quebrando a compatibilidade com versões anteriores (System.in
alguém está?) Ou introduzir um conceito novinho em folha anteriormente desconhecido (palavras-chave contextuais). Tudo para que ganho?for(variable in expression)
nunca pode ser ambíguo com qualquer código legal, mesmo que "in" possa ser usado para variáveis. No entanto, uma fase separada do lexer é bastante comum em muitas cadeias de ferramentas do compilador. Isso tornaria impossível ou pelo menos muito mais difícil analisar o Java com alguns geradores de analisadores comuns. Manter a sintaxe de um idioma simples geralmente é bom para todos os envolvidos; nem todo mundo precisa de monstruosidades sintáticas como C ++ ou Perl.const
egoto
são palavras reservadas em Java, mas ainda não foram usadas.