2010-02-24 33 views
73

Tôi đang cố gắng sử dụng Ruby 1.9.1 cho một ngôn ngữ kịch bản được nhúng, để mã "người dùng cuối" được viết trong khối Ruby. Một vấn đề với điều này là tôi muốn người dùng có thể sử dụng từ khóa 'return' trong các khối, vì vậy họ không cần phải lo lắng về các giá trị trả về tiềm ẩn. Với điều này trong tâm trí, đây là loại điều tôi muốn để có thể làm:Sử dụng 'return' trong khối Ruby

def thing(*args, &block) 
    value = block.call 
    puts "value=#{value}" 
end 

thing { 
    return 6 * 7 
} 

Nếu tôi sử dụng 'trở lại' trong ví dụ trên, tôi nhận được một LocalJumpError. Tôi biết rằng điều này là bởi vì các khối trong câu hỏi là một Proc và không phải là một lambda. Mã hoạt động nếu tôi xóa 'trả lại', nhưng tôi thực sự muốn có thể sử dụng 'trả lại' trong trường hợp này. Điều này có thể không? Tôi đã cố gắng chuyển đổi các khối để một lambda, nhưng kết quả là như nhau.

+0

tại sao bạn muốn tránh một giá trị trả về tiềm ẩn? – marcgg

+0

@marcgg - Tôi có câu hỏi liên quan ở đây - https://stackoverflow.com/questions/25953519/simple-way-to-understand-returning-from-a-block-in-ruby. –

Trả lời

1

Điều được gọi ở đâu? Bạn đang ở trong một lớp học?

Bạn có thể xem xét sử dụng một cái gì đó như thế này:

class MyThing 
    def ret b 
    @retval = b 
    end 

    def thing(*args, &block) 
    implicit = block.call 
    value = @retval || implicit 
    puts "value=#{value}" 
    end 

    def example1 
    thing do 
     ret 5 * 6 
     4 
    end 
    end 

    def example2 
    thing do 
     5 * 6 
    end 
    end 
end 
3

Bạn đang tìm kiếm nó từ quan điểm sai trái của quan điểm. Đây là vấn đề của thing, không phải là lambda.

def thing(*args, &block) 
    block.call.tap do |value| 
    puts "value=#{value}" 
    end 
end 

thing { 
    6 * 7 
} 
14

Bạn không thể làm điều đó trong Ruby.

Từ khóa returnluôn trả về trả về từ phương thức hoặc lambda trong ngữ cảnh hiện tại. Trong các khối, nó sẽ trở về từ phương pháp trong đó đóng cửa là được xác định. Không thể thực hiện trả lại từ phương thức gọi hoặc lambda.

Các Rubyspec chứng minh rằng điều này thực sự là hành vi đúng cho Ruby (phải thừa nhận là không phải là một thực hiện thực, nhưng nhằm mục đích tương thích đầy đủ với C Ruby):

describe "The return keyword" do 
# ... 
describe "within a block" do 
# ... 
it "causes the method that lexically encloses the block to return" do 
# ... 
it "returns from the lexically enclosing method even in case of chained calls" do 
# ... 
150

Đơn giản chỉ cần sử dụng next trong bối cảnh này:

$ irb 
irb(main):001:0> def thing(*args, &block) 
irb(main):002:1> value = block.call 
irb(main):003:1> puts "value=#{value}" 
irb(main):004:1> end 
=> nil 
irb(main):005:0> 
irb(main):006:0* thing { 
irb(main):007:1* return 6 * 7 
irb(main):008:1> } 
LocalJumpError: unexpected return 
     from (irb):7:in `block in irb_binding' 
     from (irb):2:in `call' 
     from (irb):2:in `thing' 
     from (irb):6 
     from /home/mirko/.rvm/rubies/ruby-1.9.1-p378/bin/irb:15:in `<main>' 
irb(main):009:0> thing { break 6 * 7 } 
=> 42 
irb(main):011:0> thing { next 6 * 7 } 
value=42 
=> nil 
  • return luôn trả về từ phương pháp, nhưng nếu bạn kiểm tra đoạn mã này trong irb bạn không có phương pháp, đó là lý do bạn có LocalJumpError
  • break trả về giá trị từ khối và kết thúc cuộc gọi của nó. Nếu khối của bạn được gọi bằng cách yield hoặc .call, thì break ngắt khỏi trình lặp này quá
  • next trả về giá trị từ khối và kết thúc cuộc gọi của nó. Nếu khối của bạn đã được gọi bằng yield hoặc .call, sau đó next lợi nhuận giá trị cho dòng nơi yield được gọi
+0

độc đáo nói. làm tốt. –

+4

ngắt trong một proc sẽ nêu ra một ngoại lệ – gfreezy

+0

bạn có thể trích dẫn nơi bạn nhận được thông tin này từ đó "giá trị trả về tiếp theo từ khối và kết thúc nó gọi". Tôi muốn đọc thêm về nó. – user566245

0

tôi đã cùng một vấn đề viết một DSL cho một khuôn khổ web trong ruby ​​... (khuôn khổ web biếng ăn sẽ đá !) ...

dù sao, tôi đào vào bên trong ruby ​​và tìm thấy một giải pháp đơn giản bằng cách sử dụng LocalJumpError trả lại khi một cuộc gọi Proc trở lại ...nó chạy tốt trong các cuộc thử nghiệm cho đến nay, nhưng tôi không chắc chắn đó là đầy đủ giấy tờ chứng minh:

def thing(*args, &block) 
    if block 
    block_response = nil 
    begin 
     block_response = block.call 
    rescue Exception => e 
     if e.message == "unexpected return" 
      block_response = e.exit_value 
     else 
      raise e 
     end 
    end 
    puts "value=#{block_response}" 
    else 
    puts "no block given" 
    end 
end 

câu lệnh if trong phân khúc giải cứu có thể có thể giống như thế này:

if e.is_a? LocalJumpError 

nhưng nó lãnh thổ thám hiểm cho tôi, vì vậy tôi sẽ dính vào những gì tôi đã thử nghiệm cho đến nay.

1

Tôi tin rằng đây là câu trả lời đúng, bất chấp những hạn chế:

def return_wrap(&block) 
    Thread.new { return yield }.join 
rescue LocalJumpError => ex 
    ex.exit_value 
end 

def thing(*args, &block) 
    value = return_wrap(&block) 
    puts "value=#{value}" 
end 

thing { 
    return 6 * 7 
} 

Hack này cho phép người dùng sử dụng trở lại trong procs của họ mà không hậu quả, tự được bảo tồn vv

Ưu điểm của việc sử dụng Chủ đề ở đây là trong một số trường hợp, bạn sẽ không nhận được LocalJumpError - và sự trở lại sẽ xảy ra ở nơi bất ngờ nhất (phía trên một phương thức mức cao nhất, bất ngờ bỏ qua phần còn lại của phần thân của nó).

Bất lợi chính là chi phí tiềm năng (bạn có thể thay thế Chủ đề + tham gia chỉ với yield nếu đó là đủ trong kịch bản của bạn).

0

Tôi tìm thấy một cách, nhưng nó liên quan đến việc xác định một phương pháp như một bước trung gian:

def thing(*args, &block) 
    define_method(:__thing, &block) 
    puts "value=#{__thing}" 
end 

thing { return 6 * 7 } 
Các vấn đề liên quan