配列 [X,Y] の配列が与えられた場合:
a=[[1,2],[2,2],[3,2],[4,2],[5,2],[6,2]]
のすべての Y 桁を合計する最も効率的な方法は2<=X<4
?
配列 [X,Y] の配列が与えられた場合:
a=[[1,2],[2,2],[3,2],[4,2],[5,2],[6,2]]
のすべての Y 桁を合計する最も効率的な方法は2<=X<4
?
私はこれで作業します:
a.select{ |x,y| (2...4) === x }.inject(0){ |m, (x,y)| m + y }
=> 4
私はあまり使いたくありません...
。なぜなら、それがどのように機能するかによって人々を混乱させるからです。同等のテスト方法を次に示します。
a.select{ |x,y| (2..3) === x }.inject(0){ |m, (x,y)| m + y }
ary.select{ |x,y| (2 <= x) && (x < 4) }.inject(0){ |m, (x,y)| m + y } } }
ベンチマークコードは次のとおりです。
require 'benchmark'
a = [ [1,2], [2,2], [3,2], [4,2], [5,2], [6,2] ]
n = 1_000_000
Benchmark.bm(12) do |b|
b.report('The Tin Man') { n.times { a.select{ |x,y| (2...4) === x }.inject(0){ |m, (x,y)| m + y } } }
b.report('The Tin Man2') { n.times { a.select{ |x,y| (2 <= x) && (x < 4) }.inject(0){ |m, (x,y)| m + y } } }
b.report('Mik_Die') { n.times { a.select{ |i| (2...4).include? i[0] }.map(&:last).reduce(:+) } }
b.report('Justin Ko') { n.times { a.inject(0){ |sum, coord| (coord[0] >= 2 and coord[0] < 4) ? sum + coord[1] : sum } } }
b.report('Justin Ko2') { n.times { a.inject(0){ |sum, (x,y)| (x >= 2 and x < 4) ? sum + y : sum } } }
b.report('Leo Correa') { n.times { sum = 0; a.each { |x, y| sum += y if x >= 2 and x < 4 } } }
b.report('tokland') { n.times { a.map { |x, y| y if x >= 2 && x < 4 }.compact.inject(0, :+) } }
end
そしてその出力:
ユーザーシステム合計実数 ブリキ男 4.020000 0.000000 4.020000 ( 4.020154) ブリキ男2 2.420000 0.000000 2.420000 ( 2.424424) Mik_Die 3.830000 0.000000 3.830000 ( 3.836531) ジャスティン・コー 2.070000 0.000000 2.070000 ( 2.072446) ジャスティン Ko2 2.000000 0.000000 2.000000 (2.035079) レオ・コレア 1.260000 0.000000 1.260000 ( 1.259672) トクランド 2.650000 0.010000 2.660000 ( 2.645466)
ここで学んだ教訓inject
は、コストがかかるということです。
私は使用しますinject
:
a = [[1,2],[2,2],[3,2],[4,2],[5,2],[6,2]]
sum = a.inject(0){ |sum, (x,y)| (x >= 2 and x < 4) ? sum + y : sum }
puts sum
#=> 4
rdoc はこのinject
方法をよく説明しています:
注入(初期){| メモ、obj | ブロック } → obj
メソッドまたは演算子を指定するブロックまたはシンボルで指定された二項演算を適用して、enum のすべての要素を結合します。
ブロックを指定すると、enum の各要素に対して、ブロックにアキュムレータ値 (メモ) と要素が渡されます。代わりにシンボルを指定すると、コレクション内の各要素が memo の名前付きメソッドに渡されます。どちらの場合も、結果が memo の新しい値になります。反復の最後に、 memo の最終値がメソッドの戻り値になります。
memo の初期値を明示的に指定しない場合は、コレクションの最初の要素が memo の初期値として使用されます。
更新 - ベンチマーク配列とアンパック:
@tokland は、ペアをアンパックすることを提案していました。これにより、読みやすさが確実に向上します。次のベンチマークは、配列を使用するよりも高速かどうかを確認するために実行されました (つまり、私の元のソリューション)。
require 'benchmark'
a = [ [1,2], [2,2], [3,2], [4,2], [5,2], [6,2] ]
n = 2_000_000
Benchmark.bm(12) do |b|
b.report('array'){n.times{a.inject(0){ |sum, coord| (coord[0] >= 2 and coord[0] < 4) ? sum + coord[1] : sum }}}
b.report('unpacked'){n.times{a.inject(0){ |sum, (x,y)| (x >= 2 and x < 4) ? sum + y : sum }}}
end
結果を出したのは
user system total real
array 3.916000 0.000000 3.916000 ( 3.925393)
unpacked 3.619000 0.000000 3.619000 ( 3.616361)
したがって、少なくともこの場合は、ペアをアンパックする方が適切です。
@JustinKo が提供した注入の回答が気に入っていますが、Ruby を初めて使用する場合に理解しやすい別の解決策を次に示します。
a=[[1,2],[2,2],[3,2],[4,2],[5,2],[6,2]]
sum = 0
a.each { |x, y| sum += y if x >= 2 and x < 4 }
puts sum
#=> 4
Ruby では、より単純なメソッドのチェーンを使用する方が明確です。そう:
a=[[1,2],[2,2],[3,2],[4,2],[5,2],[6,2]]
a.select{ |i| (2...4).include? i[0] }.map(&:last).reduce(:+)
# => 4
概念的に使用したいのはリスト内包表記です。残念ながら、Ruby には LC 用の組み込み構文はありませんが、compact+map を使用すると問題なく機能します。
a.map { |x, y| y if x >= 2 && x < 4 }.compact.inject(0, :+)
#=> 4
中規模/大規模なスクリプトを作成している場合は、おそらく拡張モジュールを持っているでしょう (そして持つべきです)。必要なメソッドを追加して、宣言的で簡潔なコードを記述できるようにします。
a.map_select { |x, y| y if x >= 2 && x < 4 }.sum
あるいは:
a.sum { |x, y| y if x >= 2 && x < 4 }