2009-09-16 20 views
40
def foo 
    f = Proc.new { return "return from foo from inside proc" } 
    f.call # control leaves foo here 
    return "return from foo" 
end 

def bar 
    b = Proc.new { "return from bar from inside proc" } 
    b.call # control leaves bar here 
    return "return from bar" 
end 

puts foo # prints "return from foo from inside proc" 
puts bar # prints "return from bar" 

Tôi nghĩ từ khóa return là tùy chọn trong Ruby và bạn luôn luôn là return cho dù bạn có yêu cầu hay không. Do đó, tôi thấy điều ngạc nhiên là foobar có đầu ra khác nhau được xác định bởi thực tế là foo chứa một số return rõ ràng trong Proc f.Tại sao lợi nhuận rõ ràng tạo sự khác biệt trong một Proc?

Có ai biết tại sao trường hợp này xảy ra không?

Trả lời

63

Ruby có ba cấu trúc:

  1. Một khối không phải là một đối tượng và được tạo ra bởi { ... } hoặc do ... end.
  2. A proc là một đối tượng Proc được tạo bởi Proc.new hoặc proc.
  3. A lambdaProc được tạo bởi lambda (hoặc proc trong Ruby 1.8).

Ruby có ba từ khóa mà trở về từ một cái gì đó:.

  1. return chấm dứt phương pháp hoặc lambda nó là trong
  2. next chấm dứt khối, proc, hoặc lambda nó là trong
  3. . break chấm dứt phương thức mang lại cho khối hoặc gọi proc hoặc lambda nó đang ở.

Trong lambdas, return hoạt động như next, vì bất kỳ lý do gì. nextbreak được đặt tên theo cách mà họ là vì họ thường được sử dụng với các phương pháp như each, nơi chấm dứt khối sẽ gây ra lặp đi lặp lại để tiếp tục với phần tử tiếp theo của bộ sưu tập, và chấm dứt each sẽ làm bạn nghỉ ra khỏi vòng lặp.


Nếu bạn sử dụng return bên trong định nghĩa của foo, bạn sẽ trở lại từ foo, ngay cả khi nó nằm bên trong một khối hoặc một proc. Để trở về từ một khối, bạn có thể sử dụng từ khóa next để thay thế.

def foo 
    f = Proc.new { next "return from foo from inside proc" } 
    f.call # control leaves foo here 
    return "return from foo" 
end 
puts foo # prints "return from foo" 
+0

Đã chỉnh sửa cũng đề cập đến hành vi của lambdas – sepp2k

+0

lưu ý, trong ví dụ của bạn nếu tiếp theo bị bỏ qua, hành vi vẫn còn. –

+1

btw, tôi nghĩ cả hai chúng tôi đã làm một công việc tuyệt vời trả lời một câu hỏi khác nhau :) là tại sao, tôi nghĩ chỉ có Matz biết, rất nhiều thứ xung quanh việc đóng cửa vi phạm nguyên tắc ít ngạc nhiên nhất. –

12

Đây là ngữ nghĩa cho Proc s; nó không nhất thiết là ngữ nghĩa cho tất cả các khối. Tôi đồng ý điều này là một chút bối rối. Nó có thêm tính linh hoạt (và có lẽ một phần nguyên nhân Ruby không có đặc điểm nào ngoại trừ việc thực hiện nó).

Hành vi được xác định trong triển khai Proc. Lambda hoạt động khác nhau, vì vậy nếu bạn muốn return s không thoát khỏi ngoài phương pháp kèm theo, hãy sử dụng lambdas.Hoặc, bỏ qua từ khóa return từ số Proc của bạn.

Điều tra sâu về đóng cửa của Rubys is here. Đó là một sự phô trương tuyệt vời.

Vì vậy:

def foo 
    f = Proc.new { 
    p2 = Proc.new { return "inner proc"}; 
    p2.call 
    return "proc" 
    } 
    f.call 
    return "foo" 
end 

def foo2 
    result = Proc.new{"proc"}.call 
    "foo2 (proc result is: #{result})" 
end 

def bar 
    l = lambda { return "lambda" } 
    result = l.call 
    return "bar (lambda result is: #{result})" 
end 

puts foo 
# inner proc 
puts foo2 
# foo (proc result is: proc) 
puts bar 
# bar (lambda result is: lambda) 
+0

"Đây là ngữ nghĩa cho Procs, nó không nhất thiết phải là ngữ nghĩa cho tất cả các khối." Đây là ngữ nghĩa cho các cá thể Proc được tạo bởi Proc.new cũng như tất cả các khối "bình thường" (tức là các khối không được sử dụng cùng với từ khóa 'proc' hoặc' lambda'). – sepp2k

+0

True, tôi có thể thêm một ví dụ, nhưng tôi nghĩ rằng ví dụ của tôi là đủ phức tạp vì nó. –

+0

vì một số lý do lambdas được chuyển thành khối hoạt động như chúng luôn luôn là các khối: '[1] .map & lambda {return" lambda "}' trả về từ hàm. Đây có phải là lỗi trong JRuby không? –

6

nghĩ về nó theo cách này: Proc.new chỉ cần tạo một khối mã đó là một phần của chức năng gọi điện thoại. proc/lambda tạo ra một hàm ẩn danh có các ràng buộc đặc biệt. Một chút ví dụ mã sẽ giúp:

def foo 
    f = Proc.new { return "return from foo from inside Proc.new" } 
    f.call # control leaves foo here 
    return "return from foo" 
end 

tương đương với

def foo 
    begin 
    return "return from foo from inside begin/end" } 
    end 

    return "return from foo" 
end 

nên rõ ràng là sự trở lại sẽ chỉ trở về từ chức năng 'foo'

trái ngược:

def foo 
    f = proc { return "return from foo from inside proc" } 
    f.call # control stasy in foo here 
    return "return from foo" 
end 

tương đương với (bỏ qua các ràng buộc vì không được sử dụng trong ví dụ này):

def unonymous_proc 
    return "return from foo from inside proc" 
end 

def foo 
    unonymous_proc() 
    return "return from foo" 
end 

Điều này rõ ràng sẽ không trở lại từ foo và tiếp tục đến tuyên bố tiếp theo.

+3

mặc dù đây là câu hỏi cũ, xin lưu ý rằng có sự khác biệt giữa Ruby 1.8.7 và 1.9.3 trong Kernel.proc sau cư xử như Proc.new thay vì lambda. – froginvasion

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