Dlaczego nazwy zmiennych nie mogą zaczynać się od cyfr?

136

Pracowałem z nowym programistą C ++ jakiś czas temu, kiedy zadał pytanie: „Dlaczego nazwy zmiennych nie mogą zaczynać się od cyfr?”

Nie mogłem wymyślić odpowiedzi, poza tym, że niektóre liczby mogą zawierać tekst (123456L, 123456U) i nie byłoby to możliwe, gdyby kompilatorzy myśleli, że wszystko z pewną ilością znaków alfa jest nazwą zmiennej.

Czy to była właściwa odpowiedź? Czy jest więcej powodów?

string 2BeOrNot2Be = "that is the question"; // Why won't this compile?
Jeremiasz
źródło
15
A dlaczego nie mogą mieć w sobie spacji?
Tim
4
Ten problem jest starszy od C ++ o co najmniej 20 lat, jeśli nie z powrotem do pierwszych asemblerów makr.
Ken Gentle
2
Cóż, w FORTH możesz to zrobić. AFAIK, istnieje słowo o nazwie, 0które odkłada 0 na stos. inny to 0=sprawdzanie, czy na stosie jest 0.
Ingo,
12
Dlaczego to pytanie jest tak popularne, a odpowiedzi tak błędne? W wielu językach zmienne mogą zaczynać się od liczb. C ++ nie, ale jest po prostu wygodnym ograniczeniem, które pozwala uniknąć pewnych niejednoznaczności. Czasami TAK zadziwia mnie na wszystkie złe sposoby.
david.pfx
5
Jeśli to pytanie zostało zadane dzisiaj na SO, zostanie określone jako oparte na opinii i zamknięte. Dzięki za pytanie.
Boon

Odpowiedzi:

116

Ponieważ wtedy ciąg cyfr byłby zarówno prawidłowym identyfikatorem, jak i prawidłową liczbą.

int 17 = 497;
int 42 = 6 * 9;
String 1111 = "Totally text";
skiphoppy
źródło
37
A co jeśli powiedzieli, że zmiennymi nie mogą być tylko liczby. Więc co?
Pirolistyczne
6
Dłużej zajęłoby mi wymyślenie wyrażenia regularnego dla leksera, który pobrałby identyfikatory przy użyciu tej reguły, jeśli jest to w ogóle możliwe, więc widzę, dlaczego żaden język nie został nigdy zaimplementowany w ten sposób, oprócz powodów podanych w inne odpowiedzi.
skiphoppy
39
Gdyby miały to być liczby + alfa, nadal można by zrobić String 0x123 = "Hello World". Chyba że stwierdzisz, że nazwy zmiennych są „liczbami + alfą, które nie są analizowane do prawidłowego oznaczenia liczbowego”, a to po prostu głupie.
eaolson
4
Mniejsza o kompilator: osoby używające tego języka muszą być w stanie łatwo (na pierwszy rzut oka) odróżnić nazwy zmiennych od liczb. Gdyby pierwszy znak ci nie powiedział - zamiast tego, gdybyś musiał przeszukać resztę słowa, aby stwierdzić, czy gdzieś tam jest nienumeryczna alfa - kod byłby trudniejszy do odczytania.
nadchodząca burza
10
@eaolson: Pracowałem z asemblerem, który zastosował tę regułę do liczb szesnastkowych zaczynających się od A- Fi kończących się h. Zaskoczyło mnie, gdy po raz pierwszy próbowałem zdefiniować wytwórnię, która wskazywałaby na dane muzyczne dla Two Part Invention # 13 Bacha (nazwa logiczna? Bach).
supercat
116

Dobrze pomyśl o tym:

int 2d = 42;
double a = 2d;

Co to jest? 2.0? czy 42?

Podpowiedź, jeśli tego nie rozumiesz, d po liczbie oznacza liczbę poprzedzającą podwójny literał

Pirolistyczne
źródło
11
W rzeczywistości jest to [stosunkowo] późna notacja („d” od „double”), standard IIRC C89. Wiodące liczby w identyfikatorach nie są możliwe, jeśli ta konstrukcja jest w języku, ale nie jest to powód, dla którego liczby nie mogą rozpocząć identyfikatora.
Ken Gentle
1
dnie jest poprawnym zmiennym sufiksem literału w C ++. Pływające literały są domyślnie dublowane, możesz użyć flub, ljeśli potrzebujesz liczby zmiennoprzecinkowej lub długiego podwójnego literału.
CB Bailey
1
Dotyczy to Javy i chociaż pierwotne pytanie dotyczyło C ++, odnosi się również do wielu innych języków, takich jak Java. Ale ja się zgadzam. To nie jest oryginalny powód, dla którego identyfikatory nie mogą zaczynać się od cyfr.
Pyrolistic
50

Teraz jest to konwencja, ale zaczęła się jako wymóg techniczny.

W dawnych czasach parsery języków takich jak FORTRAN czy BASIC nie wymagały użycia spacji. Zasadniczo następujące są identyczne:

10 V1=100
20 PRINT V1

i

10V1=100
20PRINTV1

Załóżmy teraz, że przedrostki liczbowe są dozwolone. Jak byś to zinterpretował?

101V=100

tak jak

10 1V = 100

lub jako

101 V = 100

lub jako

1 01V = 100

Więc to zostało uznane za nielegalne.

Roy Dictus
źródło
1
Mniejsza nitka: numery wierszy musiały znajdować się w kolumnach 1-6, a kod wykonywalny za kolumną 8. Z drugiej strony DO 10 I=1,50można by je analizować niejednoznacznie jako DO1 0I=1,50[nawiasem mówiąc, jeśli użyje się kropki zamiast przecinka, instrukcja staje się przypisaniem do zmienna zmiennoprzecinkowa o nazwie DO10I.
supercat
Ciekawe wyjaśnienie! To ma sens w przypadku starszych języków, wciąż zastanawiam się, dlaczego nadal wybieramy projekt dla języków takich jak Python, JavaScript lub R.
Charles Clayton
Zdecydowanie pamiętam to z BASICem i czuję, że jest to prawdopodobnie najważniejszy praktyczny powód praktyki. Jednak z technicznego punktu widzenia niewyraźnie pamiętam, że może to faktycznie powrócić do wczesnego języka asemblera. Nie jestem jednak pewien, który asembler jest, i bardzo dobrze mogę się mylić.
Brian Chandler
42

Ponieważ w analizie leksykalnej podczas kompilacji unika się cofania. Zmienna taka jak:

Apple;

kompilator będzie wiedział, że jest to identyfikator od razu, gdy spotka się z literą „A”.

Jednak zmienna taka jak:

123apple;

Kompilator nie będzie w stanie zdecydować, czy jest to liczba czy identyfikator, dopóki nie trafi w „a”, w związku z czym wymaga cofnięcia.

Jiayang
źródło
2
Odpowiadając, przypominając sobie moją klasę projektów kompilatora, odpowiedź jest prosta! Kudos
nehem
15

Kompilatory / parsery / analizatory leksykalne były dla mnie dawno, dawno temu, ale wydaje mi się, że pamiętam trudności w jednoznacznym ustaleniu, czy znak numeryczny w jednostce kompilacji reprezentuje literał, czy identyfikator.

Języki, w których przestrzeń jest niewielka (jak ALGOL i oryginalny FORTRAN, jeśli dobrze pamiętam) nie mogły z tego powodu akceptować liczb rozpoczynających identyfikatory.

To sięga daleko wstecz - przed specjalnymi zapisami oznaczającymi pamięć lub bazę numeryczną.

Ken Gentle
źródło
9

Zgadzam się, że byłoby przydatne, gdyby identyfikatory zaczynały się od cyfry. Jedna lub dwie osoby wspomniały, że możesz obejść to ograniczenie, dodając podkreślenie do swojego identyfikatora, ale to naprawdę brzydkie.

Myślę, że część problemu wynika z literałów liczbowych, takich jak 0xdeadbeef, które utrudniają wymyślenie łatwych do zapamiętania reguł dla identyfikatorów, które mogą zaczynać się od cyfry. Jednym ze sposobów może być zezwolenie na wszystko pasujące do [A-Za-z _] +, co NIE jest słowem kluczowym ani literałem liczbowym. Problem polega na tym, że doprowadziłoby to do dopuszczenia dziwnych rzeczy, takich jak 0xdeadpork, ale nie 0xdeadbeef. Ostatecznie uważam, że powinniśmy być fair w stosunku do wszystkich mięs: P.

Pamiętam, że kiedy po raz pierwszy uczyłem się C, czułem, że reguły nazw zmiennych są arbitralne i restrykcyjne. Co najgorsze, były trudne do zapamiętania, więc zrezygnowałem z ich uczenia się. Po prostu zrobiłem to, co uważałem za właściwe i zadziałało całkiem nieźle. Teraz, kiedy nauczyłem się dużo więcej, nie wydaje się to takie złe i w końcu udało mi się nauczyć tego dobrze.

allyourcode
źródło
8
LOL - „Problem polega na tym, że doprowadziłoby to do dopuszczenia 0xdeadpork, ale nie 0xdeadbeef. Ostatecznie myślę, że powinniśmy być fair w stosunku do wszystkich mięs: P.”
mr-euro
6

Prawdopodobnie jest to decyzja podjęta z kilku powodów, kiedy analizujesz token, wystarczy spojrzeć na pierwszy znak, aby ustalić, czy jest to identyfikator, czy literał, a następnie wysłać go do odpowiedniej funkcji w celu przetworzenia. Więc to jest optymalizacja wydajności.

Inną opcją byłoby sprawdzenie, czy nie jest to literał, i pozostawienie domeny identyfikatorów jako wszechświat minus literały. Ale aby to zrobić, musiałbyś zbadać każdy znak każdego tokena, aby wiedzieć, jak go sklasyfikować.

Istnieją również implikacje stylistyczne, które mają być mnemonikami, więc słowa są znacznie łatwiejsze do zapamiętania niż liczby. Kiedy pisano wiele oryginalnych języków, ustawiając style na kilka następnych dziesięcioleci, nie myśleli o zastąpieniu „2” przez „do”.

William
źródło
6

Nazwy zmiennych nie mogą zaczynać się od cyfry, ponieważ może to powodować pewne problemy, jak poniżej:

int a = 2;
int 2 = 5;
int c = 2 * a; 

jaka jest wartość c? to 4 lub 10!

inny przykład:

float 5 = 25;
float b = 5.5;

czy pierwsza 5 jest liczbą lub jest obiektem (. operatorem). Podobny problem występuje z drugą 5.

Może są inne powody. Dlatego nie powinniśmy używać żadnej cyfry na początku nazwy zmiennej.

sbagdat
źródło
Nawet jeśli wymagałoby się, aby identyfikatory zawierały co najmniej jeden znak niebędący cyfrą, należałoby również wymagać, aby formaty numeryczne zawierające litery również zawierały znak niealfanumeryczny [np. Wymagałoby zapisu 0x1234 jako 1234 USD i 1E6 jak 1.E6 lub 1.0E6] lub mają dziwną kombinację legalnych i niedozwolonych nazw identyfikatorów.
supercat
4

Użycie cyfry na początku nazwy zmiennej znacznie komplikuje sprawdzanie błędów podczas kompilacji lub interpertacji.

Zezwolenie na używanie nazw zmiennych, które zaczynały się jak liczba, prawdopodobnie spowodowałoby ogromne problemy dla projektantów języków. Podczas analizowania kodu źródłowego, za każdym razem, gdy kompilator / interpreter napotkał token zaczynający się od cyfry, na której oczekiwano nazwy zmiennej, musiałby przeszukać ogromny, skomplikowany zestaw reguł, aby określić, czy token jest naprawdę zmienną, czy też błędem . Dodatkowa złożoność dodana do parsera języka może nie uzasadniać tej funkcji.

Odkąd pamiętam (około 40 lat), nie sądzę, żebym kiedykolwiek używał języka, który pozwalałby na użycie cyfry jako początku nazw zmiennych. Jestem pewien, że zostało to zrobione przynajmniej raz. Może ktoś tutaj rzeczywiście gdzieś to widział.

mkClark
źródło
1
To nie jest takie trudne. To sprawia, że ​​faza leksykalna jest trudniejsza, to wszystko. Oczywiście, kiedy brałem kompilatory, powiedziano mi, że skanowanie leksykalne może zająć ponad jedną czwartą całkowitego czasu kompilacji.
David Thornley
4

Jak zauważyło kilka osób, istnieje wiele historycznego bagażu dotyczącego prawidłowych formatów nazw zmiennych. A projektanci języków zawsze są pod wpływem tego, co wiedzą, kiedy tworzą nowe języki.

To powiedziawszy, prawie cały czas język nie pozwala na rozpoczynanie nazw zmiennych liczbami, ponieważ takie są zasady projektowania języka. Często dzieje się tak, ponieważ taka prosta reguła znacznie ułatwia analizowanie i leksowanie języka. Jednak nie wszyscy projektanci języków wiedzą, że to jest prawdziwy powód. Pomagają w tym nowoczesne narzędzia leksykalne, ponieważ jeśli spróbujesz zdefiniować je jako dopuszczalne, dadzą ci konflikty analizy.

OTOH, jeśli twój język ma unikalny, rozpoznawalny znak, który zapowiada nazwy zmiennych, można ustawić je tak, aby zaczynały się liczbą. Podobne odmiany reguł mogą być również używane, aby zezwolić na spacje w nazwach zmiennych. Ale wynikowy język prawdopodobnie nie będzie bardzo przypominał żadnego popularnego języka konwencjonalnego, jeśli w ogóle.

Aby zapoznać się z przykładem dość prostego języka tworzenia szablonów HTML, który zezwala na rozpoczynanie zmiennych od liczb i ma osadzone spacje, spójrz na Qompose .

staticsan
źródło
1
W rzeczywistości istnieje kilka języków, w których można używać znaków do oznaczania identyfikatorów. Nazywają się „sigils” i masz je w Perlu i PHP.
Jason Baker
Tyle że nadal nie możesz zaczynać nazwy zmiennej w PHP od liczby - zabraniają tego reguły językowe. :-) Ale możesz w Qompose z dokładnie tego samego powodu.
staticsan
4

Ponieważ jeśli pozwolisz, aby słowo kluczowe i identyfikator zaczynały się od znaków numerycznych, lekser (część kompilatora) nie byłby w stanie łatwo odróżnić początku literału numerycznego od słowa kluczowego bez znacznie bardziej skomplikowanego (i wolniejszego).

Nicholas Carey
źródło
2
Proces leksowania rzadko jest wąskim gardłem. Jasne, to sprawia, że ​​wyrażenie regularne dla tokenów identyfikacyjnych jest bardziej złożone, ale nadal mogą być superszybkimi DFA. Ich czas wykonania jest o wiele mniejszy w porównaniu z większością innych zadań, które kompilatorzy muszą wykonać.
4

Ograniczenie jest arbitralne. Różne Lispy pozwalają, aby nazwy symboli zaczynały się od cyfr.

Kyle Jones
źródło
4

W języku COBOL zmienne mogą zaczynać się od cyfry.

ćwiek
źródło
2

C ++ nie może tego mieć, ponieważ projektanci języka uczynili z tego regułę. Gdybyś miał stworzyć swój własny język, z pewnością mógłbyś na to pozwolić, ale prawdopodobnie napotkałbyś te same problemy, co oni i zdecydowałby się na to nie zezwalać. Przykłady nazw zmiennych, które mogą powodować problemy:

0x, 2d, 5555

Kevin
źródło
To ograniczenie obowiązuje w językach, w których tego rodzaju składnia nie jest dozwolona.
Jason Baker
2

Jednym z kluczowych problemów związanych z rozluźnieniem konwencji składniowych jest wprowadzenie dysonansu poznawczego do procesu kodowania. Na sposób, w jaki myślisz o swoim kodzie, może mieć duży wpływ brak jasności, który to wprowadził.

Czy to nie Dykstra powiedział, że „najważniejszym aspektem każdego narzędzia jest jego wpływ na użytkownika”?

speleologia
źródło
1

Prawdopodobnie dlatego, że człowiekowi łatwiej jest stwierdzić, czy jest to liczba, czy identyfikator, a także tradycja. Posiadanie identyfikatorów, które mogłyby zaczynać się od cyfry, nie skomplikowałoby tak bardzo skanowania leksykalnego.

Nie wszystkie języki mają zabronione identyfikatory zaczynające się od cyfry. We Forth mogą to być liczby, a małe liczby całkowite były zwykle definiowane jako słowa Forth (zasadniczo identyfikatory), ponieważ szybsze było odczytanie „2” jako procedury umieszczania 2 na stosie niż rozpoznawania „2” jako liczby którego wartość wynosiła 2. (Podczas przetwarzania danych wejściowych z programatora lub bloku dyskowego, system Forth podzieliłby dane wejściowe według spacji. Próbowałby przeszukać token w słowniku, aby sprawdzić, czy jest to zdefiniowane słowo, i jeśli nie, spróbowałby przetłumaczyć to na liczbę, a jeśli nie, oznaczałoby błąd.)

David Thornley
źródło
Rzecz w tym, że Forth tak naprawdę nie ma bardzo wyrafinowanego parsera. Naprawdę, wszystko, o co chodzi, to to, czy identyfikator znajduje się między dwoma zestawami białych znaków.
Jason Baker
1

Załóżmy, że pozwoliłeś, aby nazwy symboli zaczynały się od cyfr. Teraz przypuśćmy, że chcesz nazwać zmienną 12345foobar. Jak odróżniłbyś to od 12345? W rzeczywistości nie jest to strasznie trudne do zrobienia z wyrażeniem regularnym. W rzeczywistości problemem jest wydajność. Naprawdę nie potrafię wyjaśnić, dlaczego jest to tak szczegółowe, ale zasadniczo sprowadza się to do faktu, że odróżnienie 12345foobar od 12345 wymaga wycofania. To sprawia, że ​​wyrażenie regularne jest niedeterministyczne.

Jest to znacznie lepsze wyjaśnienie tego tutaj .

Jason Baker
źródło
1
Jak można zaprojektować wyrażenie regularne, aby zezwalać na zmienną nazwaną ifqlub, doublezale nie iflub double? Podstawowym problemem związanym z zezwalaniem na rozpoczynanie identyfikatorów od cyfr byłoby to, że istnieją formy literałów szesnastkowych i liczb zmiennoprzecinkowych, które składają się wyłącznie ze znaków alfanumerycznych (języki używałyby czegoś takiego jak 1234 $ lub h'1234 zamiast 0x1234 i wymagałyby liczb takich jak 1E23, aby uwzględnić kropkę, można uniknąć tego problemu). Zauważ, że próby analizy regex-parsingu C mogą już zostać przerwane przez takie rzeczy jak 0x12E+5.
supercat
1

kompilatorowi łatwo jest zidentyfikować zmienną za pomocą ASCII zamiast numeru.

Vivek
źródło
1

Kompilator ma 7 faz w następujący sposób:

  1. Analiza leksykalna
  2. Analiza składni
  3. Analiza semantyczna
  4. Generowanie kodu pośredniego
  5. Optymalizacja kodu
  6. Generowanie kodu
  7. Tabela symboli

W fazie analizy leksykalnej podczas kompilowania fragmentu kodu unika się cofania. W przypadku zmiennej, takiej jak Apple, kompilator od razu pozna swój identyfikator, gdy napotka literę „A” w fazie analizy leksykalnej. Jednak w przypadku zmiennej, takiej jak 123apple, kompilator nie będzie w stanie zdecydować, czy jest to liczba, czy identyfikator, dopóki nie trafi w „a” i musi przejść wstecz, aby przejść do fazy analizy leksykalnej w celu zidentyfikowania, że ​​jest to zmienna. Ale nie jest obsługiwany w kompilatorze.

Podczas analizowania tokenu wystarczy spojrzeć na pierwszy znak, aby określić, czy jest to identyfikator, czy literał, a następnie wysłać go do odpowiedniej funkcji w celu przetworzenia. Więc to jest optymalizacja wydajności.

Harikesh
źródło
0

Myślę, że prosta odpowiedź jest taka, że ​​tak, ograniczenie jest oparte na języku. W C ++ i wielu innych nie może, ponieważ język tego nie obsługuje. Nie jest to wbudowane w zasady, aby to umożliwić.

Pytanie to jest podobne do pytania, dlaczego król nie może poruszać się o cztery pola naraz w szachach? To dlatego, że w szachach jest to nielegalne posunięcie. Czy to na pewno w innej grze. Zależy to tylko od reguł, którymi się kierują.

kemiller2002
źródło
Tyle że C ++ został wymyślony niedawno przez ludzi, którzy wciąż żyją. Możemy ich zapytać, dlaczego wybrali to, co zrobili, i odrzucili alternatywy. To samo nie dotyczy szachów.
Steve Jessop
Ale nie o to mi chodzi. Jest to analogia do tego, dlaczego na początku nazw zmiennych nie mogą występować liczby, a najprostsza odpowiedź brzmi, ponieważ reguły języka na to nie pozwalają.
kemiller2002
Jasne, ale nie sądzę, żeby pytający był imbecylem. Prawdopodobnie sam już tak daleko wyszedł. Pytanie IMO brzmi: „dlaczego reguły języka na to nie pozwalają?”. Chce wypełnić lukę między znajomością zasad a ich zrozumieniem.
Steve Jessop
Tak, zastanawiając się nad tym, zdałem sobie sprawę, dokąd się wybierasz. Masz rację. Wydaje mi się, że trochę swobodnie stosowałem brzytwę Ockhama i założyłem, że nie ma prawdziwej odpowiedzi na pytanie dlaczego, poza tym, że zmienne nie zaczynają się od liczb, ponieważ nie ma liczb.
kemiller2002,
Nie mówię, że się mylisz, uważaj, czasami decyzje ciał zajmujących się normami C ++ przekraczają śmiertelne zrozumienie i kończy się na tym, że „ponieważ oni musieli coś zdecydować i zdecydowali tak”. Ale jest tam przynajmniej jedno pytanie :-)
Steve Jessop,
0

Pierwotnie było to po prostu dlatego, że łatwiej było zapamiętać (możesz nadać mu więcej znaczenia) nazwy zmiennych jako łańcuchy zamiast liczb, chociaż liczby mogą być zawarte w ciągu, aby zwiększyć znaczenie ciągu lub pozwolić na użycie tej samej nazwy zmiennej, ale wyznaczyć jako posiadające odrębne, ale bliskie znaczenie lub kontekst. Na przykład pętla 1, pętla 2 itd. Zawsze informowałyby cię, że jesteś w pętli i / lub pętla 2 była pętlą w pętli1. Którą wolisz (ma większe znaczenie) jako zmienną: adres czy 1121298? Który jest łatwiejszy do zapamiętania? Jeśli jednak język używa czegoś do oznaczenia, że ​​nie jest to tylko tekst lub liczby (takie jak $ w adresie $), to naprawdę nie powinno to robić różnicy, ponieważ powiedziałoby to kompilatorowi, że to, co następuje, ma być traktowane jako zmienna ( w tym przypadku).

cjtech
źródło
0

Zmienna może być traktowana jako wartość również w czasie kompilacji przez kompilator, więc wartość może wywoływać wartość ponownie i ponownie rekurencyjnie

aravinth
źródło
0

W fazie analizy leksykalnej podczas kompilowania fragmentu kodu unika się cofania . Zmienna taka jak Apple; , kompilator będzie znać swój identyfikator od razu, gdy napotka literę „A” w fazie analizy leksykalnej. Jednak zmienna taka jak 123apple; , kompilator nie będzie w stanie zdecydować, czy jest to liczba, czy identyfikator, dopóki nie trafi w „a” i musi przejść wstecz, aby przejść do fazy analizy leksykalnej w celu zidentyfikowania, że ​​jest to zmienna. Ale nie jest obsługiwany w kompilatorze.

Odniesienie

Angelin Nadar
źródło
0

Nie może być w tym nic złego, jeśli chodzi o deklarowanie zmiennej, ale jest pewna niejednoznaczność, gdy próbuje użyć tej zmiennej w innym miejscu, jak to:

let 1 = "Witaj świecie!" drukuj (1) drukuj (1)

print to ogólna metoda, która akceptuje wszystkie typy zmiennych. więc w tej sytuacji kompilator nie wie, do której (1) odnosi się programista: 1 z wartości całkowitej, czy 1, która przechowuje wartość ciągu. może lepiej dla kompilatora w tej sytuacji jest pozwolić na zdefiniowanie czegoś takiego, ale próbując użyć tej niejednoznacznej rzeczy, przynieś błąd z możliwością korekty, jak naprawić ten błąd i wyczyść tę niejednoznaczność.

Ali Torabi
źródło