Sự cố khi thực hiện đóng trong cài đặt phi chức năng


18

Trong các ngôn ngữ lập trình, đóng cửa là một tính năng phổ biến và thường được mong muốn. Wikipedia nói (nhấn mạnh của tôi):

Trong khoa học máy tính, bao đóng (...) là một hàm cùng với môi trường tham chiếu cho các biến không cục bộ của hàm đó. Một bao đóng cho phép một hàm truy cập các biến ngoài phạm vi từ vựng ngay lập tức của nó.

Vì vậy, một bao đóng thực chất là một giá trị hàm (nặc danh?) Có thể sử dụng các biến ngoài phạm vi của chính nó. Theo kinh nghiệm của tôi, điều này có nghĩa là nó có thể truy cập các biến có phạm vi tại điểm định nghĩa của nó.

Trong thực tế, khái niệm này dường như đang chuyển hướng, mặc dù, ít nhất là bên ngoài lập trình chức năng. Các ngôn ngữ khác nhau thực hiện các ngữ nghĩa khác nhau, thậm chí dường như có những cuộc chiến tranh về. Nhiều lập trình viên dường như không biết đóng cửa là gì, xem chúng ít hơn các hàm ẩn danh.

Ngoài ra, dường như tồn tại những rào cản lớn khi thực hiện đóng cửa. Đáng chú ý nhất, Java 7 được cho là bao gồm chúng nhưng tính năng này đã bị đẩy trở lại bản phát hành trong tương lai.

Tại sao đóng cửa rất khó (để hiểu và) để nhận ra? Đây là một câu hỏi quá rộng và mơ hồ, vì vậy hãy để tôi tập trung vào nó nhiều hơn với những câu hỏi liên kết với nhau:

  • Có vấn đề gì với việc thể hiện sự khép kín trong các hình thức ngữ nghĩa thông thường (bước nhỏ, bước lớn, ...) không?
  • Là các hệ thống loại hiện có không phù hợp cho việc đóng cửa và không thể mở rộng dễ dàng?
  • Có vấn đề gì khi đưa các bao đóng phù hợp với bản dịch thủ tục dựa trên ngăn xếp truyền thống không?

Lưu ý rằng câu hỏi liên quan chủ yếu đến các ngôn ngữ thủ tục, hướng đối tượng và kịch bản nói chung. Theo tôi biết, ngôn ngữ chức năng không có bất kỳ vấn đề.


Câu hỏi hay. Việc đóng đã được triển khai trong Scala và Martin Oderky đã viết trình biên dịch Java 1.5, vì vậy không hiểu tại sao chúng không có trong Java 7. C # có chúng. (Tôi sẽ cố gắng viết một câu trả lời tốt hơn sau.)
Dave Clarke

4
Các ngôn ngữ chức năng không tinh khiết như Lisp và ML phù hợp với việc đóng cửa tốt, vì vậy không thể có một lý do ngữ nghĩa nội tại nào để chúng có vấn đề.
Gilles 'SO- ngừng trở thành ác quỷ'

Tôi bao gồm các mục bởi vì tôi đã đấu tranh để tưởng tượng một ngữ nghĩa bước nhỏ có thể trông như thế nào đối với việc đóng cửa. Rất có thể là việc đóng cửa trong bản thân họ không phải là một vấn đề nhưng bao gồm chúng trong một ngôn ngữ không được thiết kế với họ trong tâm trí là khó khăn.
Raphael

1
Hãy xem pdfs.semanticscholar.org/73a2/ Từ - Các tác giả Lua đã thực hiện một cách rất thông minh và thảo luận về các vấn đề chung về việc thực hiện đóng cửa
Bulat

Câu trả lời:


10

Tôi có thể hướng bạn đến trang wikipedia của Funarg không? Ít nhất đây là cách người biên dịch sử dụng để tham chiếu vấn đề thực hiện đóng.

Vì vậy, một bao đóng thực chất là một giá trị hàm (nặc danh?) Có thể sử dụng các biến ngoài phạm vi của chính nó. Theo kinh nghiệm của tôi, điều này có nghĩa là nó có thể truy cập các biến có phạm vi tại điểm định nghĩa của nó.

Mặc dù định nghĩa này có ý nghĩa, nhưng nó không giúp mô tả vấn đề triển khai các hàm hạng nhất trong ngôn ngữ dựa trên ngăn xếp thời gian chạy truyền thống. Khi nói đến các vấn đề triển khai, các hàm hạng nhất có thể được chia thành hai lớp:

  • Các biến cục bộ trong các hàm không bao giờ được sử dụng sau khi hàm trả về.
  • Các biến cục bộ có thể được sử dụng sau khi hàm trả về.

Trường hợp đầu tiên (funargs xuống) không khó thực hiện và có thể được tìm thấy ngay cả các ngôn ngữ thủ tục cũ hơn, như Algol, C và Pascal. C loại vấn đề này, vì nó không cho phép các hàm lồng nhau, nhưng Algol và Pascal thực hiện việc ghi sổ cần thiết để cho phép các hàm bên trong tham chiếu các biến ngăn xếp của hàm ngoài.

Trường hợp thứ hai (mặt khác trở lên), mặt khác, yêu cầu các bản ghi kích hoạt được lưu bên ngoài ngăn xếp, trong heap. Điều này có nghĩa là rất dễ rò rỉ tài nguyên bộ nhớ trừ khi thời gian chạy ngôn ngữ bao gồm trình thu gom rác. Trong khi hầu hết mọi thứ là rác được thu thập ngày hôm nay, yêu cầu một vẫn là một quyết định thiết kế quan trọng và thậm chí còn nhiều hơn trước đây.


Đối với ví dụ cụ thể về Java, nếu tôi nhớ chính xác, vấn đề chính không thực sự là có thể thực hiện các bao đóng, mà là làm thế nào để giới thiệu chúng với ngôn ngữ theo cách không dư thừa với các tính năng hiện có (như các lớp bên trong ẩn danh) và điều đó không xung đột với các tính năng hiện có (như các trường hợp ngoại lệ được kiểm tra - một vấn đề không phải là vấn đề nhỏ để giải quyết và hầu hết mọi người lúc đầu không nghĩ đến).

Tôi cũng có thể nghĩ về những thứ khác làm cho các hàm hạng nhất trở nên ít tầm thường hơn để thực hiện, chẳng hạn như quyết định phải làm gì với các biến "ma thuật" như thế này , tự hoặc siêu và cách tương tác với các toán tử dòng điều khiển hiện có, chẳng hạn như ngắttrả về (chúng tôi có muốn cho phép trả lại không cục bộ hay không?). Nhưng cuối cùng, sự phổ biến gần đây của các chức năng hạng nhất dường như chỉ ra rằng các ngôn ngữ không có chúng chủ yếu làm như vậy vì lý do lịch sử hoặc do một số quyết định thiết kế quan trọng sớm.


1
Bạn có biết ngôn ngữ nào phân biệt các trường hợp lên và xuống không? Trong các ngôn ngữ .NET, một phương thức chung dự kiến ​​sẽ nhận được một hàm chỉ hướng xuống có thể nhận được cấu trúc của loại chung cùng với một đại biểu sẽ nhận cấu trúc như một byref (trong C #, một " reftham số"). Nếu người gọi đóng gói tất cả các biến quan tâm trong cấu trúc, đại biểu có thể hoàn toàn tĩnh, tránh mọi nhu cầu phân bổ heap. Trình biên dịch không cung cấp bất kỳ trợ giúp cú pháp hay nào cho các cấu trúc như vậy, nhưng Framework có thể hỗ trợ chúng.
supercat

2
@supercat: Rust có nhiều kiểu đóng cho phép bạn thực thi tại thời điểm biên dịch nếu một hàm bên trong sẽ cần sử dụng heap. Tuy nhiên, điều này không có nghĩa là việc triển khai không thể cố gắng tránh phân bổ heap mà không buộc bạn phải quan tâm đến tất cả các loại bổ sung đó. Trình biên dịch có thể cố gắng suy ra vòng đời của hàm hoặc nó có thể sử dụng kiểm tra thời gian chạy để lưu các biến vào heap một cách lười biếng khi cần thiết (xem phần "phạm vi từ vựng" của bài báo Evolution of Lua để biết chi tiết)
hugomg

5

Chúng ta có thể xem cách đóng cửa được thực hiện trong C #. Quy mô của các phép biến đổi mà trình biên dịch C # thực hiện cho thấy rõ rằng cách thực hiện các bao đóng của chúng là khá nhiều công việc. Có thể có nhiều cách dễ dàng hơn để thực hiện các bao đóng, nhưng tôi nghĩ nhóm trình biên dịch C # sẽ nhận thức được điều này.

Hãy xem xét các giả danh C # sau (Tôi đã cắt ra một chút nội dung cụ thể về C #):

int x = 1;
function f = function() { x++; };
for (int i = 1; i < 10; i++) {
    f();
}
print x; // Should print 9

Trình biên dịch biến cái này thành cái gì đó như thế này:

class FunctionStuff {
   int x;
   void theFunction() {
       x++;
   }
}

FunctionStuff theClosureObject = new FunctionStuff();
theClosureObject.x = 1;
for (int i = 1; i < 10; i++) {
    theClosureObject.theFunction();
}
print theClosureObject.x; // Should print 9

. với C #)

Sự chuyển đổi này khá lớn và phức tạp: xem xét việc đóng cửa bên trong các lần đóng cửa và sự tương tác của các lần đóng cửa với phần còn lại của các tính năng ngôn ngữ C #. Tôi có thể tưởng tượng rằng tính năng này đã bị đẩy lùi cho Java, vì Java 7 đã có khá nhiều tính năng mới.


Tôi có thể thấy nơi này sẽ đi; có nhiều lần đóng và phạm vi truy cập chính cùng một biến sẽ trở nên lộn xộn.
Raphael

Thành thật mà nói, điều này nhiều hơn do sử dụng khung OO hiện có để thực hiện các lần đóng cửa sau đó bất kỳ vấn đề thực sự nào với chúng. Các ngôn ngữ khác chỉ phân bổ các biến trong một cấu trúc riêng biệt, không có phương thức, và sau đó để nhiều bao đóng chia sẻ nó nếu chúng muốn.
hugomg

@Raphael: bạn cảm thấy thế nào về việc đóng cửa bên trong việc đóng cửa? Đợi đã, để tôi thêm nó vào.
Alex ten Brink

5

Để trả lời một phần câu hỏi của bạn. Chủ nghĩa hình thức được mô tả bởi Morrisett và Harper bao gồm các ngữ nghĩa lớn và nhỏ của các ngôn ngữ đa hình bậc cao có chứa các bao đóng. Có những bài báo trước khi cung cấp các loại ngữ nghĩa mà bạn đang tìm kiếm. Nhìn, ví dụ, tại máy SECD . Thêm các tài liệu tham khảo có thể thay đổi hoặc người địa phương có thể thay đổi vào các ngữ nghĩa này là đơn giản. Tôi không thấy rằng có bất kỳ vấn đề kỹ thuật nào trong việc cung cấp ngữ nghĩa như vậy.


Cảm ơn bạn đã tham khảo! Nó dường như không làm cho việc đọc nhẹ, nhưng đó có lẽ là điều được mong đợi từ một bài báo ngữ nghĩa.
Raphael

1
@Raphael: Có lẽ có những cái đơn giản hơn xung quanh. Tôi sẽ cố gắng tìm một cái gì đó và lấy lại cho bạn. Trong mọi trường hợp, Hình 8 có ngữ nghĩa mà bạn đang tìm kiếm.
Dave Clarke

Có lẽ bạn có thể đưa ra một cái nhìn tổng quan thô. những ý tưởng trung tâm trong câu trả lời của bạn?
Raphael

2
@Raphael. Có lẽ tôi có thể giới thiệu cho bạn các ghi chú bài giảng của tôi, tôi sử dụng cho một khóa học ngôn ngữ lập trình, giúp bạn giới thiệu nhanh. Vui lòng tra cứu bản phát hành 8 và 9.
Uday Reddy

1
Liên kết đó xuất hiện hoặc chết hoặc đằng sau xác thực vô hình. ( cs.cmu.edu/afs/cs/user/rwh/public/www/home/ con / gcpoly / tr.pdf ). Tôi bị cấm 403.
Ben Fletcher
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.