Może nie jestem z tej planety, ale wydaje mi się, że następujący błąd powinien być błędem składni:
int a[] = {1,2,}; //extra comma in the end
Ale nie jest. Byłem zaskoczony, gdy ten kod został skompilowany w Visual Studio, ale nauczyłem się nie ufać kompilatorowi MSVC, jeśli chodzi o zasady C ++, więc sprawdziłem ten standard i jest on również dozwolony. Możesz zobaczyć 8.5.1 dla reguł gramatyki, jeśli mi nie wierzysz.
Dlaczego to jest dozwolone? To może być głupie bezużyteczne pytanie, ale chcę, żebyś zrozumiał, dlaczego pytam. Zrozumiałbym, gdyby był to pod-przypadek ogólnej reguły gramatycznej - postanowili nie utrudniać gramatyki ogólnej po prostu nie dopuszczać zbędnego przecinka na końcu listy inicjalizującej. Ale nie, dodatkowy przecinek jest wyraźnie dozwolony. Na przykład niedozwolone jest umieszczanie nadmiarowego przecinka na końcu listy argumentów wywołania funkcji (gdy funkcja przyjmuje ...
), co jest normalne .
Czy zatem istnieje jakiś szczególny powód, dla którego ta nadmiarowa przecinek jest wyraźnie dozwolona?
źródło
;
gdy jest jasne, że następny token jest tak naprawdę kolejnym stwierdzeniem.for()
na przykład po dodaniu), dodanie powoduje wygenerowanie ostrzeżenia kompilatora.if (x = 1)
nie jest dwuznaczny w gramatyce, ale jest bardzo niejednoznaczny dla ludzi, a zatem rzuca ostrzeżenie.if
przykład też nie jest niejednoznaczny. Nie uważam, że „dwuznaczny” oznacza to, co według ciebie oznacza!Odpowiedzi:
Ułatwia generowanie kodu źródłowego, a także pisanie kodu, który można łatwo rozszerzyć w późniejszym terminie. Zastanów się, co jest wymagane, aby dodać dodatkowy wpis do:
... musisz dodać przecinek do istniejącej linii i dodać nową linię. Porównaj to z przypadkiem, w którym trzy mają już przecinek, po prostu musisz dodać linię. Podobnie, jeśli chcesz usunąć linię, możesz to zrobić, nie martwiąc się o to, czy jest to ostatnia linia, czy nie, i możesz zmienić kolejność linii bez zbędnego wprowadzania przecinków. Zasadniczo oznacza to jednolitość w traktowaniu linii.
Pomyśl teraz o wygenerowaniu kodu. Coś w stylu (pseudo-kod):
Nie musisz się martwić, czy zapisywany element jest pierwszym, czy ostatnim. O wiele prostsze.
źródło
Jest to przydatne, jeśli robisz coś takiego:
źródło
var a = [1, 2,];
podobnie jak większość innych języków, które znam ... ActionScript, Python, PHP.undefined
.Myślę, że łatwość użycia dla programisty.
Dodatkowo, jeśli z jakiegokolwiek powodu masz narzędzie, które wygenerowało dla ciebie kod; narzędzie nie musi dbać o to, czy jest to ostatni element w inicjalizacji, czy nie.
źródło
Zawsze zakładałem, że ułatwia to dodanie dodatkowych elementów:
po prostu staje się:
w późniejszym terminie.
źródło
[1,2,3,]
Jest OK, ale{a:1, b:2, c:3,}
nie jest).Wszystko, co wszyscy mówią o łatwości dodawania / usuwania / generowania linii, jest poprawne, ale prawdziwym miejscem, w którym ta składnia się świeci, jest łączenie plików źródłowych. Wyobraź sobie, że masz tę tablicę:
Załóżmy, że sprawdziłeś ten kod w repozytorium.
Następnie twój kolega edytuje go, dodając do końca:
I jednocześnie edytujesz go, dodając na początku:
Semantycznie tego rodzaju operacje (dodawanie na początku, dodawanie na końcu) powinny być całkowicie bezpieczne, a twoje oprogramowanie do wersji (mam nadzieję, że git) powinno być w stanie zautomatyzować. Niestety tak nie jest, ponieważ twoja wersja nie ma przecinka po 9, a twój kumpel ma. Podczas gdy gdyby oryginalna wersja miała końcową liczbę 9, automatycznie by się pojawiły.
Zatem moja ogólna zasada brzmi: użyj przecinka końcowego, jeśli lista obejmuje wiele linii, nie używaj go, jeśli lista znajduje się w jednej linii.
źródło
Przecinek końcowy Uważam, że jest dozwolony ze względu na kompatybilność wsteczną. Istnieje wiele istniejących kodów, głównie generowanych automatycznie, co powoduje wstawienie przecinka. Ułatwia napisanie pętli bez specjalnych warunków na końcu. na przykład
Praktycznie nie ma żadnej przewagi dla programisty.
PS Chociaż w ten sposób łatwiej jest wygenerować kod automatycznie, zawsze starałem się nie wstawiać przecinka, wysiłki są minimalne, poprawia się czytelność, a to jest ważniejsze. Piszesz kod raz, czytasz go wiele razy.
źródło
int a = b + c +;
lubif(a && b &&);
łatwiej będzie po prostu skopiować i wkleić cokolwiek na końcu i łatwiej napisać generatory kodu. Ten problem jest zarówno trywialny, jak i subiektywny, w takich przypadkach zawsze dobrze jest robić to, co najlepsze dla czytnika kodów.&&
operatora, czasami robię takie operacje warunkowe,if (true \n && b1 \n && b2)
aby móc dodawać i usuwać wiersze według potrzeb.O ile mi wiadomo, jest to jeden z powodów, dla których automatyczne generowanie kodu powinno być proste; nie potrzebujesz żadnej specjalnej obsługi ostatniego elementu.
źródło
Ułatwia to generowanie kodów, które wypluwają tablice lub wyliczenia.
Wyobrażać sobie:
Tj. Nie ma potrzeby specjalnego obchodzenia się z pierwszym lub ostatnim elementem, aby uniknąć plucia końcowego przecinka.
Jeśli na przykład generator kodu jest napisany w języku Python, łatwo jest uniknąć plucia końcowego przecinka za pomocą
str.join()
funkcji:źródło
Dziwi mnie, że przez cały ten czas nikt nie zacytował Annotated C ++ Reference Manual ( ARM ), mówi on o [dcl.init] z naciskiem na mój:
chociaż gramatyka ewoluowała od czasu napisania ARM, pochodzenie pozostaje.
i możemy przejść do uzasadnienia C99, aby zobaczyć, dlaczego było to dozwolone w C, i mówi:
źródło
Widzę jeden przypadek użycia, który nie został wymieniony w innych odpowiedziach, nasze ulubione makra:
Dodanie makr do obsługi ostatniego
,
byłoby dużym bólem. Przy tej małej zmianie w składni zarządzanie tym jest trywialne. Jest to ważniejsze niż kod generowany maszynowo, ponieważ zwykle łatwiej jest to zrobić w pełnym języku Turinga niż w bardzo ograniczonym preprocesorze.źródło
Jedynym językiem, w którym - w praktyce * - jest niedozwolony, jest JavaScript i powoduje on niezliczoną ilość problemów. Na przykład, jeśli skopiujesz i wkleisz wiersz ze środka tablicy, wkleisz go na końcu i zapomnisz usunąć przecinek, Twoja witryna zostanie całkowicie zepsuta dla użytkowników IE.
* Teoretycznie jest to dozwolone, ale Internet Explorer nie przestrzega standardu i traktuje go jako błąd
źródło
var x = [,,,]
są legalne (z wyjątkiem IE <9, ale specyfikacja mówi, że jest legalne)Jest to łatwiejsze dla maszyn, tj. Parsowania i generowania kodu. Jest to również łatwiejsze dla ludzi, tj. Modyfikacja, komentowanie i wizualna elegancja dzięki spójności.
Zakładając C, czy napisałbyś następujące?
Nie. Nie tylko dlatego, że końcowe stwierdzenie jest błędem, ale także dlatego, że jest niespójne. Dlaczego więc to samo dotyczy kolekcji? Nawet w językach, w których można pominąć ostatnie średniki i przecinki, społeczność zwykle tego nie lubi. Na przykład społeczność Perla nie lubi pomijać średników, poza linijkami. Stosują to również do przecinków.
Nie pomijaj przecinków w zbiorach wielowierszowych z tego samego powodu, dla którego nie odrzucasz średników dla wielowierszowych bloków kodu. To znaczy, nie zrobiłbyś tego, nawet gdyby język na to pozwalał, prawda? Dobrze?
źródło
Powód jest trywialny: łatwość dodawania / usuwania linii.
Wyobraź sobie następujący kod:
Teraz możesz łatwo dodawać / usuwać elementy do listy bez konieczności dodawania / usuwania końcowego przecinka.
W przeciwieństwie do innych odpowiedzi, tak naprawdę nie uważam, że łatwość generowania listy jest ważnym powodem: w końcu kod zajmuje specjalne miejsce w ostatniej (lub pierwszej) linii. Generatory kodu są pisane raz i używane wiele razy.
źródło
Pozwala każdej linii na przyjęcie tego samego formularza. Po pierwsze, ułatwia to dodawanie nowych wierszy i pozwala systemowi kontroli wersji w znaczący sposób śledzić zmianę, a także umożliwia łatwiejszą analizę kodu. Nie mogę wymyślić przyczyny technicznej.
źródło
Pozwala to zabezpieczyć się przed błędami powodowanymi przez przenoszenie elementów na długiej liście.
Załóżmy na przykład, że mamy kod wyglądający tak.
I jest świetny, ponieważ pokazuje oryginalną trylogię witryn Stack Exchange.
Ale jest z tym jeden problem. Widzisz, stopka na tej stronie pokazuje awarię serwera przed superużytkownikiem. Lepiej to napraw, zanim ktokolwiek zauważy.
W końcu przesuwanie linii nie może być takie trudne, prawda?
Wiem, że nie ma witryny o nazwie „Server FaultSuper User”, ale nasz kompilator twierdzi, że istnieje. Problem polega na tym, że C ma funkcję konkatenacji łańcuchów, która pozwala napisać dwa łańcuchy z podwójnym cudzysłowem i połączyć je przy użyciu niczego (podobny problem może się również zdarzyć z liczbami całkowitymi, ponieważ
-
znak ma wiele znaczeń).A co jeśli oryginalna tablica miała na końcu bezużyteczny przecinek? Cóż, linie byłyby przesuwane, ale taki błąd nie miałby miejsca. Łatwo przeoczyć coś tak małego jak przecinek. Jeśli pamiętasz, aby po każdym elemencie wstawić przecinek, taki błąd po prostu nie może się zdarzyć. Nie chcesz tracić czterech godzin na debugowanie czegoś, dopóki nie znajdziesz przecinka, który jest przyczyną twoich problemów .
źródło
Podobnie jak wiele innych, przecinek końcowy w inicjalizatorze tablicowym jest jedną z rzeczy odziedziczonych przez C ++ po C (i będzie musiał być obsługiwany na zawsze). Pogląd całkowicie odmienny od przedstawionego tutaj jest wspomniany w książce „Deep C Secrets” .
Oto przykład z kilkoma „paradoksami przecinków”:
czytamy :
... dla mnie ma to większy sens
źródło
enum
sprawie jest dość interesujący, ponieważ w tym przypadku brakujący przecinek stanowiłby najmniejszą dwuznaczność. Biorąc pod uwagęstruct foo arr[] = {{1,2,3,4,5}, {3,4,5,6,7}, }
; język ma dwa sensowne znaczenia: utwórz tablicę dwuelementową lub stwórz tablicę trzyelementową, w której ostatni element ma wartości domyślne. Gdyby C przyjął późniejszą interpretację, widziałbym zakazanieenum foo {moe, larry, curly, };
na zasadzie, że powinien istnieć tylko jeden sposób na napisanie oświadczenia (bez przecinka), ale ...enum foo {moe,,larry,curly,};
jako pomijanie liczby pomiędzy,moe
ilarry
generalnie nie ma znaczenia, czy przecinek końcowy został przetworzony czy zignorowany. Jedyny przypadek, w którym mogłoby mieć znaczenie, to gdyby ostatnia pozycja była maksymalną wartością dla zadeklarowanego typu i że ...Oprócz łatwości generowania i edycji kodu, jeśli chcesz zaimplementować analizator składni, ten typ gramatyki jest prostszy i łatwiejszy do wdrożenia. C # przestrzega tej zasady w kilku miejscach, w których znajduje się lista elementów oddzielonych przecinkami, takich jak elementy w
enum
definicji.źródło
Ułatwia generowanie kodu, ponieważ wystarczy dodać tylko jeden wiersz i nie trzeba traktować dodawania ostatniego wpisu, jakby to był szczególny przypadek. Jest to szczególnie prawdziwe w przypadku używania makr do generowania kodu. Próbuje się wyeliminować potrzebę używania makr z danego języka, ale wiele języków ewoluowało równolegle z dostępnymi makrami. Dodatkowy przecinek umożliwia zdefiniowanie i użycie makr, takich jak następujące:
Stosowanie:
To bardzo uproszczony przykład, ale często ten wzór jest używany przez makra do definiowania takich rzeczy, jak mapy i tabele wysyłki, wiadomości, zdarzenia lub tłumaczenia. Jeśli przecinek nie byłby dozwolony na końcu, potrzebowalibyśmy specjalnego:
i byłoby to bardzo niewygodne w użyciu.
źródło
Tak więc, gdy dwie osoby dodają nowy element do listy w oddzielnych gałęziach, Git może poprawnie scalić zmiany, ponieważ Git działa w trybie liniowym.
źródło
Jeśli użyjesz tablicy bez określonej długości, VC ++ 6.0 może automatycznie zidentyfikować jej długość, więc jeśli użyjesz „int a [] = {1,2,};” długość a wynosi 3, ale ostatnia nie ma „ t został zainicjowany, możesz użyć „cout <
źródło