42

Tôi đã tìm thấy rất nhiều người sử dụng các từ đóng cửakhối thay thế cho nhau. Hầu hết những người này không thể giải thích những gì họ đang nói về.Chính xác sự khác nhau giữa "đóng cửa" và "chặn" là gì?

Một số lập trình viên Java (ngay cả những người từ tư vấn đắt tiền) nói về các lớp bên trong vô danh là "khối" và "đóng cửa" - nhưng tôi biết điều này không đúng. (Bạn không thể vượt qua các biến có thể thay đổi từ phạm vi của phương pháp trong đó chúng được xác định ...)

Tôi đang tìm:

  • một chính xác, khoa học máy tính nghĩa của một chặn
  • một chính xác, khoa học máy tính nghĩa của một đóng
  • làm rõ về sự khác biệt giữa hai người.

Tôi thực sự muốn xem các liên kết, bài viết hoặc tham khảo tài liệu tham khảo về các yêu cầu này.

+0

Tôi không chắc là chúng có định nghĩa chính xác, ví dụ bao đóng của Apple được thêm vào gcc được gọi là "khối" – cobbal

+0

Nếu bạn đang nói về [] - chúng đi vào Objective-C từ Smalltalk, nơi chúng được biên dịch thành một phiên bản có tên là 'BlockClosure'. – daf

+2

Anh ấy không nói về Objective-C, anh ấy đang nói về các phần mở rộng của Apple đối với C. Trong OSX 10.6 Snow Leopard, Appel đã giới thiệu một thư viện đồng thời mới có tên là Grand Central Dispatch. Đó là một thư viện dựa trên nhiệm vụ tương tự như Thư viện song song nhiệm vụ của Microsoft. Ý tưởng cơ bản của GCD là bạn đưa một khối mã vào thư viện và thư viện sau đó quyết định khi nào và trên lõi nào để thực thi. Thông thường, bạn sẽ làm điều đó với các con trỏ hàm, nhưng Apple cho rằng quá xấu và chúng mở rộng C với các biểu thức lambda cơ bản, mà chúng gọi là các khối. –

Trả lời

27

Trong khi một khối chỉ là một đoạn mã có thể được sáng tác bởi các báo cáo và tờ khai nhưng không có gì khác, một đóng cửa là một đối tượng hạng nhất thực, một biến thực rằng có một khối như giá trị của nó.

Sự khác biệt chính là một khối chỉ đơn giản là nhóm các hướng dẫn lại với nhau (ví dụ phần thân của câu lệnh trong khi), trong khi đóng là một biến chứa một số mã có thể được thực thi.

Nếu bạn thường xuyên đóng cửa, bạn có thể truyền nó như một tham số cho các chức năng, quấy rầy và giải mã nó, và chủ yếu gọi nó!

Closure c = { println 'Hello!' } 
/* now you have an object that contains code */ 
c.call() 

Trong số đóng cửa tất nhiên là mạnh hơn, họ là các biến và có thể được sử dụng để xác định hành vi của các đối tượng tùy chỉnh (trong khi thông thường bạn phải sử dụng giao diện hoặc phương pháp OOP khác trong chương trình).

Bạn có thể nghĩ đến việc đóng làm hàm chứa chức năng của chính nó bên trong chính nó.

Các khối hữu ích vì chúng cho phép phạm vi biến. Thông thường khi bạn định nghĩa một biến bên trong một phạm vi, bạn có thể ghi đè các định nghĩa bên ngoài mà không gặp bất kỳ vấn đề nào và các định nghĩa mới sẽ tồn tại chỉ trong quá trình thực thi khối.

for (int i = 0; i < 10; ++i) 
{ 
    int t = i*2; 
    printf("%d\r\n", t); 
} 

t được định nghĩa bên trong khối (cơ thể của báo cáo kết quả for) và sẽ kéo dài chỉ trong khối đó.

+0

một khối cũng đánh dấu điểm bắt đầu của "phạm vi đối tượng". – jldupont

+0

bạn đúng, quên đề cập đến .. chỉ cần cập nhật – Jack

+0

Ví dụ cho vòng lặp không bay chung, như "đóng cửa" là sử dụng với Javascript nhiều hơn với các ngôn ngữ khác và cho các khối lặp không đóng gói các biến của họ ở tất cả ngoại trừ * chỉ * với khai báo hàm. Tôi nghi ngờ ví dụ của bạn là Java? Xin hãy chỉ ra cụ thể. –

16

Khối là thứ gì đó mang tính thực tế - Một đơn vị lôgic các câu lệnh (liên quan đến phạm vi hơn là đóng).

if (Condition) { 
    // Block here 
} 
else { 
    // Another block 
} 

Đóng cửa có liên quan đến các chức năng hoặc lớp học an toàn - một đối tượng ẩn danh (mã) bị ràng buộc với môi trường (với các biến của nó).

def foo() { 
    var x = 0 
    return() => { x += 1; return x } 
} 

Ở đây foo trả về kết thúc! Biến cục bộ x vẫn tồn tại thông qua việc đóng cửa ngay cả sau khi foo chấm dứt và có thể được tăng lên thông qua các cuộc gọi của hàm ẩn danh được trả về.

val counter = foo() 
print counter() // Returns 2 
print counter() // Return 3 

Lưu ý rằng chỉ là Ruby trong đó khối và đóng cửa được đối xử tương tự từ những gì của Ruby gọi khối là một đóng:

(1..10).each do |x| 
    p x 
end 

each -method được thông qua một chức năng đóng cửa (dùng một tham số x) được gọi là một khối trong Ruby.

+0

... sau đó có sự nhầm lẫn với Smalltalk. Những gì các lập trình viên Ruby gọi là một khối, Smalltalk gọi một 'BlockClosure'! – daf

+0

Trên thực tế, trong ví dụ Ruby của bạn, khối là * không * đóng cửa, bởi vì nó không đóng bất cứ thứ gì. Thú vị hơn sẽ là 'a = 7; (1..10) .each do | x | p một << x end' hoặc một cái gì đó như thế. –

1

Cụm từ bạn đang sử dụng thường được sử dụng cùng nhau những ngày này trong Ruby, mặc dù các cấu trúc trước đây xuất hiện trong Algol, Smalltalk và Scheme. Tôi sẽ trích dẫn tiêu chuẩn Ruby, nếu có.

Tôi không chắc chắn tôi có thể trả lời câu hỏi chính xác của bạn, nhưng tôi có thể minh họa. lời xin lỗi của tôi nếu bạn biết điều này đã ...

def f &x 
    yield 
    x 
end 

def g 
    y = "block" 
    t = f { p "I'm a #{y}" } 
    y = "closure" 
    t 
end 

t = g 
t.call 

Và ...

$ ruby exam.rb 
"I'm a block" 
"I'm a closure" 
$ 

Vì vậy, một khối là một chuỗi chức năng giống như vô danh mã gắn liền với một lời gọi phương thức. Nó được sử dụng trên tất cả các API Ruby. Khi bạn làm cho nó dễ dàng, đủ để tạo ra một chức năng vô danh hóa ra nó rất hữu ích cho tất cả mọi thứ.

Nhưng lưu ý rằng sau khi f trở về, sau đó g trở về, chúng tôi tổ chức vào các khối bằng cách trả lại nó từ f (như x) và sau đó từ g (như t). Bây giờ chúng ta gọi khối lần thứ hai. Một lần nữa, lưu ý rằng g() đã trở lại. Nhưng khối đề cập đến một biến địa phương trong một thể hiện chức năng (và phạm vi) mà không tồn tại nữa? Và nó nhận được giá trị mới của y?!

Vì vậy, việc đóng là đối tượng giống như chức năng được đóng trong phạm vi từ vựng của nó. Chúng khá khó khăn để thực hiện vì chúng phá hủy mô hình do-it-with-a-stack rất hữu ích cho các biến cục bộ trong các cá thể gọi hàm.


1. Ruby có nhiều hương vị của các đối tượng chức năng giống như đóng cửa; Đây chỉ là một trong số họ.

+0

Đoạn mã trong câu trả lời này là một minh họa hoàn hảo về lý do tại sao tôi không sử dụng Ruby. –

2

Tiếng chuông lớn, râu quai nón người ta có này để nói về đóng cửa và Blocks:

http://martinfowler.com/bliki/Closure.html

Tại một thời điểm, ông nói đóng cửa là một khối có thể được thông qua như là một cuộc tranh cãi đến một phương pháp.

+0

Tôi chỉ thấy khái niệm đóng cửa là tổng quát hơn và được xác định rõ hơn là một khối. Tôi không thấy sự cần thiết của các khối nếu đã có thực sự đóng cửa xung quanh. –

+0

Cảm ơn bạn đã liên kết! –

+0

@WeiQiu Khi tôi nhiều hay ít nói trong câu trả lời của tôi ở trên, hành vi của 'return' bao gồm hầu hết các lý do cho đóng cửa khối như các thực thể riêng biệt từ đóng cửa chức năng. –

4

Có rất nhiều sự nhầm lẫn xung quanh ở đây vì có các cụm từ có nhiều định nghĩa và nhiều thứ khác nhau bị bẻ khóa đơn giản vì chúng thường được tìm thấy cùng nhau.

Trước tiên, chúng tôi có "chặn". Đó chỉ là một đoạn mã từ vựng mà làm cho một đơn vị - cơ thể của một vòng lặp, ví dụ. Nếu ngôn ngữ thực sự có phạm vi chặn, thì các biến có thể được xác định chỉ tồn tại trong đoạn mã đó.

Thứ hai, chúng tôi có mã có thể gọi là loại giá trị. Trong các ngôn ngữ chức năng, đây là các giá trị hàm - đôi khi được gọi là "funs", "functions anonymous" (vì hàm được tìm thấy trong giá trị chứ không phải tên được gán cho nó; bạn không cần tên để gọi chúng) hoặc " lambdas "(từ nhà điều hành được sử dụng để tạo ra chúng trong Lambda Calculus của Giáo hội). Chúng có thể được gọi là "đóng cửa", nhưng chúng không tự động đóng cửa; để đủ điều kiện, chúng phải đóng gói ("đóng") phạm vi từ vựng xung quanh sự tạo của chúng - nghĩa là các biến được định nghĩa bên ngoài phạm vi của hàm đó nhưng trong phạm vi định nghĩa của nó vẫn có sẵn bất cứ khi nào hàm được gọi, thậm chí nếu điểm gọi là sau biến số được tham chiếu nếu không sẽ không còn phạm vi và đã được lưu trữ lại.

Mặc dù vậy, bạn có thể có các giá trị mã có thể gọi không phải là toàn bộ chức năng. Smalltalk gọi những "block closures", trong khi Ruby gọi chúng là "procs". Nhưng hầu hết người Ruby chỉ gọi họ là "chặn", bởi vì họ là phiên bản được chỉnh sửa của những gì được tạo bởi cú pháp { ... } hoặc do ... end. Điều gì làm cho chúng khác biệt với lambdas (hoặc "đóng cửa chức năng") là chúng không giới thiệu một cấp độ mới của chương trình con. Nếu mã trong phần đóng của một khối gọi là return, nó trả về từ hàm/phương thức bên ngoài, việc đóng khối tồn tại bên trong, không chỉ chính khối đó.

Hành vi đó rất quan trọng để bảo tồn những gì mà Tennent gắn nhãn là "nguyên tắc tương ứng", tuyên bố rằng bạn có thể thay thế bất kỳ mã nào bằng hàm nội tuyến có chứa mã đó trong cơ thể và được gọi ngay lập tức. Ví dụ, trong Javascript, bạn có thể thay thế này:

x = 2; 

với điều này:

(function(){x = 2;})(); 

Ví dụ của tôi không phải là rất thú vị, nhưng khả năng làm điều này loại chuyển đổi mà không ảnh hưởng đến hành vi của các chương trình đóng một vai trò quan trọng trong chức năng tái cấu trúc. Vấn đề là, ngay sau khi bạn đã nhúng câu lệnh return, nguyên tắc không còn giữ.

Đây là lý do tại sao Ruby có cả procs và lambdas - một nguồn gây nhầm lẫn lâu năm cho người mới. Cả procs và lambdas đều là đối tượng của lớp Proc, nhưng chúng hoạt động khác nhau, như được chỉ ra ở trên: một return chỉ trả về từ phần thân của lambda, nhưng nó trả về từ phương thức xung quanh một proc. (Ngoài ra, trong khi không liên quan đến sự phân biệt tôi đang vẽ ở đây, lambdas kiểm tra tinh thần và phàn nàn nếu được gọi với số lượng sai đối số. Bạn có thể cho biết loại bạn có bằng cách gọi .lambda? trên đối tượng.)

Javascript hiện tại chỉ có chức năng đóng cửa, mặc dù có một đề xuất trên bàn để giới thiệu các đóng cửa chặn cho ngôn ngữ.

0

Đó là một số nguyên .

Int workDaysInAWeek = 5

Đó là một số nguyên biến và nó có thể được thiết lập để một khác nhau nguyên. (Nếu hoàn cảnh khiến bạn thay đổi giá trị, nó có thể được gọi là một liên tục.)

Trong khi những con số lo ngại trên, khốiđóng cửa thuật toán quan tâm. Sự khác biệt giữa các khối đóng cửa, tương ứng, cũng tương đương với ở trên.

Các vấn đề liên quan