Theo thuật ngữ của giáo dân, những gì còn lại là đệ quy?


11

Theo một trang trên code.google.com, "đệ quy bên trái" được định nghĩa như sau:

Đệ quy bên trái chỉ đề cập đến bất kỳ nội quy đệ quy nào, khi nó tạo ra một hình thức cảm tính có chứa chính nó, bản sao mới của chính nó xuất hiện ở bên trái của quy tắc sản xuất.

Wikipedia cung cấp hai định nghĩa khác nhau:

  1. Về mặt ngữ pháp không ngữ cảnh, một r không phải đầu cuối được đệ quy trái nếu biểu tượng ngoài cùng bên trái trong bất kỳ sản phẩm nào của r ('thay thế') ngay lập tức (đệ quy trực tiếp / ngay lập tức) hoặc thông qua một số đầu cuối khác định nghĩa (gián tiếp / ẩn đệ quy trái) viết lại thành r một lần nữa.

  2. "Một ngữ pháp là đệ quy trái nếu chúng ta có thể tìm thấy một số A không phải là thiết bị đầu cuối A mà cuối cùng sẽ lấy được một hình thức cảm tính với chính nó là biểu tượng bên trái."

Tôi chỉ mới bắt đầu với việc sáng tạo ngôn ngữ ở đây và tôi đang làm việc đó trong thời gian rảnh rỗi. Tuy nhiên, khi nói đến việc chọn một trình phân tích cú pháp ngôn ngữ, liệu đệ quy trái có được trình phân tích cú pháp này hỗ trợ hay trình phân tích cú pháp đó là một vấn đề ngay lập tức xuất hiện trước và trung tâm. Tìm kiếm các thuật ngữ như "hình thức cảm tính" chỉ dẫn đến danh sách biệt ngữ xa hơn, nhưng sự phân biệt của đệ quy "trái" hầu như phải là một cái gì đó rất đơn giản. Làm ơn hãy phiên dịch?

Câu trả lời:


19

Một quy tắc Rlà đệ quy trái nếu, để tìm hiểu xem có Rkhớp hay không , trước tiên bạn phải tìm hiểu xem có Rkhớp không. Điều này xảy ra khi Rxuất hiện, trực tiếp hoặc gián tiếp, là thuật ngữ đầu tiên trong một số sản xuất của chính nó.

Hãy tưởng tượng một phiên bản đồ chơi của ngữ pháp cho các biểu thức toán học, chỉ với phép cộng và phép nhân để tránh phân tâm:

Expression ::= Multiplication '+' Expression
            || Multiplication

Multiplication ::= Term '*' Term
                 || Term

Term ::= Number | Variable

Như đã viết, không có đệ quy trái ở đây - chúng ta có thể chuyển ngữ pháp này cho một trình phân tích cú pháp gốc đệ quy.

Nhưng giả sử bạn đã cố viết nó theo cách này:

Expression ::= Expression '*' Expression
            || Expression '+' Expression
            || Term

Term ::= Number | Variable

Đây là một ngữ pháp và một số trình phân tích cú pháp có thể đối phó với nó, nhưng trình phân tích cú pháp gốc đệ quy và trình phân tích cú pháp LL không thể - bởi vì quy tắc Expressionbắt đầu từ Expressionchính nó. Rõ ràng là tại sao trong một trình phân tích cú pháp gốc đệ quy, điều này dẫn đến đệ quy không giới hạn mà không thực sự tiêu thụ bất kỳ đầu vào nào.

Nó không quan trọng cho dù quy tắc đề cập đến chính nó trực tiếp hay gián tiếp; nếu Acó một giải pháp thay thế bắt đầu bằng BBcó một giải pháp thay thế bắt đầu bằng A, sau đó ABcả hai đệ quy gián tiếp trái, và trong một trình phân tích cú pháp gốc đệ quy, các hàm khớp của chúng sẽ dẫn đến đệ quy lẫn nhau vô tận.


Vì vậy, trong ví dụ thứ hai, nếu bạn thay đổi điều đầu tiên sau ::=từ Expressionsang Termvà nếu bạn làm tương tự sau lần đầu tiên ||, nó sẽ không còn được đệ quy nữa? Nhưng nếu bạn chỉ làm điều đó sau đó ::=, nhưng không ||, nó vẫn sẽ được đệ quy?
Panzercrisis

Có vẻ như bạn đang nói rằng rất nhiều trình phân tích cú pháp đi từ trái sang phải, dừng lại ở mọi biểu tượng và đánh giá nó một cách đệ quy tại chỗ. Trong trường hợp này, nếu cái đầu tiên Expressionđược tắt Term, cả sau ::=và sau cái đầu tiên ||, mọi thứ sẽ ổn; bởi vì sớm hay muộn, nó sẽ chạy vào cái gì đó là không phải là một Numberhay một Variable, do đó việc có thể để xác định cái gì đó không phải là một Expressionmà không cần thực hiện thêm ...
Panzercrisis

... Nhưng nếu một trong hai người vẫn bắt đầu Expression, nó có khả năng sẽ tìm thấy thứ gì đó không phải là một Term, và nó sẽ tiếp tục kiểm tra xem mọi thứ có Expressionlặp đi lặp lại không. Có phải đây không?
Panzercrisis

1
@Panzercrisis nhiều hay ít. Bạn thực sự cần phải tìm kiếm ý nghĩa của LL, LR và các trình phân tích cú pháp gốc đệ quy.
hobbs

Điều này là chính xác về mặt kỹ thuật, nhưng có lẽ không đủ đơn giản (thuật ngữ của giáo dân). Tôi cũng sẽ nói thêm rằng trong thực tế, các trình phân tích LL thường có khả năng phát hiện đệ quy và tránh nó (có khả năng loại bỏ các chuỗi bị tước có giá trị trong quy trình), cũng như thực tế là trong thực tế hầu hết các ngôn ngữ lập trình đều có ngữ pháp được định nghĩa trong như một cách để tránh đệ quy vô hạn.

4

Tôi sẽ cố gắng đưa nó vào điều khoản của giáo dân.

Nếu bạn nghĩ về cây phân tích cú pháp (không phải AST, mà là sự truy cập và mở rộng của trình phân tích cú pháp), thì đệ quy trái sẽ dẫn đến một cây mọc ngược và xuôi. Đệ quy phải hoàn toàn ngược lại.

Ví dụ, một ngữ pháp phổ biến trong trình biên dịch là một danh sách các mục. Hãy lấy một danh sách các chuỗi ("đỏ", "xanh", "xanh") và phân tích nó. Tôi có thể viết ngữ pháp một vài cách. Các ví dụ sau đây là đệ quy trực tiếp trái hoặc phải, tương ứng:

arg_list:                           arg_list:
      STRING                              STRING
    | arg_list ',' STRING               | STRING ',' arg_list 

Những cây cho những phân tích này:

         (arg_list)                       (arg_list)
          /      \                         /      \
      (arg_list)  BLUE                  RED     (arg_list)
       /       \                                 /      \
   (arg_list) GREEN                          GREEN    (arg_list)
    /                                                  /
 RED                                                BLUE

Lưu ý làm thế nào nó phát triển theo hướng đệ quy.

Đây thực sự không phải là một vấn đề, bạn có thể muốn viết một ngữ pháp đệ quy trái ... nếu công cụ trình phân tích cú pháp của bạn có thể xử lý nó. Trình phân tích cú pháp từ dưới lên xử lý nó tốt. Vì vậy, có thể phân tích cú pháp LL hiện đại hơn. Vấn đề với các ngữ pháp đệ quy không phải là đệ quy, đó là đệ quy mà không tiến bộ trình phân tích cú pháp, hoặc, đệ quy mà không tiêu tốn mã thông báo. Nếu chúng tôi luôn tiêu thụ ít nhất 1 mã thông báo khi chúng tôi tái diễn, cuối cùng chúng tôi sẽ đạt đến cuối phân tích. Đệ quy trái được định nghĩa là đệ quy mà không tiêu thụ, đó là một vòng lặp vô hạn.

Hạn chế này hoàn toàn là một chi tiết triển khai thực hiện một ngữ pháp với trình phân tích cú pháp LL từ trên xuống ngây thơ (trình phân tích cú pháp gốc đệ quy). Nếu bạn muốn gắn bó với các ngữ pháp đệ quy trái, bạn có thể xử lý nó bằng cách viết lại sản xuất để tiêu thụ ít nhất 1 mã thông báo trước khi đệ quy, vì vậy điều này đảm bảo chúng tôi không bao giờ bị kẹt trong vòng lặp không sản xuất. Đối với bất kỳ quy tắc ngữ pháp nào là đệ quy trái, chúng ta có thể viết lại nó bằng cách thêm một quy tắc trung gian làm phẳng ngữ pháp thành một cấp độ nhìn, tiêu thụ mã thông báo giữa các sản phẩm đệ quy. (LƯU Ý: Tôi không nói đây là cách duy nhất hoặc cách ưa thích để viết lại ngữ pháp, chỉ nêu ra quy tắc khái quát. Trong ví dụ đơn giản này, tùy chọn tốt nhất là sử dụng hình thức đệ quy đúng). Vì cách tiếp cận này được khái quát hóa, một trình tạo phân tích cú pháp có thể thực hiện nó mà không liên quan đến lập trình viên (về mặt lý thuyết). Trong thực tế, tôi tin rằng ANTLR 4 bây giờ làm điều đó.

Đối với ngữ pháp ở trên, việc thực hiện LL hiển thị đệ quy bên trái sẽ trông như thế này. Trình phân tích cú pháp sẽ bắt đầu với việc dự đoán một danh sách ...

bool match_list()
{
    if(lookahead-predicts-something-besides-comma) {
       match_STRING();
    } else if(lookahead-is-comma) {
       match_list();   // left-recursion, infinite loop/stack overflow
       match(',');
       match_STRING();
    } else {
       throw new ParseException();
    }
}

Trong thực tế, những gì chúng ta đang thực sự giải quyết là "thực hiện ngây thơ", tức là. ban đầu chúng tôi đưa ra một câu đã cho, sau đó gọi đệ quy hàm cho dự đoán đó và hàm đó gọi một cách ngây thơ một lần nữa.

Các trình phân tích cú pháp từ dưới lên không có vấn đề về các quy tắc đệ quy theo một trong hai hướng, bởi vì chúng không lặp lại phần đầu của câu, chúng hoạt động bằng cách đặt câu lại với nhau.

Đệ quy trong một ngữ pháp chỉ là một vấn đề nếu chúng ta sản xuất từ ​​trên xuống, tức là. trình phân tích cú pháp của chúng tôi hoạt động bằng cách "mở rộng" dự đoán của chúng tôi khi chúng tôi tiêu thụ mã thông báo. Nếu thay vì mở rộng, chúng tôi thu gọn (sản phẩm bị "giảm"), như trong trình phân tích cú pháp từ dưới lên LALR (Yacc / Bison), thì đệ quy của một trong hai bên không phải là vấn đề.

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.