Z przewodnika po języku Java 5 :
Gdy zobaczysz dwukropek (:), przeczytaj go jako „in”.
Dlaczego więc nie użyć in
?
Martwi mnie to od lat. Ponieważ jest to niezgodne z resztą języka. Na przykład w Javie istnieją implements
, extends
, super
dla stosunków między rodzajami zamiast symboli, takich jak C ++, Scala czy Ruby.
W Javie dwukropek używany w 5 kontekstach . Trzy z nich są odziedziczone po C. A pozostałe dwa zostały poparte przez Jozuego Blocha. Tak przynajmniej powiedział podczas rozmowy „Kontrowersje dotyczące zamknięcia” . Pojawia się to, gdy krytykuje użycie dwukropka do mapowania jako niespójne dla każdej semantyki. Co wydaje mi się dziwne, ponieważ jest to nadużywane oczekiwane wzorce. Jak list_name/category: elements
lub laberl/term: meaning
.
Szperałem w jcp i jsr, ale nie znalazłem żadnych znaków na liście mailingowej. Google nie znalazł dyskusji na ten temat. Tylko początkujący pomyleni znaczeniem jelita grubego w for
.
Główne argumenty przeciwko in
przedstawione dotychczas:
- wymaga nowego słowa kluczowego; i
- komplikuje leksykację.
Spójrzmy na odpowiednie definicje gramatyczne :
komunikat : instrukcja „for” („forControl”) | ... ; forControl : ulepszoneForControl | forInit? „;” wyrażenie? „;” forUpdate? ; ulepszoneForControl : variableModifier * type variableDeclaratorId ':' wyrażenie ;
Zmień z, :
aby in
nie przynosić dodatkowej złożoności lub wymaga nowego słowa kluczowego.
Odpowiedzi:
Normalne parsery, jak się zwykle uczy, mają etap leksykalny, zanim parser dotknie wejścia. Lexer (także „skaner” lub „tokenizer”) dzieli dane wejściowe na małe tokeny opatrzone adnotacjami typu. Pozwala to głównemu parserowi używać tokenów jako elementów terminalu, zamiast traktować każdy znak jako terminal, co prowadzi do zauważalnego wzrostu wydajności. W szczególności leksykon może również usunąć wszystkie komentarze i białe znaki. Jednak oddzielna faza tokenizera oznacza, że słowa kluczowe nie mogą być również używane jako identyfikatory (chyba że język obsługuje stropowanie, które nieco popadło w niełaskę, lub poprzedza wszystkie identyfikatory znakiem podobnym do sigil
$foo
).Dlaczego? Załóżmy, że mamy prosty tokenizer, który rozumie następujące tokeny:
Tokenizer zawsze będzie pasował do najdłuższego tokena i woli słowa kluczowe niż identyfikatory.
interesting
Będzie więc leksykalny jakoIDENT:interesting
, alein
będzie leksykalny jakoIN
, nigdy jakoIDENT:interesting
. Fragment kodu podobny dozostaną przetłumaczone na strumień tokenu
Jak dotąd to działa. Ale każda zmienna
in
byłaby leksykowana jako słowo kluczowe,IN
a nie zmienna, która złamałaby kod. Lexer nie utrzymuje żadnego stanu między tokenami i nie może wiedzieć, żein
zwykle powinna to być zmienna, z wyjątkiem sytuacji, gdy jesteśmy w pętli for. Ponadto następujący kod powinien być legalny:Pierwszy
in
byłby identyfikatorem, drugi byłby słowem kluczowym.Istnieją dwie reakcje na ten problem:
Kontekstowe słowa kluczowe są mylące, zamiast tego użyjmy słów kluczowych.
Java ma wiele zastrzeżonych słów, z których niektóre nie mają żadnego zastosowania poza dostarczaniem bardziej pomocnych komunikatów o błędach programistom przechodzącym na Javę z C ++. Dodanie nowych słów kluczowych powoduje uszkodzenie kodu. Dodanie kontekstowych słów kluczowych jest mylące dla czytelnika kodu, chyba że mają dobre wyróżnianie składni i utrudniają implementację narzędzi, ponieważ będą musieli użyć bardziej zaawansowanych technik analizy (patrz poniżej).
Gdy chcemy rozszerzyć język, jedynym rozsądnym podejściem jest użycie symboli, które wcześniej nie były legalne w tym języku. W szczególności nie mogą to być identyfikatory. Dzięki składni pętli foreach Java ponownie wykorzystała istniejące
:
słowo kluczowe z nowym znaczeniem. Do lambdas Java dodało->
słowo kluczowe, które wcześniej nie mogło występować w żadnym legalnym programie (-->
nadal byłoby leksykowane jako'--' '>'
zgodne z prawem, i->
mogło być wcześniej leksykowane jako'-', '>'
, ale ta sekwencja zostałaby odrzucona przez parser).Kontekstowe słowa kluczowe upraszczają języki, zaimplementujmy je
Lexery są bezdyskusyjnie przydatne. Ale zamiast uruchamiania leksera przed analizatorem składni, możemy uruchamiać je razem z analizatorem składni. Parsery oddolne zawsze znają zestaw typów tokenów, który byłby akceptowalny w danym miejscu. Analizator składni może następnie poprosić leksera o dopasowanie dowolnego z tych typów w bieżącej pozycji. W pętli dla każdego analizator składni byłby w pozycji oznaczonej przez
·
(uproszczoną) gramatykę po znalezieniu zmiennej:W tej pozycji legalne żetony są
SEMICOLON
lubIN
nie sąIDENT
. Słowo kluczowein
byłoby całkowicie jednoznaczne.W tym konkretnym przykładzie parsery odgórne również nie miałyby problemu, ponieważ możemy przepisać powyższą gramatykę na
i wszystkie żetony niezbędne do podjęcia decyzji można zobaczyć bez cofania się.
Rozważ użyteczność
Java zawsze dążyła do semantycznej i syntaktycznej prostoty. Na przykład język nie obsługuje przeciążania operatora, ponieważ znacznie skomplikowałby kod. Więc przy podejmowaniu decyzji pomiędzy
in
i:
dla każdej pętli FOR-składni, musimy zastanowić się, które jest mniej skomplikowany i bardziej widoczne dla użytkowników. Prawdopodobnie byłby to skrajny przypadek(Uwaga: Java ma osobne przestrzenie nazw dla nazw typów, zmiennych i metod. Myślę, że to głównie pomyłka. To nie znaczy, że późniejszy projekt języka musi dodać więcej błędów.)
Która alternatywa zapewnia wyraźniejsze wizualne rozdzielenie między zmienną iteracyjną a iterowaną kolekcją? Którą alternatywę można rozpoznać szybciej, gdy spojrzysz na kod? Przekonałem się, że symbole rozdzielające są lepsze niż ciąg słów, jeśli chodzi o te kryteria. Inne języki mają różne wartości. Np. Python określa wiele operatorów w języku angielskim, aby można je było czytać w sposób naturalny i są łatwe do zrozumienia, ale te same właściwości mogą utrudnić zrozumienie fragmentu Pythona na pierwszy rzut oka.
źródło
Składnia pętli for-each została dodana w Javie 5. Trzeba było utworzyć
in
słowo kluczowe języka, a później dodawanie słów kluczowych do języka jest czymś, czego unika się za wszelką cenę, ponieważ psuje on istniejący kod - nagle wszystkie nazwane zmiennein
powodują parsowanie błąd.enum
był wystarczająco zły pod tym względem.źródło
in
oznaczałoby albo wprowadzenie nowego słowa kluczowego, a tym samym zerwanie z kompatybilnością wsteczną (System.in
ktoś?) Lub wprowadzenie nieznanej wcześniej zupełnie nowej koncepcji (kontekstowe słowa kluczowe). Wszystko za jaki zysk?for(variable in expression)
nigdy nie może być niejednoznaczne z żadnym kodem prawnym, nawet jeśli „in” może być użyte dla zmiennych. Jednak osobna faza leksykalna jest dość powszechna w wielu łańcuchach narzędzi kompilatora. Uniemożliwiłoby to lub przynajmniej znacznie trudniej parsowało Javę za pomocą niektórych popularnych generatorów parsera. Utrzymanie prostej składni języka jest zwykle dobre dla wszystkich zaangażowanych; nie wszyscy potrzebują potworności składniowych, takich jak C ++ lub Perl.const
igoto
oba są zastrzeżonymi słowami w Javie, ale nie są jeszcze używane.