1

アイテムが列挙に表示される回数を数えています。

irb(main):003:0> (1..3).reduce(0) {|sum, p| sum += 1 if p == 1}
=> nil
irb(main):004:0> (1..3).find_all{|p| p == 1}.length
=> 1

reduce メソッドは、find_all メソッドと同じ動作をする必要があるようです。nilの代わりに返されるのはなぜ1ですか?

irb(main):023:0> (1..3).reduce(0) {|sum, p| sum += 1 if p == 2}
NoMethodError: undefined method `+' for nil:NilClass
    from (irb):23:in `block in irb_binding'
    from (irb):23:in `each'
    from (irb):23:in `reduce'
    from (irb):23
    from /usr/bin/irb:12:in `<main>'

最初の反復で何かがうまくいかない。このように使用しないだけで削減できますか?

4

2 に答える 2

7

reduce では、ブロック内のコードの値がアキュムレータに割り当てられます。あなたの場合、最初の割り当てをオーバーライドして、後の nil で合計します。

これは次の方法で修正できます。

(1..3).reduce(0) {|sum, p| sum += 1 if p == 1; sum}

また

(1..3).reduce(0) {|sum, p| sum += p == 1 ? 1 : 0}

2 番目の例では、最初の反復で sum に nil が割り当てられ、2 番目の反復で nil に 1 を追加しようとしています。

reduce/inject はおそらくカウントに最適な手段ではないことに注意してください - 試してみてください

(1..3).count(1)
于 2012-09-19T04:30:03.403 に答える
5

Enumerable#reduceメソッドに渡されたブロックからの戻り値 (または最後の値) は、呼び出しごとにアキュムレータの新しい値として常にsum+=1格納されるため、その場で合計をインクリメントする ( ) のは誤解を招きます。ブロックは期待される値を返しますp==1が、それnil以外の場合は、アキュムレータが上書きされます。

常にアキュムレータ (合計) の期待値を返すようにブロックを変更してみてください。

(1..3).reduce(0) { |sum,p| (p==1) ? sum+1 : sum }

reduce メソッドの実装での呼び出しシーケンスは次のようになります。

acc = 0
acc = yield(acc, 1) # (sum=0, p=1) => sum+1 => 1
acc = yield(acc, 2) # (sum=1, p=2) => sum => 1
acc = yield(acc, 3) # (sum=1, p=3) => sum => 1
acc # => 1
于 2012-09-19T04:57:13.477 に答える