Ruby 블록에서 'return'사용
임베디드 스크립팅 언어로 Ruby 1.9.1을 사용하여 "최종 사용자"코드를 Ruby 블록에 작성하려고합니다. 이것의 한 가지 문제는 사용자가 블록에서 'return'키워드를 사용할 수 있기를 원하기 때문에 암시 적 반환 값에 대해 걱정할 필요가 없다는 것입니다. 이 점을 염두에두고 다음과 같이 할 수 있기를 바랍니다.
def thing(*args, &block)
value = block.call
puts "value=#{value}"
end
thing {
return 6 * 7
}
위의 예에서 'return'을 사용하면 LocalJumpError가 발생합니다. 나는 이것이 문제의 블록이 람다가 아니라 Proc이기 때문이라는 것을 알고 있습니다. 코드는 '반환'을 제거하면 작동하지만이 시나리오에서는 '반환'을 사용할 수 있기를 원합니다. 이것이 가능한가? 블록을 람다로 변환하려고 시도했지만 결과는 동일합니다.
next
이 컨텍스트에서 간단히 사용 하십시오.
$ 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
항상 메서드에서 반환하지만 irb에서이 스 니펫을 테스트하면 메서드가 없습니다.LocalJumpError
break
블록에서 값을 반환하고 호출을 종료합니다.yield
또는.call
에서 블록을 호출 한break
경우이 반복자에서도 중단됩니다.next
블록에서 값을 반환하고 호출을 종료합니다. 당신의 블록에 의해 호출 된 경우yield
또는.call
다음next
라인에 값 반환yield
불렸다을
Ruby에서는 그렇게 할 수 없습니다.
return
키워드는 항상 현재 컨텍스트의 방법 또는 람다에서 반환합니다. 블록에서는 클로저가 정의 된 메서드에서 반환 됩니다 . 호출 메서드 또는 람다 에서 반환 할 수 없습니다 .
Rubyspec은 이 루비에 대한 올바른 동작 (일반적으로 인정 하듯이 아닌 실제 구현하지만, 목표 C 루비와 완벽한 호환성)이 참임을 보여줍니다
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
# ...
당신은 잘못된 관점에서 그것을보고 있습니다. 이것은 thing
람다가 아니라의 문제입니다 .
def thing(*args, &block)
block.call.tap do |value|
puts "value=#{value}"
end
end
thing {
6 * 7
}
물건은 어디에서 호출됩니까? 수업 안에 있습니까?
다음과 같은 것을 사용하는 것이 좋습니다.
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
단점에도 불구하고 이것이 정답이라고 생각합니다.
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
}
이 해킹을 통해 사용자는 결과없이 자신의 procs에서 반환을 사용할 수 있습니다.
여기서 Thread를 사용하면 어떤 경우에는 LocalJumpError가 발생하지 않고 반환이 가장 예상치 못한 곳에서 발생한다는 것입니다 (최상위 메서드에서 예기치 않게 나머지 본문을 건너 뜁니다).
The main disadvantage is the potential overhead (you can replace the Thread+join with just the yield
if that's enough in your scenario).
I had the same issue writing a DSL for a web framework in ruby... (the web framework Anorexic will rock!)...
anyway, I dug into the ruby internals and found a simple solution using the LocalJumpError returned when a Proc calls return... it runs well in the tests so far, but I'm not sure it's full-proof:
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
the if statement in the rescue segment could probably look something like this:
if e.is_a? LocalJumpError
but it's uncharted territory for me, so I'll stick to what I tested so far.
I found a way, but it involves defining a method as an intermediate step:
def thing(*args, &block)
define_method(:__thing, &block)
puts "value=#{__thing}"
end
thing { return 6 * 7 }
참고URL : https://stackoverflow.com/questions/2325471/using-return-in-a-ruby-block
'Nice programing' 카테고리의 다른 글
Rails의 보호 및 비공개 메서드 (0) | 2020.10.04 |
---|---|
"모두 제외"jQuery 선택기 (0) | 2020.10.04 |
ArrayList를 스레드로부터 안전하게 만드는 방법은 무엇입니까? (0) | 2020.10.04 |
python : urllib2 urlopen 요청으로 쿠키를 보내는 방법 (0) | 2020.10.04 |
레이크 스펙을 실행하지 않고 Rails rspec 테스트를 위해 테스트 데이터베이스를 준비하려면 어떻게해야합니까? (0) | 2020.10.04 |