Liệu cú pháp của ngôn ngữ lập trình phụ thuộc vào việc thực hiện chúng?


12

Mặc dù, câu hỏi của tôi có thể hoàn toàn không liên quan, nhưng tôi đã cảm nhận được một mô hình giữa hầu hết các ngôn ngữ lập trình và việc triển khai chính thức của chúng.

Các ngôn ngữ được giải thích (byte-phiên dịch?) Như Python, Lua, v.v. thường có cú pháp cực kỳ nhẹ nhàng và dễ hiểu và thường không có loại hoặc không yêu cầu nhà phát triển viết rõ ràng các loại biến trong mã nguồn;

Các ngôn ngữ được biên dịch như C, C ++, Pascal, v.v. thường có cú pháp chặt chẽ, thường có các loại và chủ yếu yêu cầu nhiều mã / thời gian phát triển hơn

Các ngôn ngữ có triển khai chính thức là JIT-Compiled như Java / C # thường là một sự thỏa hiệp duy nhất giữa hai ngôn ngữ trên với một số tính năng tốt nhất của cả hai.

Một số ngôn ngữ lập trình được biên dịch hiện đại hơn như D và Vala (và triển khai Java GJC của Java) có lẽ là một ngoại lệ đối với quy tắc này và giống với Cú pháp và các tính năng của các ngôn ngữ được biên dịch JIT như Java và C #.

Câu hỏi đầu tiên của tôi là, điều này có thực sự phù hợp? Hoặc đây chỉ là một sự trùng hợp ngẫu nhiên mà hầu hết các ngôn ngữ được diễn giải đều có cú pháp dễ dàng, những ngôn ngữ được biên dịch JIT có cú pháp và tính năng vừa phải, v.v.

Thứ hai, nếu đây không phải là sự trùng hợp, thì tại sao nó lại như vậy? Ví dụ như, một số tính năng chỉ có thể được triển khai trong ngôn ngữ lập trình nếu bạn nói, JIT-Biên dịch nó?


@YannisRizos xin lỗi, Đây không phải là một trích dẫn. Tôi chỉ muốn làm nổi bật nó. Tôi sẽ chỉnh sửa nó.
Người học việc

1
Thật tuyệt, tôi mặc dù đó không phải là một câu trích dẫn nhưng nó có thể dẫn đến những người trả lời nghĩ rằng đó là một và không cố gắng bác bỏ nó (hoặc đồng ý một cách mù quáng với nó) ... Tôi đã nhận thấy những mô hình tương tự nhưng không may là không tốt câu trả lời.
yannis

@ R.MartinhoFernandes xin lỗi, tôi đã không nhận ra điều đó. Tôi sẽ chỉnh sửa nó (một lần nữa).
Người học việc

4
Perl được gõ động cho các loại do người dùng xác định, được nhập tĩnh để phân biệt các mảng, băm, vô hướng và chương trình con, và được gõ mạnh thông qua việc sử dụng nghiêm ngặt, được giải thích và biên dịch JIT (không phải cùng lúc) để hiểu được thiết kế ngôn ngữ, ném vào một số Perl luôn vui vẻ ...
yannis

3
Bạn có ý nghĩa gì bởi "cú pháp khoan dung" so với "cú pháp nghiêm ngặt"? Chúng đều là ngôn ngữ chính thức và không ngôn ngữ nào sẽ chạy mã nguồn có lỗi cú pháp.
nikie

Câu trả lời:


17

Không có bất kỳ kết nối nào giữa ngữ nghĩa và cú pháp. Các ngôn ngữ được biên dịch homoiconic như Scheme đi kèm với một cú pháp khá tối giản. Các ngôn ngữ meta được biên dịch ở mức độ thấp như Forth thậm chí còn đơn giản hơn thế. Một số ngôn ngữ được biên dịch rất nghiêm ngặt được xây dựng theo cú pháp tầm thường (nghĩ ML, Haskell). Cú pháp OTOH, Python rất nặng, về một số quy tắc cú pháp.

Và đúng vậy, gõ không liên quan gì đến cú pháp, nó thuộc về ngữ nghĩa của ngôn ngữ, trừ khi đó là một thứ bị biến thành C ++, nơi bạn thậm chí không thể phân tích cú pháp mà không có sẵn tất cả thông tin gõ.

Một xu hướng chung là các ngôn ngữ phát triển quá lâu và không chứa bất kỳ biện pháp bảo vệ thiết kế nào chống lại sự sai lệch cú pháp sớm muộn sẽ phát triển thành những sự ghê tởm cú pháp.


+1 để khiến tôi tìm kiếm "homoiconic" ... Và cho cái gật đầu tinh tế với PHP ...
yannis

1
+1, các ngôn ngữ phát triển quá lâu và không chứa bất kỳ biện pháp bảo vệ thiết kế nào , điều này cũng đề cập đến Delphi / Object-Pascal?
Người học việc

1
@ThomasEding, bạn sai rồi. Các ngữ nghĩa tương tự có thể được triển khai trên một loạt các kiểu cú pháp rất rộng, ngay cả với một ngôn ngữ không cú pháp (như Lisp hoặc Forth). Có thể sử dụng cùng một cú pháp với rất nhiều ngữ nghĩa khác nhau - ví dụ: cú pháp của các biểu thức C và Verilog gần giống nhau, nhưng ngữ nghĩa thì khác nhau đáng kể.
SK-logic

1
@ SK-logic - Chỉ vì nó phức tạp và Turing-Complete không có nghĩa là nó ít nhất là một phần rất lớn trong cú pháp của chương trình. Phân tích cú pháp các ngôn ngữ khác nhau là Turing-Complete, điều đó không làm cho việc phân tích cú pháp một cái gì đó "không liên quan gì đến cú pháp". Cú pháp không phải là về "phạm vi", đó là về các quy tắc về cấu trúc của các câu trong một ngôn ngữ - mà không nói bất cứ điều gì về những câu đó có nghĩa gì. Kiểm tra kiểu và suy luận kiểu hoạt động trên các cây cú pháp của chương trình mà không thực hiện chúng - chúng xác định mọi thứ về cấu trúc của chương trình mà không nói gì về ...
Jack

1
@Jack, bạn đang cố gắng xác định lại cú pháp là gì. Không có ngôn ngữ thực tế nào cần trình phân tích cú pháp hoàn chỉnh Turing, hầu hết không có gì khác ngoài ngữ cảnh. Và đây là nơi cú pháp nên ở lại. Xin vui lòng không mở rộng khái niệm này (đã quá kéo dài) bất cứ nơi nào khác. Và tôi đã đề cập đến sự đồng hình của Curry-Howard - tất cả là về ngữ nghĩa, vượt xa các quy tắc chính xác đơn thuần. Tôi nghĩ, thuật ngữ " type checking" cực kỳ phản tác dụng và không nên được sử dụng, nó rất sai lệch, nó không phản ánh bản chất của các hệ thống loại.
SK-logic

6

Chủ yếu đây là một sự trùng hợp.

Ngôn ngữ lập trình đã phát triển theo thời gian và công nghệ trình biên dịch và phiên dịch đã được cải thiện. Hiệu quả của xử lý cơ bản (tức là thời gian biên dịch, chi phí phiên dịch, thời gian thực hiện, v.v.) cũng ít quan trọng hơn vì các nền tảng điện toán chính thống đã phát triển mạnh mẽ.

Cú pháp ngôn ngữ không có tác động - ví dụ, Pascal được thiết kế rất cẩn thận để nó có thể sử dụng một trình biên dịch thông qua duy nhất - tức là vượt qua một trong những nguồn và bạn có mã máy excutable. Mặt khác, Ada không chú ý đến điều này và trình biên dịch Ada nổi tiếng là khó viết - hầu hết yêu cầu nhiều hơn một lần vượt qua. (Một trình biên dịch Ada rất tốt mà tôi đã sử dụng nhiều năm trước là trình biên dịch 8 pass. Như bạn có thể tưởng tượng, nó rất chậm.)

Nếu bạn nhìn vào các ngôn ngữ cũ như Fortran (được biên dịch) và BASIC (được giải thích hoặc biên dịch) thì chúng có / có các quy tắc ngữ nghĩa và ngữ nghĩa rất nghiêm ngặt. [Trong trường hợp BASIC, đó không phải là hóa đơn BASIC cũ, bạn cần quay lại trước đó với bản gốc.]

Mặt khác, nhìn vào những thứ cũ hơn như APL (một trò vui), kiểu này có kiểu gõ động. Nó cũng thường được giải thích nhưng cũng có thể được biên dịch.

Cú pháp Lenient là một câu hỏi khó - nếu điều đó có nghĩa là bạn có những thứ không bắt buộc hoặc có thể được suy ra thì điều đó có nghĩa là ngôn ngữ có đủ độ phong phú mà nó có thể bị loại bỏ. Sau đó, một lần nữa, BASIC đã có điều đó từ nhiều năm trước khi tuyên bố "LET" trở thành tùy chọn!

Nhiều ý tưởng mà bạn thấy bây giờ (ví dụ, kiểu chữ không gõ hoặc động) thực sự rất cũ - xuất hiện lần đầu tiên vào những năm 1970 hoặc đầu những năm 1980. Cách chúng được sử dụng và ngôn ngữ mà những ý tưởng này được sử dụng đã thay đổi và phát triển. Nhưng về cơ bản, phần lớn những gì mới thực sự là những thứ cũ mặc quần áo mới.

Dưới đây là một số ví dụ ngoài đỉnh đầu của tôi:

  • APL: gõ động. Nói chung là diễn giải. Đến từ những năm 1960/1970.
  • CƠ BẢN: gõ mạnh mẽ hoặc năng động. Giải thích hoặc biên soạn. 1970 và nhiều hơn nữa.
  • Fortran: gõ mạnh. Tổng hợp. Những năm 1960 hoặc sớm hơn.
  • Algol68: đánh máy mạnh. Tổng hợp. Những năm 1960.
  • PL / 1: gõ mạnh. Tổng hợp. Những năm 1960.
  • Pascal: đánh máy mạnh. Tổng hợp. Những năm 1970. (Nhưng vào những năm 1980, có trình biên dịch P-System rất giống với trình biên dịch JIT!)
  • Một số triển khai của Fortran và những người khác của DEC trong những ngày đầu đã được biên soạn một phần và diễn giải một phần.
  • Smalltalk: gõ động. Được biên dịch thành mã byte được giải thích. Những năm 1980
  • Prolog: lạ hơn. Chức năng. Biên dịch (Turbo Prolog, có ai không?). Những năm 1980
  • C: gõ mạnh (ha ha). Tổng hợp. Năm 1960..Hôm nay.
  • Ada: gõ mạnh mẽ. Tổng hợp. Những năm 1980
  • Perl: gõ động. (Cú pháp mạnh). Giải thích. Những năm 1990 (?).

Tôi có thể tiếp tục.

  • Góc Nitopperers: Nhiều ngôn ngữ được giải thích được mã hóa hoặc "biên dịch byte" tại thời điểm nguồn được tải / đọc. Điều này làm cho hoạt động tiếp theo của trình thông dịch đơn giản hơn rất nhiều. Đôi khi bạn có thể lưu phiên bản được biên dịch byte của mã. Đôi khi bạn không thể. Nó vẫn giải thích.

Cập nhật: Bởi vì tôi không đủ rõ ràng.

Gõ có thể rất khác nhau.

Kiểu gõ tĩnh cố định thời gian biên dịch là phổ biến (ví dụ: C, Ada, C ++, Fortan, v.v.). Đây là nơi bạn tuyên bố một THING của LOẠI và nó là như vậy mãi mãi.

Cũng có thể có kiểu gõ động, trong đó thứ chọn ra loại được gán cho nó. Ví dụ, PHP và một số BASIC đầu tiên và APL, nơi bạn sẽ gán một số nguyên cho một biến và từ đó trở đi là một kiểu số nguyên. Nếu sau này bạn đã gán một chuỗi cho nó, thì đó là một loại chuỗi. Và như thế.

Và sau đó là việc gõ lỏng lẻo, ví dụ PHP, nơi bạn có thể thực hiện những việc thực sự kỳ lạ như gán một số nguyên (được trích dẫn, do đó là một chuỗi) cho một biến và sau đó thêm một số vào đó. (ví dụ: '5' + 5 sẽ dẫn đến 10). Đây là vùng đất kỳ quái, nhưng cũng có lúc rất hữu ích.

TUY NHIÊN đây là những tính năng được thiết kế thành ngôn ngữ. Việc thực hiện chỉ làm cho điều đó xảy ra.


13
Gõ mạnh không phải là đối tác của gõ động. Đó là đối tác của gõ yếu. Đối tác của kiểu gõ động là kiểu gõ tĩnh: trong một, các loại biểu thức trong một chương trình có thể được biết tĩnh (tức là không chạy chương trình); trong một kiểu khác, các kiểu chỉ có thể được biết một cách linh hoạt (tức là chương trình phải được chạy).
R. Martinho Fernandes

Có và cả một số biến thể của BASIC và APL đã thực hiện điều này vào cuối những năm 1970. Các loại APL không hoàn toàn như chúng ta hiểu ngày nay (là những thứ như số nguyên / float phổ biến nhưng cũng có thể là vectơ, chuỗi và ma trận đa chiều).
quick_now

Một trình thông dịch Fortran vẫn được sử dụng rộng rãi (xem Cernlib và PAW). Và hậu duệ của nó, ROOT, được xây dựng dựa trên trình thông dịch C ++.
SK-logic

Tôi không hoàn toàn rõ ràng cách gõ mạnh / yếu và tĩnh / động liên quan đến cú pháp, phải trung thực. Nhưng chất lượng câu trả lời là khá tốt, vì vậy tôi chỉ tránh nâng cao. Tôi đã gõ lớp C là "tĩnh / yếu" (việc xem xét một giá trị được lưu trữ như thể đó là một loại khác, có thể nhận được giá trị sai).
Vatine

@Vatine - Tôi thực sự sẽ nói mạnh về thời gian biên dịch, không tồn tại trong thời gian chạy - nếu bạn muốn nó theo cách đó. Bạn có thể làm điều đó bằng cách sử dụng các con trỏ và tương đương của chúng trong nhiều ngôn ngữ. Nó thậm chí có thể trong pascal cổ điển bằng cách sử dụng các bản ghi biến thể và trong Ada sử dụng UNCHECKED_CONVERSION (mặc dù khó, nhưng có thể).
quick_now

2

Tôi nghĩ đó là cách khác: việc thực hiện phụ thuộc vào cú pháp. Ví dụ: nếu cú ​​pháp của bạn cho phép phản ánh, thì việc triển khai phải cung cấp thời gian chạy hỗ trợ điều đó.


@Inter liềnHacker: nhưng đó là trong java, vì vậy tôi nên tuyệt vời
sehe

2

Tôi thường đồng ý với quick_now rằng quan sát của bạn chủ yếu là kết quả của lịch sử. Điều đó nói rằng, lý do cơ bản rút ra một cái gì đó như thế này:

The more modern a language is, the more comfortable it should be to use.

(Không phải là một trích dẫn thực sự, chỉ là công thức của riêng tôi.) Khi tôi viết comfortableở đây, tôi đề cập đến những gì bạn gọi best features of both. Chính xác hơn, tôi không muốn nói hoặc chống lại kiểu gõ tĩnh / động hoặc cú pháp chặt chẽ / khoan dung. Thay vào đó, điều quan trọng là phải xem trọng tâm được đặt vào các nhà phát triển và tăng mức độ thoải mái khi làm việc với ngôn ngữ.

Dưới đây là một số lý do, chưa được đề cập trong các câu trả lời trước, có thể cung cấp cho bạn một số ý tưởng về lý do tại sao bạn quan sát những điều này (và tất cả đều dựa trên lịch sử phát triển lan truyền lập trình):

  • Chúng tôi có hàng trăm làn đường lập trình những ngày này. Khi một cái mới xuất hiện, làm thế nào nó có thể tìm thấy một đối tượng rộng? Đây là lý do chính, tại sao các ngôn ngữ mới luôn cố gắng tăng mức độ thoải mái của nhà phát triển. Nếu ngôn ngữ có thể làm giống như ngôn ngữ cũ, nhưng có thể làm điều đó dễ dàng hơn / đơn giản hơn / thanh lịch hơn / v.v. bạn có thể muốn xem xét thực sự chuyển đổi.

  • Học đường đi đôi với điều đó. Trước đây, chúng tôi có ít ngôn ngữ và đầu tư thời gian để học một ngôn ngữ là xứng đáng. Ngay cả khi điều đó có nghĩa là đầu tư rất nhiều thời gian. Sự thoải mái một lần nữa được tăng lên, nếu bạn nghĩ ra một ngôn ngữ mà các nhà phát triển có thể học rất nhanh. Sự phức tạp của bất kỳ loại nào (ví dụ, cú pháp liên quan phức tạp) đều gây bất lợi cho điều này, và do đó, ngày càng giảm đi trong các ngôn ngữ mới hơn.

  • Tiến bộ công nghệ (một lý do lịch sử trực tiếp ở đây) chịu trách nhiệm rằng các nhà xây dựng trình biên dịch giờ đây có thể tập trung nhiều hơn vào sự thoải mái của nhà phát triển. Trong những ngày đầu, chúng tôi rất vui khi có thể xây dựng một trình biên dịch. Tuy nhiên, điều đó thường ngụ ý những hạn chế nặng nề được thực hiện. Khi bí quyết công nghệ tăng lên, chúng tôi đã có thể gỡ bỏ những hạn chế này một lần nữa.

Vì vậy, nói chung, các ngôn ngữ lập trình và trình biên dịch đã thấy sự phát triển tương tự như các ứng dụng người dùng cuối điển hình:

  1. Giai đoạn ban đầu: Đó là một điều tuyệt vời để có, nhưng công nghệ chảy máu hầu như không làm cho nó hoạt động với chi phí thoải mái / khả năng sử dụng / những gì không.
  2. Cải tiến công nghệ: Chúng ta có thể xây dựng những thứ này mạnh mẽ hơn, nhanh hơn và dễ dàng hơn.
  3. Tập trung chuyển sang người dùng: Tương tự như phong trào Web2.0 tập trung vào trải nghiệm người dùng, ngôn ngữ lập trình mới tập trung vào quan điểm của nhà phát triển.

(Not a quote really, just my own formulation.)Chà, bạn đã định dạng nó dưới dạng mã, không phải là một blockquote, vì vậy tôi không nghĩ có ai nghĩ đó là một trích dẫn :)
yannis

3
Thoải mái rõ ràng phụ thuộc vào một hương vị (mà luôn luôn hoàn toàn chủ quan). Ngôn ngữ tôi cảm thấy thoải mái nhất được thiết kế vào năm 1959 và tôi không thể đối phó với một số ngôn ngữ xuất hiện trong thế kỷ này.
SK-logic

1
Tiện nghi cũng phụ thuộc vào mục đích. Chạy PHP hoặc Prolog trên micro nhúng 8k cho bộ điều khiển máy giặt có thể "thoải mái" để lập trình, nhưng cũng thật khó để làm cho nó phù hợp và chạy với hiệu suất chấp nhận được.
quick_now

0

Một ngôn ngữ lập trình nhất định có thể hoặc không thể tiết lộ hoặc hạn chế đủ thông tin ngữ nghĩa cho trình biên dịch để suy luận làm thế nào để giảm nó thành mã thực thi mà không cần thêm các quyết định thời gian chạy ("loại này là biến nào?", V.v.) Một số ngôn ngữ được thiết kế rõ ràng để thực hiện ràng buộc này là bắt buộc, hoặc dễ dàng để xác định.

Khi trình biên dịch trở nên thông minh hơn, chúng có thể đoán hoặc lập hồ sơ đủ thông tin để tạo mã thực thi cho (các) đường dẫn có khả năng nhất ngay cả đối với các ngôn ngữ không được thiết kế rõ ràng để phơi bày hoặc hạn chế các quyết định đó.

Tuy nhiên, các ngôn ngữ nơi mã (evalString ()) có thể được tạo hoặc nhập vào thời gian chạy (và các thứ khác mà trình biên dịch không thể suy ra hoặc đoán) có thể yêu cầu trình thông dịch hoặc trình biên dịch JIT có sẵn trong thời gian chạy, ngay cả khi đã thử trước biên dịch chúng.

Trước đây, ngôn ngữ lập trình và cách triển khai của nó có thể đã phát triển để phù hợp với một số ràng buộc phần cứng, chẳng hạn như trình thông dịch có thể phù hợp với 4k hoặc 16k hay trình biên dịch có thể hoàn thành trong thời gian chưa đến một phút của CPU. Khi các máy trở nên nhanh hơn, có thể (biên dịch lại) một số chương trình đã được giải thích trước đó nhanh như lập trình viên có thể nhấn phím quay lại hoặc giải thích mã nguồn chương trình được biên dịch trước đó nhanh hơn phần cứng cũ hơn có thể chạy các tệp thực thi được tối ưu hóa.

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.