Dưới đây là phần trình diễn yêu thích (hiện tại) của tôi về lý do phân tích cú pháp C ++ (có thể) Turing-Complete , vì nó hiển thị một chương trình đúng về mặt cú pháp khi và chỉ khi một số nguyên cho trước là số nguyên tố.
Vì vậy, tôi khẳng định rằng C ++ không có ngữ cảnh cũng không nhạy cảm với ngữ cảnh .
Nếu bạn cho phép các chuỗi ký hiệu tùy ý ở cả hai mặt của bất kỳ sản phẩm nào, bạn sẽ tạo ra một ngữ pháp Loại 0 ("không giới hạn") trong hệ thống phân cấp Chomsky , mạnh hơn ngữ pháp nhạy cảm theo ngữ cảnh; ngữ pháp không giới hạn là Turing-Complete. Một ngữ pháp nhạy cảm theo ngữ cảnh (Loại 1) cho phép nhiều biểu tượng ngữ cảnh ở phía bên trái của sản phẩm, nhưng cùng một bối cảnh phải xuất hiện ở phía bên phải của sản phẩm (do đó có tên là "nhạy cảm ngữ cảnh"). [1] Các ngữ pháp nhạy cảm theo ngữ cảnh tương đương với các máy Turing giới hạn tuyến tính .
Trong chương trình ví dụ, tính toán nguyên tố có thể được thực hiện bằng máy Turing giới hạn tuyến tính, do đó nó không hoàn toàn chứng minh tính tương đương của Turing, nhưng phần quan trọng là trình phân tích cú pháp cần thực hiện tính toán để thực hiện phân tích cú pháp. Nó có thể là bất kỳ tính toán rõ ràng nào dưới dạng khởi tạo mẫu và có mọi lý do để tin rằng khởi tạo mẫu C ++ là Turing-Complete. Xem, ví dụ, bài viết năm 2003 của Todd L. Veldhuizen .
Bất kể, C ++ có thể được phân tích cú pháp bằng máy tính, do đó chắc chắn nó có thể được phân tích cú pháp bằng máy Turing. Do đó, một ngữ pháp không hạn chế có thể nhận ra nó. Trên thực tế, việc viết một ngữ pháp như vậy sẽ không thực tế, đó là lý do tại sao tiêu chuẩn không cố gắng làm như vậy. (Xem bên dưới.)
Vấn đề với "sự mơ hồ" của một số biểu hiện nhất định chủ yếu là cá trích đỏ. Để bắt đầu, sự mơ hồ là một tính năng của một ngữ pháp cụ thể, không phải là một ngôn ngữ. Ngay cả khi một ngôn ngữ có thể được chứng minh là không có ngữ pháp rõ ràng, nếu ngôn ngữ đó có thể được nhận ra bởi một ngữ pháp không ngữ cảnh, thì nó không có ngữ cảnh. Tương tự, nếu nó không thể được nhận ra bởi một ngữ pháp không ngữ cảnh nhưng nó có thể được nhận ra bởi một ngữ pháp nhạy cảm theo ngữ cảnh, nó nhạy cảm theo ngữ cảnh. Sự mơ hồ không liên quan.
Nhưng trong bất kỳ sự kiện nào, như dòng 21 (tức là auto b = foo<IsPrime<234799>>::typen<1>();
) trong chương trình bên dưới, các biểu thức hoàn toàn không mơ hồ; chúng chỉ đơn giản được phân tích cú pháp khác nhau tùy thuộc vào ngữ cảnh. Trong cách diễn đạt đơn giản nhất của vấn đề, phạm trù cú pháp của một số định danh nhất định phụ thuộc vào cách chúng được khai báo (ví dụ: loại và hàm), có nghĩa là ngôn ngữ chính thức sẽ phải nhận ra thực tế là hai chuỗi có độ dài tùy ý trong cùng một chương trình là giống hệt nhau (khai báo và sử dụng). Điều này có thể được mô hình hóa bằng ngữ pháp "sao chép", đó là ngữ pháp nhận ra hai bản sao chính xác liên tiếp của cùng một từ. Thật dễ dàng để chứng minh với bổ đề bơmrằng ngôn ngữ này không có ngữ cảnh. Có thể sử dụng ngữ pháp nhạy với ngữ cảnh cho ngôn ngữ này và ngữ pháp Loại 0 được cung cấp trong câu trả lời cho câu hỏi này: /math/163830/context-sensitive-grammar-for-the- ngôn ngữ sao chép .
Nếu một người cố gắng viết một ngữ pháp nhạy cảm theo ngữ cảnh (hoặc không bị hạn chế) để phân tích C ++, thì nó hoàn toàn có thể lấp đầy vũ trụ bằng những nét vẽ nguệch ngoạc. Viết một máy Turing để phân tích C ++ sẽ là một công việc không thể bằng. Ngay cả việc viết một chương trình C ++ cũng khó, và theo như tôi biết thì không có gì được chứng minh là đúng. Đây là lý do tại sao tiêu chuẩn không cố gắng cung cấp một ngữ pháp chính thức hoàn chỉnh và tại sao nó chọn viết một số quy tắc phân tích cú pháp bằng tiếng Anh kỹ thuật.
Những gì trông giống như một ngữ pháp chính thức trong tiêu chuẩn C ++ không phải là định nghĩa chính thức hoàn chỉnh về cú pháp của ngôn ngữ C ++. Nó thậm chí không phải là định nghĩa chính thức hoàn chỉnh của ngôn ngữ sau khi tiền xử lý, có thể dễ dàng chính thức hóa hơn. (Tuy nhiên, đó không phải là ngôn ngữ: ngôn ngữ C ++ như được định nghĩa bởi tiêu chuẩn bao gồm bộ tiền xử lý và hoạt động của bộ tiền xử lý được mô tả bằng thuật toán vì sẽ rất khó để mô tả trong bất kỳ hình thức ngữ pháp nào. Nó nằm trong phần đó. của tiêu chuẩn nơi phân tách từ vựng được mô tả, bao gồm các quy tắc nơi nó phải được áp dụng nhiều lần.)
Các ngữ pháp khác nhau (hai ngữ pháp chồng chéo để phân tích từ vựng, một ngữ pháp diễn ra trước khi tiền xử lý và ngữ pháp khác, nếu cần, sau đó, cộng với ngữ pháp "cú pháp") được thu thập trong Phụ lục A, với ghi chú quan trọng này (nhấn mạnh thêm):
Tóm tắt cú pháp C ++ này nhằm mục đích hỗ trợ cho việc hiểu. Nó không phải là một tuyên bố chính xác của ngôn ngữ . Cụ thể, ngữ pháp được mô tả ở đây chấp nhận một siêu cấu trúc C ++ hợp lệ . Các quy tắc định hướng (6.8, 7.1, 10.2) phải được áp dụng để phân biệt biểu thức với khai báo. Hơn nữa, các quy tắc kiểm soát truy cập, sự mơ hồ và loại phải được sử dụng để loại bỏ các cấu trúc có giá trị về mặt cú pháp nhưng vô nghĩa.
Cuối cùng, đây là chương trình đã hứa. Dòng 21 đúng về mặt cú pháp khi và chỉ khi N in IsPrime<N>
là số nguyên tố. Mặt khác, typen
là một số nguyên, không phải là một mẫu, do đó typen<1>()
được phân tích cú pháp (typen<1)>()
là không chính xác về mặt cú pháp vì ()
không phải là một biểu thức hợp lệ về mặt cú pháp.
template<bool V> struct answer { answer(int) {} bool operator()(){return V;}};
template<bool no, bool yes, int f, int p> struct IsPrimeHelper
: IsPrimeHelper<p % f == 0, f * f >= p, f + 2, p> {};
template<bool yes, int f, int p> struct IsPrimeHelper<true, yes, f, p> { using type = answer<false>; };
template<int f, int p> struct IsPrimeHelper<false, true, f, p> { using type = answer<true>; };
template<int I> using IsPrime = typename IsPrimeHelper<!(I&1), false, 3, I>::type;
template<int I>
struct X { static const int i = I; int a[i]; };
template<typename A> struct foo;
template<>struct foo<answer<true>>{
template<int I> using typen = X<I>;
};
template<> struct foo<answer<false>>{
static const int typen = 0;
};
int main() {
auto b = foo<IsPrime<234799>>::typen<1>(); // Syntax error if not prime
return 0;
}
[1] Để nói một cách kỹ thuật hơn, mọi sản phẩm trong một ngữ pháp nhạy cảm theo ngữ cảnh phải có dạng:
αAβ → αγβ
trong đó A
không phải là một thiết bị đầu cuối và α
, β
có thể là các chuỗi trống của các ký hiệu ngữ pháp và γ
là một chuỗi không trống. (Ký hiệu ngữ pháp có thể là thiết bị đầu cuối hoặc không phải thiết bị đầu cuối).
Điều này có thể được đọc như A → γ
chỉ trong bối cảnh [α, β]
. Trong ngữ pháp không có ngữ cảnh (Loại 2) α
và β
phải trống.
Hóa ra bạn cũng có thể hạn chế ngữ pháp với hạn chế "đơn điệu", trong đó mọi sản phẩm phải có dạng:
α → β
trong đó |α| ≥ |β| > 0
( |α|
có nghĩa là "chiều dài của α
")
Có thể chứng minh rằng tập hợp các ngôn ngữ được nhận biết bởi các ngữ pháp đơn điệu hoàn toàn giống với tập hợp các ngôn ngữ được các ngữ pháp nhạy cảm theo ngữ cảnh nhận ra và thường thì việc chứng minh dựa trên các ngữ pháp đơn điệu sẽ dễ dàng hơn. Do đó, khá phổ biến khi thấy "nhạy cảm theo ngữ cảnh" được sử dụng như thể nó có nghĩa là "đơn điệu".