Rubyで2つの配列ソート方法を学びました。
array = ["one", "two", "three"]
array.sort.reverse!
または:
array = ["one", "two", "three"]
array.sort { |x,y| y<=>x }
そして、私は両者を区別することができません。どちらの方法が優れていますか、実行方法はどの程度正確に異なりますか?
両方の行は同じです(逆配列された新しい配列を作成します)。主な議論は読みやすさとパフォーマンスについてです。 array.sort.reverse!
はarray.sort{|x,y| y<=>x}
より読みやすい-ここで同意できると思います。
パフォーマンスの部分では、簡単なベンチマークスクリプトを作成しました。これにより、システム(Ruby 1.9.3p392 [x86_64-linux]
)に次のようになります。
user system total real
array.sort.reverse 1.330000 0.000000 1.330000 ( 1.334667)
array.sort.reverse! 1.200000 0.000000 1.200000 ( 1.198232)
array.sort!.reverse! 1.200000 0.000000 1.200000 ( 1.199296)
array.sort{|x,y| y<=>x} 5.220000 0.000000 5.220000 ( 5.239487)
ベンチマークスクリプトを複数回実行しても、実行時間はかなり一定です。
array.sort.reverse
(!
あり/なし)はarray.sort{|x,y| y<=>x}
よりも高速です。したがって、私はそれをお勧めします。
これがリファレンスとしてのスクリプトです:
#!/usr/bin/env Ruby
require 'benchmark'
Benchmark.bm do|b|
master = (1..1_000_000).map(&:to_s).shuffle
a = master.dup
b.report("array.sort.reverse ") do
a.sort.reverse
end
a = master.dup
b.report("array.sort.reverse! ") do
a.sort.reverse!
end
a = master.dup
b.report("array.sort!.reverse! ") do
a.sort!.reverse!
end
a = master.dup
b.report("array.sort{|x,y| y<=>x} ") do
a.sort{|x,y| y<=>x}
end
end
ここで違いはありません。どちらのメソッドも新しい配列を返します。
この例では、単純な方が良いです。 array.sort.reverse
は、他の方法より読みやすいので、お勧めします。 sort
などのメソッドにブロックを渡すことは、より複雑なデータ構造とユーザー定義クラスの配列のために保存する必要があります。
編集:destructive
メソッド(!で終わるもの)はパフォーマンスゲームに適していますが、更新された配列を返すには、必須ではないことが指摘されましたそのことについては。 array.sort.reverse!
はnil
を返す可能性が非常に高いため、これを覚えておくことは重要です。新しく生成された配列で破壊的なメソッドを使用したい場合は、1行ではなく別の行で.reverse!
を呼び出すことをお勧めします。
例:
array = array.sort
array.reverse!
に優先する必要があります
array = array.sort.reverse!
多くの場合、ベンチマークに代わるものはありません。短いスクリプトではおそらく違いはありませんが、#reverse!この方法は、「spaceship」演算子を使用したソートよりも大幅に高速です。たとえば、MRI Ruby 2.0の場合、次のベンチマークコードが与えられます。
require 'benchmark'
array = ["one", "two", "three"]
loops = 1_000_000
Benchmark.bmbm do |bm|
bm.report('reverse!') { loops.times {array.sort.reverse!} }
bm.report('spaceship') { loops.times {array.sort {|x,y| y<=>x} }}
end
システムは#reverse!結合された比較演算子を使用する場合のほぼ2倍の速度です。
user system total real
reverse! 0.340000 0.000000 0.340000 ( 0.344198)
spaceship 0.590000 0.010000 0.600000 ( 0.595747)
私のアドバイス:タイトなループで実行しているのでない限り、特定のコンテキストで意味的に意味のある方を使用します。
比較は例のように単純なので、大きな違いはありませんが、比較の式は複雑になるため、ブロックで<=>
を使用することは避けた方がいいです。渡すブロックは、アレイ、冗長性を引き起こします。このことを考慮:
array.sort{|x, y| some_expensive_method(x) <=> some_expensive_method(y)}
この場合、some_expensive_method
は、array
の要素の可能なペアごとに評価されます。
特定のケースでは、reverse
を使用すると、<=>
を含むブロックの使用を回避できます。
array.sort_by{|x| some_expensive_method(x)}.reverse
これはシュワルツ変換と呼ばれます。
私のマシンでtessiのベンチマークを操作すると、興味深い結果が得られます。 Ruby 2.0.0p195 [x86_64-darwin12.3.0]
、つまり、OS XシステムではRuby 2の最新リリースです。ベンチマークモジュールのbm
ではなく bmbm を使用しました。私のタイミング次のとおりです。
Rehearsal -------------------------------------------------------------
array.sort.reverse: 1.010000 0.000000 1.010000 ( 1.020397)
array.sort.reverse!: 0.810000 0.000000 0.810000 ( 0.808368)
array.sort!.reverse!: 0.800000 0.010000 0.810000 ( 0.809666)
array.sort{|x,y| y<=>x}: 0.300000 0.000000 0.300000 ( 0.291002)
array.sort!{|x,y| y<=>x}: 0.100000 0.000000 0.100000 ( 0.105345)
---------------------------------------------------- total: 3.030000sec
user system total real
array.sort.reverse: 0.210000 0.000000 0.210000 ( 0.208378)
array.sort.reverse!: 0.030000 0.000000 0.030000 ( 0.027746)
array.sort!.reverse!: 0.020000 0.000000 0.020000 ( 0.020082)
array.sort{|x,y| y<=>x}: 0.110000 0.000000 0.110000 ( 0.107065)
array.sort!{|x,y| y<=>x}: 0.110000 0.000000 0.110000 ( 0.105359)
まず、リハーサル段階ではsort!
比較ブロックを使用することは明らかな勝者です。マッツはRuby 2!
私が非常に奇妙だと思ったもう1つのことは、どれほどの改善array.sort.reverse!
およびarray.sort!.reverse!
生産パスに出品。非常に極端だったので、これらの既に並べ替えられたデータをなんとか台無しにして渡したかどうか疑問に思ったので、各ベンチマークを実行する前に、並べ替えまたは逆に並べ替えられたデータの明示的なチェックを追加しました。
Tessiのスクリプトの私の変種は次のとおりです。
#!/usr/bin/env Ruby
require 'benchmark'
class Array
def sorted?
(1...length).each {|i| return false if self[i] < self[i-1] }
true
end
def reversed?
(1...length).each {|i| return false if self[i] > self[i-1] }
true
end
end
master = (1..1_000_000).map(&:to_s).shuffle
Benchmark.bmbm(25) do|b|
a = master.dup
puts "uh-oh!" if a.sorted?
puts "oh-uh!" if a.reversed?
b.report("array.sort.reverse:") { a.sort.reverse }
a = master.dup
puts "uh-oh!" if a.sorted?
puts "oh-uh!" if a.reversed?
b.report("array.sort.reverse!:") { a.sort.reverse! }
a = master.dup
puts "uh-oh!" if a.sorted?
puts "oh-uh!" if a.reversed?
b.report("array.sort!.reverse!:") { a.sort!.reverse! }
a = master.dup
puts "uh-oh!" if a.sorted?
puts "oh-uh!" if a.reversed?
b.report("array.sort{|x,y| y<=>x}:") { a.sort{|x,y| y<=>x} }
a = master.dup
puts "uh-oh!" if a.sorted?
puts "oh-uh!" if a.reversed?
b.report("array.sort!{|x,y| y<=>x}:") { a.sort!{|x,y| y<=>x} }
end