Vấn đề là, thực sự không có nhiều thời gian về mặt mã hóa chức năng. Dưới đây là các tùy chọn chính:
Viết lại thuật ngữ: bạn lưu trữ các hàm như các cây cú pháp trừu tượng của chúng (hoặc một số mã hóa của chúng. Khi bạn gọi một hàm, bạn duyệt thủ công cây cú pháp để thay thế các tham số của nó bằng đối số. Điều này rất dễ, nhưng không hiệu quả về mặt thời gian và không gian .
Đóng: bạn có một số cách biểu diễn một hàm, có thể là cây cú pháp, nhiều khả năng là mã máy. Và trong các hàm này, bạn tham chiếu các đối số của mình bằng cách tham chiếu theo một cách nào đó. Nó có thể là một con trỏ bù, nó có thể là một số nguyên hoặc chỉ số De Bruijn, nó có thể là một tên. Sau đó, bạn biểu diễn một hàm dưới dạng đóng : hàm "hướng dẫn" (cây, mã, v.v.) được ghép nối với cấu trúc dữ liệu chứa tất cả các biến miễn phí của hàm. Khi một hàm thực sự được áp dụng, bằng cách nào đó nó biết cách tìm kiếm các biến miễn phí trong cấu trúc dữ liệu của nó, sử dụng các môi trường, số học con trỏ, v.v.
Tôi chắc chắn có các tùy chọn khác, nhưng đây là những tùy chọn cơ bản và tôi nghi ngờ hầu hết các tùy chọn khác sẽ là một biến thể hoặc tối ưu hóa cấu trúc đóng cơ bản.
Vì vậy, về mặt hiệu suất, các bao đóng hầu như thực hiện tốt hơn so với viết lại thuật ngữ. Trong số các biến thể, cái nào tốt hơn? Điều đó phụ thuộc rất nhiều vào ngôn ngữ và kiến trúc của bạn, nhưng tôi nghi ngờ "mã máy có cấu trúc chứa các vars miễn phí" là hiệu quả nhất. Nó có mọi thứ mà hàm cần (hướng dẫn và giá trị) và không có gì nữa, và việc gọi không kết thúc thực hiện các giao dịch lớn.
Tôi quan tâm đến cả thuật toán mã hóa hiện tại mà các ngôn ngữ chức năng phổ biến (Haskell, ML) sử dụng
Tôi không phải là một chuyên gia, nhưng tôi 99% hầu hết các hương vị ML sử dụng một số biến thể của các bao đóng mà tôi mô tả, mặc dù có khả năng tối ưu hóa. Xem điều này cho một quan điểm (có thể lỗi thời).
Haskell làm một cái gì đó phức tạp hơn một chút vì đánh giá lười biếng: nó sử dụng Viết lại biểu đồ không có thẻ xoay tròn .
và cũng là một trong những hiệu quả nhất có thể đạt được.
Hiệu quả nhất là gì? Không có triển khai nào sẽ hiệu quả nhất trên tất cả các đầu vào, vì vậy bạn có được các triển khai có hiệu quả trung bình, nhưng mỗi triển khai sẽ vượt trội trong các kịch bản khác nhau. Vì vậy, không có thứ hạng nhất định của hầu hết hoặc kém hiệu quả nhất.
Không có phép thuật ở đây. Để lưu trữ một chức năng, bạn cần lưu trữ các giá trị miễn phí của nó bằng cách nào đó, nếu không, bạn đang mã hóa ít thông tin hơn chính chức năng đó. Có thể bạn có thể tối ưu hóa một số giá trị miễn phí với đánh giá một phần nhưng điều đó có rủi ro cho hiệu suất và bạn phải cẩn thận để đảm bảo rằng điều này luôn dừng lại.
Và, có lẽ bạn có thể sử dụng một số loại nén hoặc thuật toán thông minh để đạt được hiệu quả không gian. Nhưng sau đó, bạn hoặc là giao dịch thời gian cho không gian, hoặc bạn đang ở trong tình huống bạn đã tối ưu hóa cho một số trường hợp và làm chậm lại cho những người khác.
Bạn có thể tối ưu hóa cho các trường hợp thông thường, nhưng những gì các trường hợp thông thường là có thể thay đổi vào ngôn ngữ, diện tích ứng dụng, vv Các loại mã đó là nhanh chóng cho một trò chơi video (số crunching, vòng chặt chẽ với sự đóng góp lớn) có lẽ là khác nhau hơn những gì nhanh cho trình biên dịch (duyệt qua cây, danh sách công việc, v.v.).
Điểm thưởng: Có mã hóa như vậy mà ánh xạ chức năng mã hóa số nguyên sang số nguyên gốc (ngắn, int vv trong C). Nó thậm chí có thể?
Không, điều này là không thể. Vấn đề là phép tính lambda không cho phép bạn xem xét các điều khoản. Khi một hàm lấy một đối số có cùng kiểu với một chữ số Church, nó cần có khả năng gọi nó, mà không kiểm tra định nghĩa chính xác của chữ số đó. Đó là điều với mã hóa của Giáo hội: điều duy nhất bạn có thể làm với họ là gọi cho họ, và bạn có thể mô phỏng mọi thứ hữu ích với điều này, nhưng không phải là không có chi phí.
Quan trọng hơn, các số nguyên chiếm mọi mã hóa nhị phân có thể. Vì vậy, nếu lambdas được đại diện như số nguyên của họ, bạn sẽ không có cách nào để đại diện cho lambdas không số nhà thờ! Hoặc, bạn sẽ giới thiệu một lá cờ để biểu thị xem lambda có phải là một chữ số hay không, nhưng sau đó, bất kỳ hiệu quả nào bạn muốn có thể được đưa ra ngoài cửa sổ.
EDIT: Kể từ khi viết bài này, tôi đã biết đến một lựa chọn thứ ba để thực hiện các chức năng bậc cao hơn: khử chức năng . Ở đây, mọi lời gọi hàm đều biến thành một switch
câu lệnh lớn , tùy thuộc vào sự trừu tượng hóa lambda nào được đưa ra làm hàm. Sự cân bằng ở đây là đó là một sự chuyển đổi toàn bộ chương trình: bạn không thể biên dịch các phần riêng biệt và sau đó liên kết với nhau theo cách này, vì bạn cần phải có bộ trừu tượng lambda hoàn chỉnh trước thời hạn.