95

「エンドユーザー」コードがRubyブロックに書き込まれるように、組み込みスクリプト言語にRuby 1.9.1を使用しようとしています。これに関する 1 つの問題は、ユーザーがブロック内で「return」キーワードを使用できるようにして、暗黙の戻り値について心配する必要がないようにしたいということです。これを念頭に置いて、これは私ができるようにしたいことです:

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

thing {
  return 6 * 7
}

上記の例で「return」を使用すると、LocalJumpError が発生します。これは、問題のブロックがラムダではなく Proc であるためであることは承知しています。「return」を削除してもコードは機能しますが、このシナリオでは「return」を使用できるようにしたいと思います。これは可能ですか?ブロックをラムダに変換しようとしましたが、結果は同じです。

4

8 に答える 8

181

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またはによって呼び出された場合.callbreakこのイテレータからもブレークします
  • nextブロックから値を返し、その呼び出しを終了します。ブロックがyieldまたはによって呼び出された場合.call、呼び出されnextた行に値を返しますyield
于 2010-02-24T11:55:39.090 に答える
21

それはRubyではできません。

returnキーワードは常に、現在のコンテキストのメソッドまたはラムダから返されます。ブロックでは、クロージャーが定義されたメソッドから戻ります。呼び出し元のメソッドまたはラムダから返すことはできません。

Rubyspecは、これが実際に Ruby の正しい動作であることを示しています (確かに実際の実装ではありませんが、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
# ...
于 2010-02-24T11:42:53.110 に答える
3

あなたは間違った視点からそれを見ています。これはthingラムダの問題ではなく、 の問題です。

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

thing {
  6 * 7
}
于 2010-02-24T11:33:48.443 に答える
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
}

このハックにより、ユーザーは結果を伴わずにプロシージャで return を使用したり、self を保持したりできます。

ここで Thread を使用する利点は、場合によっては LocalJumpError を取得しないことです。また、最も予期しない場所で戻りが発生します (最上位のメソッドの上で、予期せず本体の残りをスキップします)。

yield主な欠点は、潜在的なオーバーヘッドです (シナリオで十分な場合は、スレッド + 結合を単に置き換えることができます)。

于 2015-05-27T15:16:28.123 に答える
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
于 2010-02-24T11:32:05.997 に答える