2013-11-27 24 views
3

我經歷上CodeAcademy一些教程,並跨越這種情況下傳來:<=>如何適用於不同的分揀策略?

books = ["Charlie and the Chocolate Factory", "War and Peace", "Utopia", "A Brief History of Time", "A Wrinkle in Time"] 

# To sort our books in ascending order, in-place 
books.sort! { |firstBook, secondBook| firstBook <=> secondBook } 


# Sort your books in descending order, in-place below 
# this lin initially left blank 
books.sort! {|firstBook, secondBook| secondBook <=> firstBook} 

而不是使用if/else塊,我這就給了一槍,和它的工作,但我不知道爲什麼。我假設您將這些物品放入支票的順序並不重要(即a <=> bb <=> a)。有人能解釋這裏發生了什麼嗎?

+0

如果顛倒的項目則排序改變像降序的順序和升序 – uday

+1

I * HOPE *爲Ruby的教程沒有建議你使用駝峯你的變量。在Ruby中,我們使用snake_case作爲變量。 –

+0

我真的不記得 - 這只是個人喜好。我真的不喜歡在變量名中看到_。 – MrDuk

回答

5

如果您顛倒<=>中的元素,則會顛倒其值。如果元素相等,則此運算符返回0,但如果第一個元素較小,則返回負值,如果第一個元素較大,則返回正值。因此,如果temp = a <=> b然後b <=> a-temp。所以如果你以相反的順序寫入參數,你可以顛倒排序順序。

+0

謝謝,但我仍然有點困惑 - 也許我的問題應該是,-1 vs 1的意義是什麼?我得到的結果是-1小於對象,1更大,但是一旦我們通過它,排序策略如何處理該值? – MrDuk

+1

@cote所謂的太空船運營商'<=>'是比較基礎。它用於確定兩個元素相對於另一個元素的排序順序。如果「-1」,則第一個在第二個之前,如果是「1」,則第一個在第二個之後。如果'0'從排序角度看被認爲是完全相同的,並且不應用排序。 – tadman

+1

@ctote想象你有一個排序算法,如果'a'小於'b',則會執行某些操作。如果你顛倒了這個運算符參數的順序,算法將執行相同的操作,但是它的'a'小於'b'的概念將被顛倒過來。所以在第一種情況下,只要'a'小於'b',第二種情況就會變得更大。嘗試並寫下簡單的氣泡排序。如果您比較'一個 B' –

0

Its called a spaceship operator

如果你有這樣的事情

my_array = ["b","c","a"]

my_array.sort!也比較數組中的元素,因爲它知道英文字母表的字母有自然順序,同樣的,如果你有整數陣列

my_array2 = [3,1,2]

my_array2.sort!將比較的元素,並給出了結果作爲[1,2,3]

,但如果你想改變的比較是如何在你它使用<=>指定字符串或複雜對象的數組做運營商..

my_array3 = ["hello", "world how are" , "you"]

my_array3.sort! { |first_element, second_element| first_element <=> second_element }

所以它會告訴排序的方法來比較是這樣的:

first_element < second_element

first_element = second_element

first_element>second_element

,但如果你把這個語句,

my_array3.sort! { |first_element, second_element| first_element <=> second_element }

在進行比較如下:

是second_element < first_element?

Is second_element = first_element?

是second_element> first_element?

因此,如果您更改要考慮的元素,它確實會有所幫助。

+0

它通常被稱爲「太空船操作員」。它始終是一個Perl中的二進制比較運算符,這是Ruby從中繼承而來的。 –

+0

此外,使用snake_case,而不是camelCase在Ruby中的變量。而且,你的例子不起作用。使用'first_element'和'second_element'並確保您的作業和變量拼寫一致。使用IRB測試您的示例是一個不錯的主意,然後在他們工作後複製並粘貼它們。 –

+0

@TheTinMan,謝謝更正! :)和測試:D – uday

3

下面是看一下<=>做什麼的一些簡單的直觀方式,以及如何顛倒比較變量的順序會影響輸出的順序。

與基本陣列開始:

foo = %w[a z b x] 

我們可以做一個升序排序:

foo.sort { |i, j| i <=> j } # => ["a", "b", "x", "z"] 

或者通過顛倒兩個變量降序排序進行比較:

foo.sort { |i, j| j <=> i } # => ["z", "x", "b", "a"] 

<=>運算符返回-1,0或1,具體取決於比較結果分別爲<==>

我們可以通過否定比較的結果來進行測試,如果理論成立的話,將會顛倒順序。

foo.sort { |i, j| -(i <=> j) } # => ["z", "x", "b", "a"] 
foo.sort { |i, j| -(j <=> i) } # => ["a", "b", "x", "z"] 

通過否定比較的結果,順序反轉。但是,爲了使代碼清晰,只需顛倒變量的順序即可。

這一切都說,使用sort或其破壞性的兄弟sort!,並不總是排序複雜對象的最快方法。簡單的對象,如字符串和字符以及數字,排序非常快,因爲它們的類實現了快速執行<=>測試的必要方法。

一些答案和評論提到sort_by,所以我們去那裏吧。

複雜對象通常不會正確排序,因此我們最終使用getters/accessors來檢索某些我們想要比較的值,並且該操作在CPU時間中有成本。 sort反覆比較這些值,以便重複進行檢索,並且在沒有發生排序時將其合併爲浪費的時間。

爲了解決這個問題,一位名叫Randall Schwartz的聰明人開始使用算法,該算法預先計算一次用於排序的值;結果,該算法通常被稱爲Schwartzian Transform。該值和實際對象一起捆綁在一個小的子數組中,然後進行排序。由於排序是針對預先計算的值進行的,因此它及其關聯的對象將在排序中移動,直到排序完成。此時,實際的對象被檢索並作爲該方法的結果返回。 Ruby使用sort_by來實現這種類型的排序。

sort_by不使用<=>外部,所以你可以通過簡單地告訴它如何獲得你想要比較的值進行排序:

class Foo 
    attr_reader :i, :c 
    def initialize(i, c) 
    @i = i 
    @c = c 
    end 
end 

這裏的對象數組。請注意,他們是在創建它們的順序,但不排序:

foo.sort_by{ |f| f.i } 
# => [#<Foo:0x007f97d1061d80 @c="z", @i=1>, 
#  #<Foo:0x007f97d1061d30 @c="x", @i=2>, 
#  #<Foo:0x007f97d1061ce0 @c="b", @i=25>, 
#  #<Foo:0x007f97d1061d58 @c="a", @i=26>] 

由字符值對它們進行排序:

foo.sort_by{ |f| f.c } 
# => [#<Foo:0x007f97d1061d58 @c="a", @i=26>, 
#  #<Foo:0x007f97d1061ce0 @c="b", @i=25>, 
#  #<Foo:0x007f97d1061d30 @c="x", @i=2>, 
#  #<Foo:0x007f97d1061d80 @c="z", @i=1>] 

foo = [[1, 'z'], [26, 'a'], [2, 'x'], [25, 'b'] ].map { |i, c| Foo.new(i, c) } 
# => [#<Foo:0x007f97d1061d80 @c="z", @i=1>, 
#  #<Foo:0x007f97d1061d58 @c="a", @i=26>, 
#  #<Foo:0x007f97d1061d30 @c="x", @i=2>, 
#  #<Foo:0x007f97d1061ce0 @c="b", @i=25>] 

由整數值對它們進行排序sort_by不響應,以及使用一個否定的值作爲sort<=>,因此,基於some benchmarks做了一段時間後對堆棧溢出,我們知道,在所得到的值用reverse是從上升到下降開關訂單的最快方法:

foo.sort_by{ |f| f.i }.reverse 
# => [#<Foo:0x007f97d1061d58 @c="a", @i=26>, 
#  #<Foo:0x007f97d1061ce0 @c="b", @i=25>, 
#  #<Foo:0x007f97d1061d30 @c="x", @i=2>, 
#  #<Foo:0x007f97d1061d80 @c="z", @i=1>] 

foo.sort_by{ |f| f.c }.reverse 
# => [#<Foo:0x007f97d1061d80 @c="z", @i=1>, 
#  #<Foo:0x007f97d1061d30 @c="x", @i=2>, 
#  #<Foo:0x007f97d1061ce0 @c="b", @i=25>, 
#  #<Foo:0x007f97d1061d58 @c="a", @i=26>] 

他們有點互換,但你要記住,sort_by確實有開銷,這是顯而易見的,當您在對簡單對象運行時將其時間與sort時間進行比較。在正確的時間使用正確的方法,你可以看到戲劇性的加速。

相關問題