Ưu điểm của cú pháp ngôn ngữ từ trái sang phải


18

Tôi đã xem một cuộc phỏng vấn với Herb Sutter trên Channel9 và anh ấy đã đề cập ở phần cuối của video cú pháp từ trái sang phải sẽ đứng đầu trong danh sách trắng của anh ấy cho một tiêu chuẩn C ++ trong tương lai (mặc dù anh ấy thừa nhận rằng sửa đổi C ++ theo cách đó sẽ khá nhiều làm cho một con thú hoàn toàn khác nhau).

Ngoại trừ:

  • dễ hiểu hơn bởi con người, rõ ràng hơn bằng mắt thường;

    //C syntax
    
    /*pointer to function taking a pointer to function(which takes 2 integers as 
    
    arguments and returns an int), and an int as arguments and returning an int*/
    
    int (*fp)(int (*ff)(int x, int y), int b)
    
    //Go analogous syntax which is left to write
    
    f func(func(int,int) int, int) int
  • phân tích cú pháp dễ dàng hơn (dẫn đến hỗ trợ công cụ tốt hơn như được đề cập trong video - ví dụ: tái cấu trúc mã)

còn những lợi thế nào khác với cú pháp "trái sang phải" trong ngôn ngữ lập trình. Tôi chỉ biết Pascal và Go sử dụng loại cú pháp đó (và Go thậm chí không đi theo cách hoàn toàn như tôi hiểu từ bài đăng trên blog này mà tôi cũng lấy các ví dụ) Có khả thi để có một ngôn ngữ lập trình hệ thống với loại đó không cú pháp?


1
haskell sử dụng từ trái sang phải:f :: (Int -> Int -> Int) -> Int -> Int
Karoly Horvath

1
ActionScript cũng vậy : function strlen(s:String):int {...}. Ngoài ra, gõ lambda tính toán (do đó, Haskell).
outis

2
Bất cứ ai có thể giải thích xin vui lòng bỏ phiếu :)? Tôi không thể thấy bất kỳ lý do nào để đóng nó nhưng có lẽ tôi đang hỏi một câu hỏi "sai".

1
Tôi đã không bỏ phiếu để đóng, nhưng, nhận xét của @Devjosh là phù hợp, nó phù hợp hơn với các lập trình viên, hy vọng ai đó sẽ di chuyển nó ....
Nim

3
@Frank: và đừng quên, trong trường hợp hàm con trỏ hàm, cú pháp khó xử vì loại thực tế bị tách ! Đó là một cú đánh thấp ...
Matthieu M.

Câu trả lời:


12

Ưu điểm cơ bản là phân tích cú pháp đơn giản và độc đáo hơn. Lưu ý rằng sau khi dòng được phân tích cú pháp, trình biên dịch sẽ biết loại chính xác là gì, vì vậy từ đó, loại được xác định là không liên quan.

Bất kỳ hàm nào trả về một đối số của kiểu mảng hoặc kiểu con trỏ hàm hiện khó đọc:

// common way of obtaining the static size in elements of an array:
template <typename T, int N>
char (&array_size_impl( T (&)[N] ))[N];
// alternative parser:
template <typename T, int N>               // this would probably be changed also
array_simple_impl function( T [N] & ) char [N] &;

Và sẽ có ít cơ hội hơn cho sự hiểu lầm (như cách phân tích nhiều nhất ):

// Current C++
type x( another_type() );      // create an instance x of type passing a
                               // default constructed another_type temporary?
                               // or declare a function x that returns type and takes as argument
                               // a function that has no arguments and returns another_type
// How the compiler reads it:
x function( function() another_type ) type;

// What many people mean:
x type { another_type{} };

Sử dụng một cách tiếp cận tương tự để khởi tạo thống nhất trong C ++ 0x (nghĩa là {}để xác định khởi tạo). Lưu ý rằng với cách tiếp cận từ trái sang phải, rõ ràng hơn những gì chúng ta đang xác định. Nhiều người (chắc chắn là tôi) đã bị cắn tại bất kỳ thời điểm nào bởi lỗi phân tích cú pháp này trong quá khứ (hơn một lần) và đó sẽ không phải là trường hợp với cú pháp từ trái sang phải.


Làm thế nào về biểu thức? Làm thế nào "cú pháp trái sang phải" này sẽ ảnh hưởng đến quyền ưu tiên và thứ tự đánh giá của nhà điều hành?

1
@celavek: Nếu bạn quay lại cuộc phỏng vấn, bạn sẽ lưu ý rằng anh ta không muốn thay đổi toàn bộ cú pháp của ngôn ngữ, chỉ khai báo s và định nghĩa s. Tất nhiên, điều đó có thể thấm vào các biểu thức khác, tôi không chắc chắn rằng dòng cuối cùng trong các ví dụ ở trên là từ trái sang phải (nghĩ về cách tạo tạm thời, có thể cần phải thay đổi ... trong C # điều đó được giải quyết bằng cách đưa ra hai ngữ nghĩa cho newtoán tử cho structhoặc class, không áp dụng cho C ++ như trong C ++, không có sự phân biệt về các loại giá trị / tham chiếu.
David Rodríguez - dribeas

5

Làm thế nào chúng ta có ở đây

Cú pháp C để khai báo các điểm chức năng được dự định để phản ánh việc sử dụng. Hãy xem xét một khai báo hàm thông thường như thế này từ <math.h>:

double round(double number);

Để có một biến điểm, bạn có thể gán nó với loại an toàn bằng cách sử dụng

fp = round;

bạn cần phải khai báo fpbiến điểm đó theo cách này:

double (*fp)(double number);

Vì vậy, tất cả những gì bạn phải làm là xem cách bạn sẽ sử dụng hàm và thay thế tên của hàm đó bằng một tham chiếu con trỏ, tạo roundthành *fp. Tuy nhiên, bạn cần thêm một bộ parens, mà một số người sẽ nói làm cho nó hơi lộn xộn hơn.

Có thể cho rằng, điều này từng dễ dàng hơn trong C ban đầu, thậm chí không có chữ ký hàm, nhưng chúng ta đừng quay lại đó, được chứ?

Nơi nó trở nên đặc biệt khó chịu là tìm ra cách khai báo một hàm lấy làm đối số hoặc trả về một con trỏ cho hàm hoặc cả hai.

Nếu bạn có một chức năng:

void myhandler(int signo);

bạn có thể chuyển nó đến chức năng tín hiệu (3) theo cách này:

signal(SIGHUP, myhandler);

hoặc nếu bạn muốn giữ trình xử lý cũ, thì

old_handler = signal(SIGHUP, new_handler);

đó là khá dễ dàng. Điều khá dễ dàng - không đẹp, cũng không dễ - là nhận được các tuyên bố đúng.

signal(int signo, ???)

Chà, bạn chỉ cần quay lại khai báo hàm và hoán đổi tên để tham chiếu điểm:

signal(int sendsig, void (*hisfunc)(int gotsig));

Vì bạn không khai báo gotsig, bạn có thể thấy dễ đọc hơn nếu bạn bỏ qua:

signal(int sendsig, void (*hisfunc)(int));

Hoặc có thể không. :

Ngoại trừ điều đó là không đủ, bởi vì tín hiệu (3) cũng trả về trình xử lý cũ, như trong:

old_handler = signal(SIGHUP, new_handler);

Vì vậy, bây giờ bạn phải tìm ra cách khai báo tất cả những cái đó.

void (*old_handler)(int gotsig);

là đủ cho biến bạn sẽ gán cho. Lưu ý rằng bạn không thực sự tuyên bố gotsigở đây, chỉ old_handler. Vì vậy, điều này thực sự là đủ:

void (*old_handler)(int);

Điều đó đưa chúng ta đến một định nghĩa chính xác cho tín hiệu (3):

void (*signal(int signo, void (*handler)(int)))(int);

Typedefs để giải cứu

Đến lúc này, tôi nghĩ mọi người sẽ đồng ý rằng đó là một mớ hỗn độn. Đôi khi, tốt hơn là đặt tên cho sự trừu tượng của bạn; thường xuyên Với quyền typedef, điều này trở nên dễ hiểu hơn nhiều:

typedef void (*sig_t) (int);

Bây giờ biến xử lý của riêng bạn trở thành

sig_t old_handler, new_handler;

và tuyên bố của bạn cho tín hiệu (3) trở thành

sig_t signal(int signo, sig_t handler);

đó là bất ngờ dễ hiểu. Loại bỏ các dấu * cũng loại bỏ một số dấu ngoặc đơn khó hiểu (và họ nói rằng các phép ẩn luôn làm cho mọi thứ dễ hiểu hơn - hah!). Cách sử dụng của bạn vẫn giống nhau:

old_handler = signal(SIGHUP, new_handler);

nhưng bây giờ bạn có cơ hội tìm hiểu các tờ khai old_handler, new_handlervà thậm chí signalkhi bạn lần đầu tiên gặp họ hay cần phải viết chúng.

Phần kết luận

Rất ít lập trình viên C, hóa ra, có khả năng tự mình đưa ra các tuyên bố chính xác cho những điều này mà không cần tham khảo tài liệu tham khảo.

Tôi biết, bởi vì chúng tôi đã từng có câu hỏi này trong các câu hỏi phỏng vấn của chúng tôi cho những người làm công việc điều khiển nhân và thiết bị. :) Chắc chắn, chúng tôi đã mất rất nhiều ứng cử viên theo cách đó khi họ bị rơi và bị đốt cháy trên bảng trắng. Nhưng chúng tôi cũng tránh thuê những người tuyên bố rằng họ có kinh nghiệm trước đây trong lĩnh vực này nhưng thực sự không thể làm được việc.

Tuy nhiên, do khó khăn phổ biến này, có lẽ không chỉ hợp lý mà thực sự hợp lý để có một cách để giải quyết tất cả những tuyên bố đó không còn yêu cầu bạn phải là một lập trình viên ba-geek ngồi trên ba sigmas chỉ để sử dụng điều này loại điều thoải mái.


1
Mặc dù vậy, khó theo dõi ... +1 cho nỗ lực mặc dù nó minh họa điểm mà đôi khi rất khó để có được điều đó ngay trong C.
celavek

4

Tôi nghĩ rằng bạn đã bỏ lỡ một số điểm khi bạn tập trung vào bit trái sang phải.

Vấn đề của C và C ++ là ngữ pháp khủng khiếp mà họ có, khó đọc cả (con người) và phân tích cú pháp (công cụ).

Có một ngữ pháp phù hợp hơn (hoặc thường xuyên ) làm cho cả hai dễ dàng hơn. Và phân tích cú pháp dễ dàng hơn có nghĩa là công cụ dễ dàng hơn: hầu hết các công cụ hiện tại không hiểu đúng về C ++, thậm chí không phải là plugin mới nhất của Eclipse khi họ tìm cách phát minh lại bánh xe ... và thất bại, và họ có thể có nhiều người hơn dự án HĐH trung bình.

Vì vậy, bạn có thể đóng đinh nó khi tập trung vào đọc và phân tích cú pháp ... và đó là một vấn đề lớn :)


Đó là một bất ngờ lớn về việc Eclipse vẫn không thể phân tích những thứ như các tuyên bố ở trên. Tại sao họ không sử dụng trình phân tích cú pháp C thực sự như từ gcc?
tchrist

@tchrist: Tôi đã phát hiện hai vấn đề với Eclipse và cả hai dường như được liên kết với các macro. Có lẽ nhiều vấn đề tiền xử lý hơn thế hệ AST.
Matthieu M.
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.