Làm thế nào để mô phỏng backreferences, lookahead và lookbehinds trong trạng thái automata hữu hạn?


26

Tôi đã tạo một lexer và trình phân tích cú pháp biểu thức chính quy đơn giản để lấy một biểu thức chính quy và tạo cây phân tích cú pháp của nó. Tạo một máy tự động trạng thái hữu hạn không xác định từ cây phân tích này tương đối đơn giản cho các biểu thức chính quy cơ bản. Tuy nhiên, tôi dường như không thể quấn đầu xung quanh làm thế nào để mô phỏng phản ứng ngược, nhìn và nhìn.

Từ những gì tôi đọc được trong cuốn sách rồng tím, tôi hiểu rằng để mô phỏng một cái nhìn trong đó biểu thức chính quy được khớp khi và chỉ khi khớp với một biểu thức biểu thức chính quy , bạn tạo ra một hữu hạn không xác định máy tự động trạng thái trong đó được thay thế bằng . Có thể tạo ra một máy tự động trạng thái hữu hạn xác định mà làm như vậy?r/Ss / εrS/ε

Điều gì về việc mô phỏng những cái nhìn và cái nhìn tiêu cực? Tôi thực sự sẽ đánh giá cao nếu bạn liên kết tôi với một tài nguyên mô tả cách thực hiện điều này một cách chi tiết.



Câu trả lời:


21

Trước hết, phản hồi không thể được mô phỏng bởi automata hữu hạn vì chúng cho phép bạn mô tả các ngôn ngữ không thông thường. Ví dụ, ([ab]^*)\1trận đấu , mà không phải là thậm chí bối cảnh tự do.{ww|w{một,b}*}

Nhìn về phía trước và nhìn phía sau không có gì đặc biệt trong thế giới của automata hữu hạn vì chúng ta chỉ khớp với toàn bộ đầu vào ở đây. Do đó, ngữ nghĩa đặc biệt của "chỉ kiểm tra nhưng không tiêu thụ" là vô nghĩa; bạn chỉ cần nối và / hoặc giao nhau kiểm tra và tiêu thụ các biểu thức và sử dụng automata kết quả. Ý tưởng là kiểm tra các biểu thức nhìn về phía trước hoặc nhìn phía sau trong khi bạn "tiêu thụ" đầu vào và lưu trữ kết quả ở trạng thái.

Khi thực hiện regexps, bạn muốn chạy đầu vào thông qua một máy tự động và lấy lại chỉ số bắt đầu và kết thúc của các trận đấu. Đó là một nhiệm vụ rất khác nhau, vì vậy thực sự không có một công trình nào cho automata hữu hạn. Bạn xây dựng thiết bị tự động của mình như thể biểu thức nhìn về phía trước hoặc phía sau đang tiêu tốn và thay đổi chỉ số lưu trữ chỉ mục của bạn. báo cáo phù hợp.

Lấy ví dụ, nhìn phía sau. Chúng ta có thể bắt chước ngữ nghĩa regrec bằng cách thực hiện đồng thời kiểm tra biểu thức chính quy với biểu thức chính quy "khớp tất cả" tiêu thụ ngầm. chỉ từ các trạng thái mà máy tự động của biểu thức nhìn phía sau ở trạng thái cuối cùng mới có thể nhập máy tự động của biểu thức được bảo vệ. Ví dụ: biểu thức chính quy /(?=c)[ab]+/(giả sử là bảng chữ cái đầy đủ) - lưu ý rằng nó dịch sang biểu thức chính quy { a , b , c } c { a , b } + { a , b , c }{một,b,c} - có thể được kết hợp bởi{một,b,c}*c{một,b}+{một,b,c}*

nhập mô tả hình ảnh ở đây
[ nguồn ]

và bạn sẽ phải

  • lưu trữ chỉ mục hiện tại như bất cứ khi nào bạn nhập q 2 (ban đầu hoặc từ q 2 ) vàtôiq2q2
  • báo cáo một trận đấu (tối đa) từ đến chỉ số hiện tại ( - 1 ) bất cứ khi nào bạn nhấn (rời) q 2 .tôi-1q2

Lưu ý cách phần bên trái của máy tự động là máy tự động song song của máy tự động chính tắc cho [abc]*c(lặp lại), tương ứng.

tôijtôij

Lưu ý rằng tính không xác định là cố hữu đối với điều này: tự động chính và nhìn về phía trước / -behind có thể chồng lấp, do đó bạn phải lưu trữ tất cả các chuyển đổi giữa chúng để báo cáo các kết quả khớp sau hoặc quay lại.


11

Tài liệu tham khảo có thẩm quyền về các vấn đề thực tế đằng sau việc triển khai các công cụ regex là một loạt ba bài đăng trên blog của Russ Cox . Như được mô tả ở đó, vì phản hồi làm cho ngôn ngữ của bạn không đều đặn, chúng được thực hiện bằng cách sử dụng quay lui .

Lookahead và lookbehind, giống như nhiều tính năng của công cụ khớp mẫu regex, không hoàn toàn phù hợp với mô hình quyết định liệu một chuỗi có phải là thành viên của ngôn ngữ hay không. Thay vì với các biểu thức chính, chúng ta thường tìm kiếm các chuỗi con trong một chuỗi lớn hơn. "Khớp" là các chuỗi con là thành viên của ngôn ngữ và giá trị trả về là điểm bắt đầu và điểm kết thúc của chuỗi con trong chuỗi lớn hơn.

Điểm của lookahead và lookbehind không phải là quá nhiều để giới thiệu khả năng khớp các ngôn ngữ không thông thường, mà là điều chỉnh nơi động cơ báo cáo điểm bắt đầu và điểm kết thúc của chuỗi con phù hợp.

Tôi đang dựa vào mô tả tại http://www.THER-expressions.info/lookaround.html . Các công cụ regex hỗ trợ tính năng này (Perl, TCL, Python, Ruby, ...) dường như đều dựa trên quay lui (nghĩa là chúng hỗ trợ một bộ ngôn ngữ lớn hơn nhiều so với chỉ các ngôn ngữ thông thường). Họ dường như đang thực hiện tính năng này như một phần mở rộng tương đối "đơn giản" của quay lui, thay vì cố gắng xây dựng automata hữu hạn thực sự để thực hiện nhiệm vụ.

Nhìn tích cực

Cú pháp cho cái nhìn tích cực(?=regex) . Vì vậy, ví dụ chỉ q(?=u)khớp qnếu nó được theo sau u, nhưng không khớp với u. Tôi tưởng tượng họ thực hiện điều này với một biến thể về quay lui. Tạo một FSM cho biểu thức trước cái nhìn tích cực. Khi phù hợp, hãy nhớ nơi nó kết thúc và bắt đầu một FSM mới thể hiện biểu hiện bên trong diện mạo tích cực. Nếu trận đấu đó thì bạn có một "trận đấu", nhưng trận đấu "kết thúc" ngay trước vị trí bắt đầu trận đấu nhìn tích cực.

Phần duy nhất của điều này sẽ khó mà không quay lại là bạn cần nhớ điểm trong đầu vào nơi giao diện bắt đầu và di chuyển băng đầu vào của bạn trở lại vị trí này sau khi bạn hoàn thành khớp.

Cái nhìn tiêu cực

Cú pháp cho cái nhìn tiêu cực(?!regex) . Vì vậy, ví dụ chỉ q(?!u)khớp qnếu nó không được theo sau u. Điều này có thể là qtheo sau bởi một số ký tự khác hoặc một qở cuối chuỗi. Tôi tưởng tượng điều này được thực hiện bằng cách tạo NFA cho biểu thức nhìn, sau đó chỉ thành công nếu NFA không khớp với chuỗi tiếp theo.

Nếu bạn muốn làm điều đó mà không cần dựa vào việc quay lại, bạn có thể phủ nhận NFA của biểu thức nhìn, sau đó xử lý nó giống như cách bạn đối xử với cái nhìn tích cực.

Nhìn tích cực

(?<=)(?=q)uuqqnnn

Bạn có thể thực hiện điều này mà không cần quay lại bằng cách lấy giao điểm của "chuỗi kết thúc bằng regex " với bất kỳ phần nào của biểu thức chính xuất hiện trước toán tử lookbehind. Điều này sẽ rất khó khăn, bởi vì regex của lookbehind có thể cần nhìn lại hơn so với đầu hiện tại của đầu vào.

Cái nhìn tiêu cực

Cú pháp cho cái nhìn tiêu cực(?<!regex) . Vì vậy, ví dụ, (?<!q)ukhớp u, nhưng chỉ khi nó không đi trước q. Vì vậy, nó sẽ phù hợp với utrong umbrellautrong doubt, nhưng không phải utrong quick. Một lần nữa, điều này dường như được thực hiện bằng cách tính toán độ dài của regex , sao lưu nhiều ký tự đó, thử nghiệm cho trận đấu với regex , nhưng bây giờ thất bại toàn bộ trận đấu nếu giao diện khớp.

Bạn có thể thực hiện điều này mà không cần quay lại bằng cách lấy sự phủ định của regex và sau đó thực hiện tương tự như bạn sẽ làm cho cái nhìn tích cực.


5

Ít nhất là cho phản hồi, điều này là không thể. Ví dụ, regex (.*)\1đại diện cho một ngôn ngữ không thường xuyên. Điều đó có nghĩa là không thể tạo ra một máy tự động hữu hạn (xác định hay không) có thể nhận ra ngôn ngữ này. Nếu bạn muốn chứng minh điều này một cách chính thức, bạn có thể sử dụng bổ đề bơm .


4

Tôi đã tự mình xem xét vấn đề này và bạn sẽ có thể thực hiện giao diện bằng cách sử dụng Máy tự động hữu hạn xen kẽ . Khi bạn gặp lookahead, bạn không nhất thiết phải chạy cả lookahead và phần còn lại của biểu thức, chỉ chấp nhận nếu cả hai đường dẫn chấp nhận. Bạn có thể chuyển đổi AFA thành NFA với độ nổ hợp lý (và do đó thành DFA), mặc dù tôi chưa xác minh rằng cấu trúc rõ ràng chơi độc đáo với các nhóm chụp.

Nhìn bề rộng cố định nên hoàn toàn có thể mà không cần quay lại. Gọi n là chiều rộng. Bắt đầu từ điểm trong NFA của bạn nơi bắt đầu nhìn, bạn sẽ tách ra các trạng thái nhìn về phía sau để mọi đường dẫn vào giao diện kết thúc với n trạng thái chỉ có các trạng thái chỉ đi vào giao diện. Sau đó, thêm lookahead vào đầu các trạng thái đó (và lập tức biên dịch sơ đồ con từ AFA sang NFA nếu muốn).

Backreferences, như những người khác đã đề cập, không thường xuyên, vì vậy chúng không thể được thực hiện bởi một máy tự động hữu hạn. Trên thực tế, họ đã hoàn thành NP. Trong quá trình triển khai tôi đang thực hiện, nhanh chóng có / không khớp là điều tối quan trọng, vì vậy tôi đã chọn không thực hiện phản hồi nào cả.

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.