在Ruby中对字符串数组进行排序

我在Ruby中学到了两种数组排序方法:

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

要么:

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

我无法区分这两者。 哪种方法更好,它们在执行方面究竟有何不同?

两条线都相同(创建一个新的数组,反向排序)。 主要论点是关于可读性和性能。 array.sort.reverse!array.sort{|x,y| y<=>x}更具可读性 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}更快 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 。 如果你想在新生成的数组上使用破坏性方法,你应该更喜欢调用.reverse! 在一条单独的线上,而不是一个单线。

例:

 array = array.sort array.reverse! 

应该优先考虑

 array = array.sort.reverse! 

相反! 是比较快的

基准测试通常无法替代。 虽然它可能对较短的脚本没有区别,但#reverse! 方法明显快于使用“太空船”操作员进行排序。 例如,在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! 几乎是使用组合比较运算符的两倍。

  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)} 

在这种情况下,将为每个可能的array元素评估some_expensive_method

在您的特定情况下,可以通过reverse避免使用带<=>的块。

 array.sort_by{|x| some_expensive_method(x)}.reverse 

这称为Schwartzian变换。

在我的机器上使用tessi的基准测试时,我得到了一些有趣的结果。 我正在运行ruby 2.0.0p195 [x86_64-darwin12.3.0] ,即OS X系统上最新版本的Ruby 2。 我在Benchmark模块中使用了bmbm而不是bm 。 我的时间是:

 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! 使用比较块作为明显的赢家。 Matz肯定已经在Ruby 2中调整了它!

我发现的另一件非常奇怪的事情是array.sort.reverse!多大改进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