web-dev-qa-db-ja.com

文字列の配列をRuby

Rubyで2つの配列ソート方法を学びました。

array = ["one", "two", "three"]
array.sort.reverse!

または:

array = ["one", "two", "three"]
array.sort { |x,y| y<=>x }

そして、私は両者を区別することができません。どちらの方法が優れていますか、実行方法はどの程度正確に異なりますか?

22
poorvank

両方の行は同じです(逆配列された新しい配列を作成します)。主な議論は読みやすさとパフォーマンスについてです。 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
31
tessi

ここで違いはありません。どちらのメソッドも新しい配列を返します。

この例では、単純な方が良いです。 array.sort.reverseは、他の方法より読みやすいので、お勧めします。 sortなどのメソッドにブロックを渡すことは、より複雑なデータ構造とユーザー定義クラスの配列のために保存する必要があります。

編集:destructiveメソッド(!で終わるもの)はパフォーマンスゲームに適していますが、更新された配列を返すには、必須ではないことが指摘されましたそのことについては。 array.sort.reverse!nilを返す可能性が非常に高いため、これを覚えておくことは重要です。新しく生成された配列で破壊的なメソッドを使用したい場合は、1行ではなく別の行で.reverse!を呼び出すことをお勧めします。

例:

array = array.sort
array.reverse!

に優先する必要があります

array = array.sort.reverse!
6
James Brewer

逆行する!速いです

多くの場合、ベンチマークに代わるものはありません。短いスクリプトではおそらく違いはありませんが、#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)

私のアドバイス:タイトなループで実行しているのでない限り、特定のコンテキストで意味的に意味のある方を使用します。

3
Todd A. Jacobs

比較は例のように単純なので、大きな違いはありませんが、比較の式は複雑になるため、ブロックで<=>を使用することは避けた方がいいです。渡すブロックは、アレイ、冗長性を引き起こします。このことを考慮:

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

これはシュワルツ変換と呼ばれます。

2
sawa

私のマシンで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
2
pjs