Ruby 블록에서 'return'사용


87

임베디드 스크립팅 언어로 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'을 사용할 수 있기를 원합니다. 이것이 가능한가? 블록을 람다로 변환하려고 시도했지만 결과는 동일합니다.


암시 적 반환 값을 피하고 싶은 이유는 무엇입니까?
marcgg

@marcgg - 나는 여기에 관련 질문이 있습니다 - stackoverflow.com/questions/25953519/...을 .
sid smith 2014 년

답변:


171

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불렸다을

4
proc에서 break는 예외를 발생
시킵니다

"다음은 블록에서 값을 반환하고 호출을 종료합니다"에서이 정보를 어디서 얻었는지 인용 할 수 있습니까? 나는 그것에 대해 더 읽고 싶다.
user566245

내가 올바르게 기억한다면 그것은 The Ruby Programming Language 책 (지금 당장은 가지고 있지 않습니다) 에서 나온 것입니다. 방금 Google을 확인했고 그 책에서 나온 것 같습니다 : librairie.immateriel.fr/fr/read_book/9780596516178/… 그리고 거기에서 다음 2 페이지 (내 콘텐츠와 내 페이지가 아니라 방금 봤습니다). 그러나 나는 정말로 원본 책을 추천한다. 그것은 훨씬 더 많은 보석을 설명했다.
MBO '2013

또한 나는 머릿속에서 대답하고 irb에서만 확인했기 때문에 내 대답은 기술적이거나 완전하지 않습니다. 자세한 정보는 The Ruby Programming Language 책을 확인하십시오.
MBO '2013

이 답변이 맨 위에 있었으면 좋겠습니다. 충분히 찬성 할 수 없습니다.
btx9000

20

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
# ...

여기에 블록 / 프로 시저에서 복귀하는 방법에 대한 자세한 기사가 있습니다
ComDubh

3

잘못된 관점에서보고 있습니다. 이것은 thing람다가 아니라의 문제입니다 .

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

thing {
  6 * 7
}

1

물건은 어디에서 호출됩니까? 수업 안에 있습니까?

다음과 같은 것을 사용하는 것이 좋습니다.

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

1

루비에서 웹 프레임 워크 용 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

하지만 저에게는 미지의 영역이므로 지금까지 테스트 한 내용을 고수하겠습니다.


1

단점에도 불구하고 이것이 정답이라고 생각합니다.

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을 대체 할 수 있음).


1

방법을 찾았지만 방법을 중간 단계로 정의하는 것이 포함됩니다.

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

thing { return 6 * 7 }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.