Hầu hết các ngôn ngữ lập trình dường như được thiết kế để không cho phép một người khai báo một mã định danh bắt đầu bằng một số. Tôi chỉ tò mò muốn biết lý do. Tôi đã tìm kiếm trên web, nhưng không thể tìm thấy một lời giải thích thỏa đáng.
Hầu hết các ngôn ngữ lập trình dường như được thiết kế để không cho phép một người khai báo một mã định danh bắt đầu bằng một số. Tôi chỉ tò mò muốn biết lý do. Tôi đã tìm kiếm trên web, nhưng không thể tìm thấy một lời giải thích thỏa đáng.
Câu trả lời:
Trong C / C ++, một số được theo sau bởi một chữ cái được coi là hằng số và chuỗi theo sau, đủ điều kiện loại hằng. Vì vậy, ví dụ (đây là VC ++, không chắc chúng chuẩn như thế nào):
Vì vậy, a) dễ dàng hơn đối với người từ vựng như Daniel đã nói nhưng cũng b) nó tạo ra sự khác biệt rõ ràng vì 0y có thể là một biến nhưng 0u sẽ không bao giờ. Cộng với các vòng loại khác, như "i64" đã được thêm vào muộn hơn "l" hoặc "u" và họ muốn giữ tùy chọn mở thêm nhiều hơn nếu cần.
Sự tiện lợi của những người thực hiện lexer. (Không, nghiêm túc, đó là về nó. Các ngôn ngữ khác nhau có những lý do khác, nhưng cuối cùng nó lại xuất phát từ đó.)
0flu
một nghĩa đen và 0glu
là một định danh địa phương.
int 0u = 5; unsigned int x = 0u;
Tuy nhiên, bạn chọn xác định cách giải thích mã này (có thể là x == 0 hoặc x == 5), mọi người sẽ bị nhầm lẫn Vì sự mơ hồ. Ngay cả khi việc triển khai trình biên dịch theo cách này là không quan trọng, một nhà thiết kế giỏi có thể sẽ không làm điều đó.
Hãy xem xét 2 trường hợp sau:
Giả sử rằng một định danh có thể bắt đầu bằng một số.
Vì vậy, một tuyên bố như dưới đây sẽ hợp lệ (vì một định danh có thể có 1 hoặc nhiều ký tự):
số 3;
Khi tôi cố gắng sử dụng biến trên trong một chương trình, nó sẽ dẫn đến sự mơ hồ của trình biên dịch:
int 3, a;
3 = 5;
a = 3;
Trong câu lệnh a=3
, vai trò của 3 là gì (nó có phải là biến có giá trị 5 hay là số 3)?
Trái ngược với ví dụ trên, giả sử rằng một ngôn ngữ thực sự cho phép các định danh bắt đầu bằng một số trong khi vẫn không cho phép các số được sử dụng làm định danh. Điều này có thể gây ra các vấn đề sau:
Các quy tắc ngôn ngữ liên quan đến biến nói rằng một biến có thể bao gồm 1 hoặc nhiều ký tự sẽ phải được xác định lại thành quy tắc phức tạp như: Một biến có thể có một hoặc nhiều ký tự và phải là duy nhất nếu nó không bắt đầu bằng một số trong khi nó không thể có độ dài một ký tự khi bắt đầu bằng một số (vv ..)
Trình biên dịch sẽ phải kiểm tra và báo cáo các trường hợp lỗi khi tất cả các chữ số (ví dụ 333) và hậu tố bảng chữ cái hợp lệ (ví dụ 34L) đang được sử dụng làm tên biến. Trong các ngôn ngữ được gõ lỏng lẻo như Python và JS, nơi bạn có thể sử dụng các biến một cách nhanh chóng mà không cần khai báo chúng, thậm chí có thể không thể kiểm tra các trường hợp đặc biệt liên quan đến tất cả các chữ số, ví dụ if (33==5)
Ở đây, 33 có thể là một biến không được khai báo sai mà người dùng đã khai báo. Nhưng trình biên dịch sẽ không thể xác định điều này và báo cáo lỗi.
Thực hiện hạn chế này sẽ ngăn lập trình viên sử dụng số làm tên định danh.
int char = float
sẽ như thế nào ?
int
là một từ khóa và không phải là một định danh? Vâng, int
có quyền ưu tiên cao hơn giống như các từ vựng số sẽ có.
int 3,a; 3=5; a=3;
Trong câu lệnh a = 3, 3 được hiểu là định danh hay là số? Điều này gây ra sự mơ hồ. Hy vọng nó rõ ràng.
Đối với hầu hết các phần, điều này không liên quan gì đến việc giúp người viết trình biên dịch dễ dàng và hiệu quả phân tích cú pháp, nhưng, nhiều hơn để làm với việc thiết kế một cú pháp khuyến khích mã rõ ràng dễ đọc và rõ ràng.
Các nhà thiết kế ngôn ngữ của nó nghĩ rằng thật tuyệt khi có thể viết các chữ số như số 1 chỉ đơn giản là 1 .
Hoàn toàn có thể thiết kế một cú pháp ngôn ngữ trong đó các chữ số được trích dẫn theo một cách nào đó ví dụ như dấu ngã, vì vậy chữ số cho số một được mã hóa là ~ 1 ~ và bất cứ thứ gì không phải là từ khóa và không được đặt trong dấu ngoặc kép đều được coi là một tên biến .
Vì vậy, bạn có thể mã các câu lệnh như:
1 = ~2~
two = 1 * ~2~
Nhưng cũng:
2 = ~3~
six = 2 + 2
Bất cứ cú pháp nào bạn chọn mơ hồ và khó theo dõi mã là không thể tránh khỏi.
Ngôn ngữ C và hầu hết các ngôn ngữ "ngoặc nhọn" xuất phát từ C cũng nghĩ rằng nên cho phép các lập trình viên viết mã trực tiếp các chữ Octal và Hexadecimal, và, chỉ định loại chữ nếu điều này quan trọng. Vì thế
010 // Octal 10 = 8;
0x10 // Hexadecimal 10 = 16;
5l // long integer with decimal value 5
2.0d // double float with value 2
Vì vậy, ngay cả khi bạn cho phép các tên biến bắt đầu bằng một số theo sau là sự kết hợp của các số và chữ cái bao gồm ít nhất một chữ cái, bạn sẽ đưa ra cho lập trình viên vấn đề quyết định xem một nhóm nhất định có tạo thành một tên biến hay một chữ số không
2lll = 22 // OK
2ll = 2 // compiler error
Sự mơ hồ như vậy sẽ không giúp được ai viết hay đọc một chương trình.
Đối với một ví dụ trong thế giới thực có liên quan chặt chẽ, bạn có thể xem ngôn ngữ PL / 1 mà các nhà thiết kế nghĩ rằng có thể sử dụng từ khóa làm tên biến là một ý tưởng hay để:
IF THEN THEN THEN = ELSE; ELSE ELSE = THEN;
IF IF THEN ELSE = IF; ELSE THEN = ELSE;
DO WHILE (WHILE = DO); END = WHILE + DO; END;
Là mã hợp lệ biên dịch và thực thi.
Fortran có ảnh hưởng rất lớn đến cách các ngôn ngữ sau này được thiết kế. Ngay từ sớm (một số vấn đề đã được khắc phục) Fortran gần như không có quy tắc nào giới hạn tên bạn có thể đặt cho một định danh. Điều này làm cho ngôn ngữ cực kỳ khó phân tích cả cho trình biên dịch và lập trình viên. Đây là một ví dụ kinh điển:
if if .eq. then then = else else else = endif endif
K I K K I I K I I K
Ở đây tôi đã đánh dấu "từ khóa ngôn ngữ" bằng K và số nhận dạng (tên biến) I. Cho rằng không có sự khác biệt về chính tả, tôi nghĩ bạn có thể hiểu điều này có thể gây nhầm lẫn như thế nào. Tất nhiên, đây là một ví dụ cực đoan và không có ai từng viết mã khá giống như thế này. Đôi khi, mọi người đã "tái chế" các từ khóa ngôn ngữ làm tên định danh - và trong nhiều trường hợp, một lỗi đánh máy đơn giản có thể dẫn đến mã mà thông số ngôn ngữ nói nên được phân tích theo cách này, mặc dù nó hoàn toàn không có ý định. Đối với một ví dụ nổi tiếng khác, hãy so sánh điều này:
do 10 i = 1,10
đến đây:
do 10 i = 1.10
Đầu tiên là một vòng lặp do - lặp lại một khối mã 10 lần. Tuy nhiên, dấu phẩy thứ hai đã thay đổi dấu phẩy thành dấu thập phân, do đó, nó gán giá trị 1.10
cho một biến có tên do 10 i
.
Điều này cũng có nghĩa là việc viết một trình phân tích cú pháp Fortran tương đối khó khăn - bạn không thể chắc chắn rằng do
ở đầu dòng thực sự là một từ khóa cho đến khi bạn đạt đến cuối dòng và xác minh rằng tất cả các yếu tố khác của một do
vòng lặp đã có mặt. Trình phân tích cú pháp nói chung phải sẵn sàng "quay lui", phân tích lại dòng từ đầu để đi đến câu trả lời "chính xác" (nhưng thường không có chủ ý) về những gì thực sự ở đó.
Sau một vài năm, các nhà thiết kế ngôn ngữ (hầu hết trong số họ dù sao) đã đi về phía cực đoan - hạn chế gần như mọi thứ về ngôn ngữ càng nhiều càng tốt mà không khiến người dùng phàn nàn quá nhiều.
Chẳng hạn, BASIC ban đầu, về cơ bản cho biết bạn thậm chí không thể sử dụng một từ khóa như một phần của mã định danh - ví dụ, fora=1
sẽ được phân tích cú pháp dưới dạng for a = 1
(nghĩa là bắt đầu một for
vòng lặp, không phải là một bài tập). Điều đó rõ ràng đã tạo ra đủ khiếu nại mà nó không kéo dài rất lâu. Quy tắc về việc bắt đầu một mã định danh bằng một chữ số dường như không tạo ra nhiều khiếu nại, vì vậy nó tiếp tục được sử dụng (ít nhất là trong hầu hết các ngôn ngữ).
Có khả năng quy ước này đã phát triển từ các quyết định thiết kế ngôn ngữ lịch sử rất sớm, vì trên các máy đầu tiên, toàn bộ trình biên dịch, bao gồm phân tích từ vựng, phải chạy trong một vài từ, ít bộ nhớ hơn cả bộ đệm dữ liệu của bộ xử lý cấp đầu tiên trên các thiết bị di động hiện tại, vì vậy các tên biến được phép rất hạn chế và phải dễ phân biệt với các hằng số trong rất ít mã op.
Do đó, quy ước đã trở thành những gì các thế hệ lập trình viên được sử dụng.
Đây không phải là quy tắc bắt buộc về ngôn ngữ lập trình mà chỉ là quy ước được nhiều nhà thiết kế ngôn ngữ sử dụng.
Tôi có thể thiết kế ngôn ngữ hoàn toàn khác nhau cho phép tất cả các ký tự cho định danh. Đối với tất cả các dòng mã, 20 ký tự đầu tiên sẽ mô tả loại câu lệnh, sau đó 20 ký tự tiếp theo sẽ xác định ký hiệu đầu tiên cho câu lệnh và 20 ký tự tiếp theo là toán hạng cho câu lệnh. Ngôn ngữ này sẽ được thực thi trên bộ xử lý ngăn xếp.
01234567890123456789 01234567890123456789 01234567890123456789
decl symbol 12345
assign value 12345 12345
decl symbol 99999
assign value 99999 12345
push 12345
push 99999
add
print top
Mã này có thể được dịch bằng C như sau:
int i12345 = 12345;
int i99999 = 12345;
printf("%d", i12345+i9999);
Đó là tất cả. Điều đó là vô nghĩa và quy tắc không có số định danh cũng vô nghĩa trong nền tảng logic.
Ngoài "sự tiện lợi cho người từ chối", tôi nghĩ nó cũng đáng để xem xét "sự tiện lợi cho người đọc".
Khi đọc mã, bạn cần nhanh chóng và liên tục xác định từ nào là định danh và số nào là số. Tìm kiếm một chữ số ở đầu dễ dàng hơn trong việc khớp mẫu trực quan của chúng tôi; Sẽ là một việc vặt nếu chúng ta phải kiểm tra cẩn thận tất cả các nhân vật để đảm bảo.
Câu trả lời cho câu hỏi này nằm ở automata hoặc chính xác hơn là automata hữu hạn xác định biểu thức chính quy. Quy tắc là ... trình biên dịch cần các thuật toán hoặc quy tắc chính xác để quyết định tại mọi ký tự mà chúng phân tích. Nếu số nhận dạng được cho phép bắt đầu bằng một số thì trình biên dịch sẽ ở trạng thái sửa chữa..bạn bản chất của mã thông báo sắp tới ... nó sẽ là số hoặc số nhận dạng ... và vì trình biên dịch không thể quay lại vị trí trước đó .. .so..để làm rõ cho trình biên dịch rằng mã thông báo sắp tới chính xác là mã định danh hoặc số ... hạn chế này là có ... vì trình biên dịch này biết chỉ bằng cách quét ký tự đầu tiên mà mã thông báo sắp tới là một định danh hoặc một số.