Nice programing

Ruby 블록에서 'return'사용

nicepro 2020. 10. 4. 13:22
반응형

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

반응형