임베디드 스크립팅 언어로 Ruby 1.9.1을 사용하여 “최종 사용자”코드를 Ruby 블록에 작성하려고합니다. 이것의 한 가지 문제는 사용자가 블록에서 ‘return’키워드를 사용할 수 있기를 원하므로 암시 적 반환 값에 대해 걱정할 필요가 없습니다. 이를 염두에두고 다음과 같이 할 수 있기를 바랍니다.
def thing(*args, &block)
value = block.call
puts "value=#{value}"
end
thing {
return 6 * 7
}
위의 예에서 ‘return’을 사용하면 LocalJumpError가 발생합니다. 나는 이것이 문제의 블록이 람다가 아니라 Proc이기 때문이라는 것을 알고 있습니다. ‘return’을 제거하면 코드가 작동하지만이 시나리오에서는 ‘return’을 사용할 수 있기를 원합니다. 이것이 가능한가? 블록을 람다로 변환하려고 시도했지만 결과는 동일합니다.
답변
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
답변
루비에서 웹 프레임 워크 용 DSL을 작성하는 데 동일한 문제가 발생했습니다 … (웹 프레임 워크 Anorexic이 흔들릴 것입니다!) …
어쨌든, 나는 루비 내부를 파헤쳐 서 Proc가 return을 호출 할 때 반환 된 LocalJumpError를 사용하여 간단한 해결책을 찾았습니다 … 지금까지 테스트에서 잘 실행되지만 완전한 증거인지는 모르겠습니다.
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
구조 세그먼트의 if 문은 다음과 같을 수 있습니다.
if e.is_a? LocalJumpError
하지만 저에게는 미지의 영역이므로 지금까지 테스트 한 내용을 고수하겠습니다.
답변
단점에도 불구하고 이것이 정답이라고 생각합니다.
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에서 return을 사용할 수 있습니다.
여기서 Thread를 사용하는 장점은 어떤 경우에는 LocalJumpError가 발생하지 않고 가장 예상치 못한 곳에서 반환이 발생한다는 것입니다 (최상위 메서드에서 예기치 않게 나머지 본문을 건너 뜁니다).
가장 큰 단점은 잠재적 인 오버 헤드입니다 ( yield
시나리오에서 충분한 경우 에만 Thread + join을 대체 할 수 있음).
답변
방법을 찾았지만 방법을 중간 단계로 정의하는 것이 포함됩니다.
def thing(*args, &block)
define_method(:__thing, &block)
puts "value=#{__thing}"
end
thing { return 6 * 7 }