5

Enumerator で Yielder を使用する場合と Enumerator で単に yield を呼び出す場合の違いを誰かが理解するのを手伝ってくれれば幸いです。

「地に足のついた Rubyist」は、「ブロックから譲歩する」のではなく、何が起こっているのかを正確に説明しないことを示唆しています。

ありがとう

4

3 に答える 3

5

yield がどのように機能するかを最初に理解しておくと役立つ場合があります。次に例を示します。

def do_stuff
  if block_given?
    yield 5
  else
    5
  end
end

result = do_stuff {|x| x * 3 }
puts result

--output:--
15

do_stuff メソッド呼び出しで:

do_stuff {|x| x * 3 }

..ブロックは関数のようなもので、メソッド do_stuff に渡されます。do_stuff 内で、yield は関数を呼び出し、指定された引数 (この場合は 5) を渡します。

注意すべき重要事項:

  1. yield はメソッド内で呼び出されます

  2. メソッドを呼び出すときに、メソッドにブロックを渡すことができます

  3. ブロックの呼び出しには yield が使用されます。

では、コメントの質問を見てみましょう。

それは本当ですか

e = Enumerator.new do |y| 
  y << 1 
  y << 2 
  y << 3 
end 

とまったく同じです

e = Enumerator.new do   #I think you forgot to write .new here
    yield 1 
    yield 2 
    yield 3 
end

2 番目の例では、メソッド定義がどこにもないため、yield を呼び出すことはできません。エラー!したがって、2 つの例は同じではありません。

ただし、これを行うことができます:

def do_stuff
  e = Enumerator.new do 
      yield 1 
      yield 2 
      yield 3 
  end 
end

my_enum = do_stuff {|x| puts x*3}
my_enum.next

--output:--
3
6
9
1.rb:12:in `next': iteration reached an end (StopIteration)
    from 1.rb:12:in `<main>'

しかし、それは値を生成しないため、面白い列挙子です。コードを実行するだけで (たまたま出力が出力されます)、終了します。その列挙子は、次のものとほぼ同等です。

def do_stuff
  e = Enumerator.new do 
  end 
end

my_enum = do_stuff
my_enum.next

--output:--
1.rb:7:in `next': iteration reached an end (StopIteration)
    from 1.rb:7:in `<main>'

列挙子が値を生成できない場合、StopIteration 例外が発生します。したがって、どちらの場合も、列挙子は値を生成できませんでした。

しかし、「降伏者」が何をしているのかはまだはっきりしていません。後で列挙子を使用するときにそれらを逆流できるように、計算されたすべての値を収集しているようです。その場合、「小さな」シーケンスに対してのみ実用的であるように思われます.... 5000万個のアイテムを格納する列挙子を作成したくないでしょう。

いいえ。実際には、無限の数の値を生成する列挙子を作成できます。次に例を示します。

e = Enumerator.new do |y|
  val = 1

  while true
    y << val
    val += 1
  end

end

puts e.next
puts e.next
puts e.next

--output:--
1
2
3

いくつかのデバッグ メッセージを追加すると、洞察が得られるはずです。

e = Enumerator.new do |y|
  val = 1

  while true
    puts "in while loop"
    y << val
    val += 1
  end

end

puts e.next

--output:--
in while loop
1

メッセージは 1 回だけ出力されることに注意してください。そのため、明らかではない何かが起こっています。

e = Enumerator.new do |y|
  val = 1

  while true
    puts "in while loop"
    y << val
    puts "just executed y << val"
    val += 1
  end

end

puts e.next

--output:--
in while loop
1

「just execute y << val」というメッセージが出力に表示されないため、行で実行が停止したに違いありませんy << val。したがって、構文は値を配列にプッシュするのとまったく同じですが、列挙子は while ループを継続的にスピンし、すべての値を y に挿入しませんでしたarr << val

本当の意味は、 e.next y << val() が呼び出されたときにこの値を生成し、次の行で実行を継続するということです。前の例に別の e.next を追加すると、次の追加出力が表示されます。

just executed y << val
in while loop
2

何が起こっているかというとy << val、コード内で が検出されると実行が常に停止するということです。次に e.next を呼び出すと、右側に値が生成され、次の行で実行が続行されます。

ruby が yielder ステートメントの構文を次のようにしていたら、おそらくもっと意味があったでしょう。

y >> val

そして、それを意味として解釈することができます: ここで実行を停止し、e.next が呼び出されたときに val を生成します。

David Black は、このy.yield val構文を使用しないことを推奨しています。これはy << val、yield ステートメントと同様に機能すると読者が考えないようにするためです。 は次のように解釈する必要があります: "ここで実行を停止し、next が呼び出されたときに val を生成し、次の行で実行を続行します。個人的には、構文が よりも目立つy.yield valと思うので、コード内で見つけやすく、簡単に識別できます。実行が停止する場所。 y << valy.yield val

于 2013-08-12T03:45:57.183 に答える
3

まあ、私が何かを見逃していない限り、 の方法はyield単に機能しません。それを試してみてください:

e = Enumerator.new do |y|
  y << 1
  y << 2
  y << 3
end

f = Enumerator.new do
  yield 1
  yield 2
  yield 3
end

e.each { |x| puts x }
f.each { |x| puts x }

これが生成されます:

telemachus ~ $ ruby yield.rb 
1
2
3
yield.rb:13:in `block in <main>': no block given (yield) (LocalJumpError)
        from yield.rb:19:in `each'
        from yield.rb:19:in `each'
        from yield.rb:19:in `<main>

彼が(304ページ)「あなたはこれをしないでください」と言うとき、彼は「それが最善の方法ではない」という意味ではありません. 彼は「それはうまくいかない」という意味です。

編集: ただし、次のように明示的に yield を呼び出すことができます。

e = Enumerator.new do |y|
  y.yield 1
  y.yield 2
  y.yield 3
end

yieldより明確または明確に言っ<<ていることがわかった場合は、そのようにしてください。

2番目の編集:Davidの元の投稿とJorgの更新された回答を見ると、もともと質問について混乱があったと思います. Enumerator::Yielder#yieldJorg は David がとの違いについて質問していると思ったが、David はThe Well Grounded Rubyistが「don't write etc.」と言ったときにEnumerator::Yielder::<<何を意味するのかわからなかった。私の答えはThe Well Grounded Rubyistに関する質問に当てはまります。(今日このスレッドを振り返ったとき、他の更新に照らして私の答えは奇妙に見えました。)yield 1

于 2009-06-15T01:17:40.290 に答える
2

Enumerator::Yielder#yield方法とEnumerator::Yielder::<<方法はまったく同じです。実際、それらはエイリアスです。

したがって、これら2つのうちどちらを使用するかは、Enumerable#collectandEnumerable#mapまたはEnumerable#injectandと同様に、100%個人的な好みEnumerable#reduceです。

于 2009-06-14T23:12:15.503 に答える