2009-04-18 30 views

Trả lời

101

Ruby's yield không phải là một trình lặp như trong C# và Python. yield chính nó thực sự là một khái niệm thực sự đơn giản khi bạn hiểu cách khối hoạt động trong Ruby.

Có, các khối là một tính năng lập trình chức năng, mặc dù Ruby không đúng ngôn ngữ chức năng. Trong thực tế, Ruby sử dụng phương thức lambda để tạo các đối tượng khối, được vay mượn từ cú pháp của Lisp để tạo các hàm ẩn danh - đó là những khối là gì. Từ quan điểm khoa học máy tính, các khối của Ruby (và các hàm lambda của Lisp) là closures.Trong Ruby, các phương thức thường chỉ mất một khối. (Bạn có thể vượt qua nhiều hơn, nhưng nó vụng về.)

Từ khóa yield trong Ruby chỉ là cách gọi một khối được gán cho phương thức. Hai ví dụ này tương đương:

def with_log 
    output = yield # We're calling our block here with yield 
    puts "Returned value is #{output}" 
end 

def with_log(&stuff_to_do) # the & tells Ruby to convert into 
          # an object without calling lambda 
    output = stuff_to_do.call # We're explicitly calling the block here 
    puts "Returned value is #{output}" 
end 

Trong trường hợp đầu tiên, chúng tôi giả định có một khối và gọi để gọi. Mặt khác, Ruby kết thúc khối trong một đối tượng và chuyển nó thành một đối số. Việc đầu tiên là hiệu quả hơn và dễ đọc, nhưng chúng có hiệu quả giống nhau. Bạn muốn gọi một trong hai như thế này:

with_log do 
    a = 5 
    other_num = gets.to_i 
    @my_var = a + other_num 
end 

Và nó sẽ in ra giá trị mà vết thương lên bị gán cho @my_var. (OK, do đó, đó là một chức năng hoàn toàn ngu ngốc, nhưng tôi nghĩ bạn có ý tưởng.)

Các khối được sử dụng cho rất nhiều thứ trong Ruby. Hầu hết mọi nơi bạn sử dụng một vòng lặp trong một ngôn ngữ như Java, nó được thay thế bằng Ruby bằng các phương thức có khối. Ví dụ:

[1,2,3].each {|value| print value} # prints "123" 
[1,2,3].map {|value| 2**value} # returns [2, 4, 8] 
[1,2,3].reject {|value| value % 2 == 0} # returns [1, 3] 

Như Andrew lưu ý, nó cũng thường được sử dụng để mở tệp và nhiều địa điểm khác. Về cơ bản bất cứ lúc nào bạn có một chức năng tiêu chuẩn có thể sử dụng một số logic tùy chỉnh (như sắp xếp một mảng hoặc xử lý một tệp), bạn sẽ sử dụng một khối. Có những cách sử dụng khác nữa, nhưng câu trả lời này đã quá lâu rồi tôi sợ nó sẽ gây ra cơn đau tim cho độc giả với những hiến pháp yếu hơn. Hy vọng rằng điều này sẽ làm sáng tỏ sự nhầm lẫn về chủ đề này.

+2

Giải thích hay. –

+3

Cảm ơn, điều đó có ý nghĩa hơn rất nhiều, và nó gắn kết nhiều hơn với những gì tôi đã học được cho đến nay về các khối. – hbw

+0

Cảm ơn. Điều đó đã giúp. – jjohn

2

Tôi nghĩ câu lệnh yield bắt nguồn từ ngôn ngữ CLU. Tôi luôn tự hỏi liệu nhân vật từ Tron có được đặt tên sau CLU không ....

+2

Để áp phích gốc: apropros của câu trả lời của Daniel, bạn có thể muốn google cho "coroutine" - đây là khái niệm "khoa học máy tính" cơ sở mà CLU đã triển khai sử dụng lợi nhuận. – itowlson

0

Tôi nghĩ 'coroutine' là từ khóa bạn đang tìm kiếm.

Ví dụ: http://en.wikipedia.org/wiki/Yield

Năng suất trong máy tính và thông tin khoa học:

  • trong khoa học máy tính, một điểm lợi nhuận (và tái nhập cảnh) của một coroutine
+0

Tín dụng cũng để @ itowlson, người đồng thời đề cập đến 'coroutine' trong một bình luận về câu trả lời khác. – Brian

+5

Việc sử dụng từ khóa lợi nhuận trong Ruby không liên quan gì đến định nghĩa CS thông thường về lợi nhuận. Nó chỉ là một cuộc gọi chương trình con. Thật vậy, bạn chỉ có thể sử dụng cuộc gọi thay vì lợi nhuận, nếu bạn gán khối ẩn danh cho một biến. –

+0

Đây không phải là cách sử dụng trong Ruby. – Chuck

6

Có nhiều đến năng suất và khối so với chỉ looping .

Loạt Enumerating enumerable có một loạt những điều bạn có thể làm với liệt kê, chẳng hạn như hỏi xem câu lệnh có đúng với bất kỳ thành viên nào của nhóm hay không. một điều kiện nhất định.

Các khối cũng hữu ích cho phạm vi biến. Thay vì chỉ đơn giản là thuận tiện, nó có thể giúp với thiết kế tốt. Ví dụ: mã

File.open("filename", "w") do |f| 
    f.puts "text" 
end 

đảm bảo rằng luồng tệp được đóng khi bạn kết thúc, ngay cả khi ngoại lệ xảy ra và biến nằm ngoài phạm vi khi bạn đã hoàn thành.

Một google bình thường không đưa ra một bài đăng trên blog tốt về các khối và sản lượng bằng ruby. Tôi không biết tại sao.

Response to comment:

tôi nghi ngờ nó được đóng cửa vì kết thúc khối, không phải vì biến đi ra khỏi phạm vi.

Sự hiểu biết của tôi là không có gì đặc biệt xảy ra khi biến cuối cùng trỏ đến một đối tượng nằm ngoài phạm vi, ngoài đối tượng đó đủ điều kiện để thu thập rác. Tôi không biết làm thế nào để xác nhận điều này, mặc dù.

Tôi có thể cho thấy rằng đối tượng tệp bị đóng trước khi bị thu thập rác, thường không xảy ra ngay lập tức. Trong ví dụ sau, bạn có thể thấy rằng một đối tượng tệp được đóng trong câu lệnh puts thứ hai, nhưng nó chưa được thu thập rác.

g = nil 
File.open("/dev/null") do |f| 
    puts f.inspect # #<File:/dev/null> 
    puts f.object_id # Some number like 70233884832420 
    g = f 
end 
puts g.inspect # #<File:/dev/null (closed)> 
puts g.object_id # The exact same number as the one printed out above, 
    # indicating that g points to the exact same object that f pointed to 
+0

Làm cách nào để đảm bảo tệp được đóng khi bạn hoàn tất? Ruby tự động đóng nó khi khối kết thúc và 'f' rơi ra khỏi phạm vi? – aidan

+3

@aidan Phương thức 'File # open', khi được gọi với một khối, sẽ đóng xử lý tệp đã tạo khi khối đã hoàn tất. – qqx

+0

Cảm ơn bạn đã làm rõ! – aidan

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