Thuật ngữ "tính trực giao" là thuật ngữ của một giáo dân cho một khái niệm toán học chính xác: các thuật ngữ ngôn ngữ tạo thành một đại số ban đầu (tra cứu nó trong Wikipedia).
Về cơ bản nó có nghĩa là "có một sự tương ứng 1-1 giữa cú pháp và ý nghĩa". Điều này có nghĩa là: có chính xác một cách để diễn đạt sự vật và, nếu bạn có thể đặt một số biểu thức ở một vị trí cụ thể, thì bạn cũng có thể đặt bất kỳ biểu thức nào khác ở đó.
Một cách khác để suy nghĩ về "trực giao" là cú pháp tuân theo nguyên tắc thay thế. Ví dụ: nếu bạn có một câu lệnh có một vị trí cho một biểu thức, thì bất kỳ biểu thức nào cũng có thể được đặt ở đó và kết quả vẫn là một chương trình hợp lệ về mặt cú pháp. Hơn nữa, nếu bạn thay thế
Tôi muốn nhấn mạnh rằng "ý nghĩa" không bao hàm kết quả tính toán. Rõ ràng, 1 + 2 và 2 + 1 đều bằng 3. Tuy nhiên các thuật ngữ là khác biệt và ngụ ý một phép tính khác nhau ngay cả khi nó có cùng kết quả. Ý nghĩa là khác nhau, giống như hai thuật toán sắp xếp là khác nhau.
Bạn có thể đã nghe nói về "cây cú pháp trừu tượng" (AST). Từ "trừu tượng" ở đây có nghĩa chính xác là "trực giao". Về mặt kỹ thuật hầu hết AST không thực sự trừu tượng!
Có lẽ bạn đã nghe nói về ngôn ngữ lập trình "C"? Ký hiệu loại C không trừu tượng. Xem xét:
int f(int);
Vì vậy, đây là một loại khai báo hàm trả về int
. Loại con trỏ của hàm này được cho bởi:
int (*)(int)
Lưu ý, bạn không thể viết loại hàm! Ký hiệu loại C hút thời gian lớn! Nó không trừu tượng. Nó không trực giao. Bây giờ, giả sử chúng ta muốn tạo một hàm chấp nhận kiểu trên thay vì int:
int (*) ( int (*)(int) )
Tất cả đều ổn .. nhưng .. nếu chúng ta muốn trả lại thay thế:
int (*)(int) (*) (int)
Ái chà! Không hợp lệ. Cho phép thêm parens:
(int (*)(int)) (*) (int)
Ái chà! Điều đó cũng không hoạt động. Chúng ta phải làm điều này (đó là cách duy nhất!):
typedef int (intintfunc*) (int);
intintfunc (*)(int)
Bây giờ nó ổn, nhưng phải sử dụng một typedef ở đây là xấu. C hút. Nó không trừu tượng. Nó không trực giao. Đây là cách bạn làm điều này trong ML, đó là:
int -> (int -> int)
Chúng tôi lên án C ở cấp độ cú pháp.
Ok, bây giờ hãy để flog C ++. Chúng tôi có thể khắc phục sự ngu ngốc ở trên bằng các mẫu và nhận được ký hiệu ML (ít nhiều):
fun<int, int>
fun< fun<int,int>, int>
nhưng hệ thống loại thực tế bị thiếu sót về cơ bản bởi các tham chiếu: nếu T
là một loại, thì là T&
một loại? Câu trả lời rất khó hiểu: ở cấp độ cú pháp, nếu bạn có loại U = T &, thì U & được cho phép nhưng nó chỉ có nghĩa là T &: tham chiếu đến tham chiếu là tham chiếu ban đầu. Thật tệ Nó phá vỡ yêu cầu duy nhất về mặt ngữ nghĩa. Tệ hơn: T & & không được phép về mặt cú pháp: điều này phá vỡ nguyên tắc thay thế. Vì vậy, các tham chiếu C ++ phá vỡ tính trực giao theo hai cách khác nhau, tùy thuộc vào thời gian ràng buộc (phân tích phân tích hoặc phân tích loại). Nếu bạn muốn hiểu làm thế nào để làm điều này đúng .. không có vấn đề gì với con trỏ!
Hầu như không có ngôn ngữ thực sự là trực giao. Ngay cả Scheme, giả vờ rõ ràng tuyệt vời của biểu thức, không. Tuy nhiên, nhiều ngôn ngữ tốt có thể được đánh giá là có "cơ sở tính năng trực giao hợp lý" và đó là một khuyến nghị tốt cho một ngôn ngữ, áp dụng cả cho cú pháp và ngữ nghĩa cơ bản.