Tại sao cho mỗi người đều có dấu hai chấm thay vì trong thế giới?


9

Từ hướng dẫn ngôn ngữ Java 5 :

Khi bạn thấy dấu hai chấm (:) hãy đọc nó là "trong".

Tại sao không sử dụng inở nơi đầu tiên sau đó?

Điều này đã làm tôi khó chịu trong nhiều năm. Bởi vì nó không phù hợp với phần còn lại của ngôn ngữ. Ví dụ, trong Java có implements, extends, supercho mối quan hệ giữa các loại thay vì biểu tượng như trong C ++, Scala hoặc Ruby.

Trong đại tràng Java được sử dụng trong 5 bối cảnh . Ba trong số đó được thừa kế từ C. Và hai người khác được Joshua Bloch chứng thực. Ít nhất, đó là anh ta sais trong cuộc nói chuyện "Cuộc tranh cãi đóng cửa" . Điều này xuất hiện khi ông chỉ trích việc sử dụng dấu hai chấm để ánh xạ là không phù hợp với từng ngữ nghĩa. Đối với tôi có vẻ kỳ lạ bởi vì đó là mô hình dự kiến ​​bị lạm dụng cho từng người. Thích list_name/category: elementshay laberl/term: meaning.

Tôi đã rình mò xung quanh jcp và jsr, nhưng không tìm thấy dấu hiệu của danh sách gửi thư. Không có cuộc thảo luận về vấn đề này được tìm thấy bởi google. Chỉ người mới bối rối bởi ý nghĩa của dấu hai chấm trong for.


Đối số chính chống lại inđược cung cấp cho đến nay:

  • yêu cầu từ khóa mới; và
  • phức tạp lexing.

Hãy xem xét các định nghĩa ngữ pháp có liên quan :

tuyên bố
    : Câu lệnh 'for' '(' forControl ')'
    | ...
    ;

để kiểm soát
    : EnhForControl
    | vì sao? ';' biểu hiện? ';' bỏ qua?
    ;

nâng caoForControl
    : biếnModifier * loại biếnDeclaratorId ':' biểu thức
    ;

Thay đổi từ :để inlàm không mang lại thêm phức tạp hoặc đòi hỏi từ khóa mới.


1
Nguồn tốt nhất để tìm ra động lực của các nhà thiết kế ngôn ngữ thường là chính các nhà thiết kế. Điều đó nói rằng, đây rõ ràng chỉ là đường cú pháp trên một lần lặp; xem stackoverflow.com/questions/11216994/ Mạnh
Robert Harvey

Câu trả lời:


8

Các trình phân tích cú pháp thông thường vì chúng thường được dạy có một giai đoạn lexer trước khi trình phân tích cú pháp chạm vào đầu vào. Các lexer (cũng có thể là máy quét của máy quét hay, máy quét tokenizer,), cắt đầu vào thành các mã thông báo nhỏ được chú thích bằng một loại. Điều này cho phép trình phân tích cú pháp chính sử dụng mã thông báo làm thành phần đầu cuối thay vì phải coi mỗi ký tự là đầu cuối, điều này dẫn đến tăng hiệu quả đáng chú ý. Đặc biệt, lexer cũng có thể xóa tất cả các bình luận và khoảng trắng. Tuy nhiên, một giai đoạn mã thông báo riêng biệt có nghĩa là các từ khóa cũng không thể được sử dụng làm định danh (trừ khi ngôn ngữ hỗ trợ việc cắt xén có phần không được ưa chuộng hoặc tiền tố tất cả các định danh có dạng sigil như $foo).

Tại sao? Giả sử chúng ta có một mã thông báo đơn giản để hiểu các mã thông báo sau:

FOR = 'for'
LPAREN = '('
RPAREN = ')'
IN = 'in'
IDENT = /\w+/
COLON = ':'
SEMICOLON = ';'

Mã thông báo sẽ luôn khớp với mã thông báo dài nhất và thích từ khóa hơn số nhận dạng. Vì vậy, interestingsẽ được lexed như IDENT:interesting, nhưng insẽ được lex như IN, không bao giờ như IDENT:interesting. Một đoạn mã như

for(var in expression)

sẽ được dịch sang luồng mã thông báo

FOR LPAREN IDENT:var IN IDENT:expression RPAREN

Cho đến nay, điều đó làm việc. Nhưng bất kỳ biến innào sẽ được coi là từ khóa INchứ không phải là một biến, sẽ phá vỡ mã. Các lexer không giữ bất kỳ trạng thái nào giữa các mã thông báo và không thể biết đó inthường là một biến trừ khi chúng ta ở trong một vòng lặp for. Ngoài ra, mã sau đây phải hợp pháp:

for(in in expression)

Đầu tiên insẽ là một định danh, thứ hai sẽ là một từ khóa.

Có hai phản ứng cho vấn đề này:

Từ khóa theo ngữ cảnh gây nhầm lẫn, thay vào đó hãy sử dụng lại từ khóa.

Java có nhiều từ dành riêng, một số từ không có tác dụng ngoại trừ việc cung cấp các thông báo lỗi hữu ích hơn cho các lập trình viên chuyển sang Java từ C ++. Thêm từ khóa mới phá vỡ mã. Việc thêm các từ khóa theo ngữ cảnh gây nhầm lẫn cho người đọc mã trừ khi chúng có tính năng tô sáng cú pháp tốt và khiến công cụ khó thực hiện vì chúng sẽ phải sử dụng các kỹ thuật phân tích cú pháp nâng cao hơn (xem bên dưới).

Khi chúng tôi muốn mở rộng ngôn ngữ, cách tiếp cận lành mạnh duy nhất là sử dụng các ký hiệu mà trước đây không hợp pháp trong ngôn ngữ. Đặc biệt, đây không thể là định danh. Với cú pháp vòng lặp foreach, Java đã sử dụng lại :từ khóa hiện tại với nghĩa mới. Với lambdas, Java đã thêm một ->từ khóa mà trước đây không thể xảy ra trong bất kỳ chương trình pháp lý nào ( -->vẫn sẽ được coi '--' '>'là hợp pháp và ->trước đây có thể đã '-', '>'bị từ chối, nhưng trình tự đó sẽ bị trình phân tích cú pháp từ chối).

Từ khóa theo ngữ cảnh đơn giản hóa các ngôn ngữ, hãy thực hiện chúng

Lexers là hữu ích không thể chối cãi. Nhưng thay vì chạy một lexer trước trình phân tích cú pháp, chúng ta có thể chạy chúng song song với trình phân tích cú pháp. Các trình phân tích cú pháp từ dưới lên luôn biết tập hợp các loại mã thông báo sẽ được chấp nhận tại bất kỳ vị trí nào. Sau đó, trình phân tích cú pháp có thể yêu cầu lexer khớp với bất kỳ loại nào ở vị trí hiện tại. Trong một vòng lặp for, mỗi trình phân tích cú pháp sẽ ở vị trí được biểu thị bằng ·ngữ pháp (đơn giản hóa) sau khi tìm thấy biến:

for_loop = for_loop_cstyle | for_each_loop
for_loop_cstyle = 'for' '(' declaration · ';' expression ';' expression ')'
for_each_loop = 'for' '(' declaration · 'in' expression ')'

Tại vị trí đó, các mã thông báo hợp pháp là SEMICOLONhoặc IN, nhưng không IDENT. Một từ khóa insẽ hoàn toàn rõ ràng.

Trong ví dụ cụ thể này, các trình phân tích cú pháp từ trên xuống sẽ không gặp vấn đề gì vì chúng ta có thể viết lại ngữ pháp trên thành

for_loop = 'for' '(' declaration · for_loop_rest ')'
for_loop_rest =  · ';' expression ';' expression
for_loop_rest = · 'in' expression

và tất cả các mã thông báo cần thiết cho quyết định có thể được nhìn thấy mà không cần quay lại.

Xem xét khả năng sử dụng

Java luôn có xu hướng đơn giản về ngữ nghĩa và cú pháp. Ví dụ, ngôn ngữ không hỗ trợ quá tải toán tử vì nó sẽ làm cho mã phức tạp hơn nhiều. Vì vậy, khi quyết định giữa in:cho một cú pháp vòng lặp cho mỗi vòng lặp, chúng ta phải xem xét cái nào ít gây nhầm lẫn và rõ ràng hơn cho người dùng. Trường hợp cực đoan có lẽ sẽ là

for (in in in in())
for (in in : in())

(Lưu ý: Java có các không gian tên riêng biệt cho tên loại, biến và phương thức. Tôi nghĩ rằng đây là một lỗi, chủ yếu. Điều này không có nghĩa là thiết kế ngôn ngữ sau này phải thêm nhiều lỗi.)

Sự thay thế nào cung cấp sự phân tách trực quan rõ ràng hơn giữa biến lặp và bộ sưu tập lặp? Sự thay thế nào có thể được nhận ra nhanh hơn khi bạn nhìn vào mã? Tôi đã thấy rằng các biểu tượng tách biệt tốt hơn một chuỗi các từ khi nói đến các tiêu chí này. Các ngôn ngữ khác có giá trị khác nhau. Ví dụ: Python đánh vần nhiều toán tử bằng tiếng Anh để chúng có thể được đọc một cách tự nhiên và dễ hiểu, nhưng những thuộc tính tương tự có thể làm cho việc hiểu một mẩu Python trong nháy mắt khá khó khăn.


17

Cú pháp vòng lặp for-every được thêm vào trong Java 5. Bạn phải tạo inmột từ khóa ngôn ngữ và thêm từ khóa vào ngôn ngữ sau này là điều bạn tránh bằng mọi giá vì nó phá vỡ mã hiện có - đột nhiên tất cả các biến có tên in gây ra phân tích cú pháp lỗi. enumđã đủ tồi tệ trong vấn đề đó.


2
Điều đó có vẻ ... bất tiện. Nó giả định rằng các nhà thiết kế ngôn ngữ đã đủ tốt để dự báo hầu hết các từ khóa cần thiết ngay từ đầu. Tôi không chắc nó thậm chí còn cần thiết; trình biên dịch phong nha có thể xác định liệu một từ khóa có phải là một biến theo ngữ cảnh của nó hay không.
Robert Harvey

2
Tôi không nghĩ Java có các từ khóa theo ngữ cảnh như C # có. Vì vậy, sử dụng incó nghĩa là để giới thiệu một từ khóa mới, do đó phá vỡ tính tương thích ngược ( System.in, bất cứ ai?) Hoặc giới thiệu một khái niệm hoàn toàn mới chưa biết trước đây (từ khóa theo ngữ cảnh). Tất cả vì cái gì đạt được?
Jörg W Mittag

2
Những tác hại của các từ khóa theo ngữ cảnh?
dùng2418306

5
@ user2418306 Thêm từ khóa không phải phá vỡ mã hiện tại, miễn là ngôn ngữ không được phân tích cú pháp với một giai đoạn từ vựng riêng biệt. Đặc biệt, một tên miền trong vùng in for(variable in expression)không bao giờ có thể mơ hồ với bất kỳ mã hợp pháp nào, ngay cả khi có thể sử dụng loại chữ in trong các biến. Tuy nhiên, một pha lexer riêng biệt khá phổ biến trong nhiều công cụ biên dịch. Điều này sẽ khiến việc phân tích Java với một số trình tạo trình phân tích cú pháp phổ biến trở nên khó khăn hơn hoặc ít nhất là khó hơn nhiều. Giữ cú pháp của ngôn ngữ đơn giản thường tốt cho tất cả những người liên quan; không phải ai cũng cần những cú pháp cú pháp như C ++ hay Perl.
amon

1
@RobertHarvey: Đừng quên điều đó constgotocả hai từ dành riêng trong Java, nhưng chưa được sử dụng (chưa).
TMN
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.