Ruby 程式設計/資料型別
如前一章所述,Ruby 中的一切都是物件。Ruby 有 8 種主要資料型別,以及 3 種從 Numeric 超類派生的資料型別。一切都有類。不信?試試執行這段程式碼
h = {"hash?" => "yep, it\'s a hash!", "the answer to everything" => 42, :linux => "fun for coders."}
puts "Stringy string McString!".class
puts 1.class
puts 1.class.superclass
puts 1.class.superclass.superclass
puts 4.3.class
puts 4.3.class.superclass
puts nil.class
puts h.class
puts :symbol.class
puts [].class
puts (1..8).class
顯示
String
Fixnum
Integer
Numeric
Float
Numeric
NilClass
Hash
Symbol
Array
Range
看到了吧?一切都 是物件。每個物件都有一個名為class的方法,它返回該物件的類。你幾乎可以對任何東西呼叫方法。之前你看到了 3.times 形式的例子。(從技術上講,當你呼叫一個方法時,你是在向物件傳送訊息,但我將把這一點的重要性留到以後。)
對我來說,這種極端的 面向物件特性非常有趣,因為所有類都是開放的,這意味著你可以在程式碼執行期間隨時向類新增變數和方法。不過,這與資料型別討論無關。
我們先從常量開始,因為它們很簡單。關於常量,有兩點需要注意
1. 常量以大寫字母開頭。Constant是一個常量。constant不是常量。
2. 你可以更改常量的值,但 Ruby 會給出警告。(我知道這很蠢...... 但你能怎麼辦呢?)
恭喜。現在你已經成為 Ruby 常量的專家了。
你是否注意到第一個程式碼列表中有一些奇怪的東西?“那個冒號到底是怎麼回事?”嗯,恰好 Ruby 的面向物件方式有成本:大量的物件會導致程式碼變慢。每次你輸入一個字串時,Ruby 都會建立一個新物件。無論兩個字串是否相同,Ruby 都將每個例項視為一個新物件。你的程式碼中可能有一次出現“live long and prosper”,然後再次出現,Ruby 甚至不會意識到它們幾乎是一樣的東西。下面是一個示例 irb 會話,演示了這一事實
irb> "live long and prosper".object_id
=> -507772268
irb> "live long and prosper".object_id
=> -507776538
請注意,irb Ruby 返回的 物件 ID 即使對於相同的兩個字串也是不同的。
為了解決這種記憶體消耗問題,Ruby 提供了“符號”。Symbol 是輕量級物件,最適合用於比較和內部邏輯。如果使用者永遠看不到它,為什麼不使用符號而不是字串呢?你的程式碼會感謝你的。讓我們嘗試使用符號而不是字串執行上面的程式碼
irb> :my_symbol.object_id
=> 150808
irb> :my_symbol.object_id
=> 150808
符號由前面的冒號表示,如下所示::symbol_name
雜湊在某種程度上就像字典。你有一個鍵,一個引用,你查詢它以找到關聯的物件,即定義。
我認為,最好的說明方法是透過快速演示
hash = { :leia => "Princess from Alderaan", :han => "Rebel without a cause", :luke => "Farmboy turned Jedi"}
puts hash[:leia]
puts hash[:han]
puts hash[:luke]
顯示
Princess from Alderaan
Rebel without a cause
Farmboy turned Jedi
我也可以這樣寫
hash = { :leia => "Princess from Alderaan", :han => "Rebel without a cause", :luke => "Farmboy turned Jedi"}
hash.each do |key, value|
puts value
end
這段程式碼遍歷雜湊中的每個元素,將鍵放在 key 變數中,將值放在 value 變數中,然後顯示該值
Princess of Alderaan
Rebel without a cause
Farmboy turned Jedi
我本可以更詳細地定義我的雜湊;我可以這樣寫
hash = Hash.[](:leia => "Princess from Alderaan", :han => "Rebel without a cause", :luke => "Farmboy turned Jedi")
hash.each do |key, value|
puts value
end
如果我想殺了盧克,我可以這樣做
hash.delete(:luke)
現在盧克不再在雜湊中了。或者假設我討厭所有農場男孩。我可以這樣做
hash.delete_if {|key, value| value.downcase.match("farmboy")}
這將遍歷每個鍵值對並將其刪除,但前提是它後面的程式碼塊返回 true。在程式碼塊中,我將值轉換為小寫(以防農場男孩開始做諸如“FaRmBoY!1!”之類的事情),然後檢查“farmboy”是否與它的內容中的任何內容匹配。我本可以使用正則表示式,但那是另一回事了。
我可以透過將新值分配給雜湊來將蘭多加入進來
hash[:lando] = "Dashing and debonair city administrator."
我可以使用 hash.length 測量雜湊。我可以使用 hash.keys 方法檢視只有鍵,該方法將雜湊的鍵作為 Array 返回。說到這個......
Array 很像 Hash,除了鍵總是連續的數字,並且總是從 0 開始。在一個包含五個專案的 Array 中,最後一個元素將位於 array[4],而第一個元素將位於 array[0]。此外,你剛學到的所有 Hash 方法也可以應用於 Array。
以下兩種方法可以建立 Array
array1 = ["hello", "this", "is", "an", "array!"]
array2 = []
array2 << "This" # index 0
array2 << "is" # index 1
array2 << "also" # index 2
array2 << "an" # index 3
array2 << "array!" # index 4
正如你可能猜到的那樣,<< 運算子將值推送到 Array 的末尾。如果我在宣告這兩個 Array 後編寫 puts array2[4],則輸出將為 array!。當然,如果我想同時獲得 array! 並將其從陣列中刪除,我可以簡單地 Array.pop 它。 Array.pop 方法返回陣列中的最後一個元素,然後立即將其從該陣列中刪除
string = array2.pop
然後 string 將儲存 array!,而 array2 將減少一個元素。
如果我一直這樣做,array2 將不再包含任何元素。我可以透過呼叫 Array.empty? 方法檢查此條件。例如,以下程式碼段將所有元素從一個 Array 移動到另一個 Array
array1 << array2.pop until array2.empty?
這裡有一件讓我非常興奮的事:Array 可以互相減去和加起來。我不能保證所有語言都這樣,但我確信如果我嘗試執行以下程式碼段,Java、C++、C# 和 perl 會覺得我瘋了
array3 = array1 - array2
array4 = array1 + array2
在評估該程式碼後,以下所有內容都為真
array3包含array1中的所有元素,除了那些也存在於array2中的元素。- 現在
array3包含了array1中的所有元素,減去array2中的元素。 array4現在包含了array1和array2中的所有元素。
你可以使用 Array.include? 方法在 array1 變數中搜索特定值:array1.include?("Is this in here?")
如果你只想將整個 Array 轉換為 String,你可以
string = array2.join(" ")
如果 array2 包含我們在上一個示例中宣告的值,那麼 string 的值將是
This is also an array!
我們可以不帶任何引數呼叫 Array.join 方法
string = array2.join
string 的值現在將是
Thisisalsoanarray!
字串
[edit | edit source]如果你還沒有閱讀過關於 字串 和 替代引號 的章節,我建議你立即閱讀。本章將涵蓋一些關於 String 的非常酷的東西,並假設你已經瞭解這兩章中的資訊。
在 Ruby 中,有一些非常酷的內建函式與 String 相關。例如,你可以將它們相乘
"Danger, Will Robinson!" * 5
產生
Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!
String 也可以進行比較
"a" < "b"
產生
true
前面的計算實際上比較了字元的 ASCII 值。但是,你可能會問,給定字元的 ASCII 值是什麼?在 1.9 之前的 Ruby 版本中,你可以使用以下方法查詢字元的 ASCII 值
puts ?A
但是,在 Ruby 1.9 或更高版本中,這不再有效。相反,你可以嘗試使用 String.ord 方法
puts "A".ord
無論哪種方法都會顯示
65
這是A的 ASCII 值。只需將A替換為你想查詢的任何字元。
要執行相反的轉換(從65到A,例如),使用 Integer.chr 方法
puts 65.chr
顯示
A
連線的工作方式與大多數其他語言相同:在兩個 String 之間放置一個 + 字元將產生一個新的 String,其值與其他值相同,一個接一個。
"Hi, this is " + "a concatenated string!"
產生
Hi, this is a concatenated string!
要處理討厭的 String 變數而不使用連線運算子,你可以使用插值。在以下程式碼塊中,string1、string2 和 string3 是相同的
thing1 = "Red fish, "
thing2 = "blue fish."
string1 = thing1 + thing2 + " And so on and so forth."
string2 = "#{thing1 + thing2} And so on and so forth."
string3 = "#{thing1}#{thing2} And so on and so forth."
如果你需要遍歷(即,逐步瀏覽)String 物件中的每個字母,可以使用 String.scan 方法
thing = "Red fish"
thing.scan(/./) {|letter| puts letter}
顯示 thing 中的每個字母(puts 會在每次呼叫後自動新增一個換行符)
R
e
d
f
i
s
h
但是引數中的那個奇怪的 "/./" 是什麼?朋友,那是所謂的 正則表示式。它們是有用的小東西,非常強大,但超出了本次討論的範圍。你現在只需要知道 /./ 是 "regex" 的說法,意思是 "任何一個字元"。如果我們使用的是 /../,那麼 Ruby 將遍歷每組兩個字元,並且會錯過最後一個字元,因為字元數量是奇數!
正則表示式的另一個用途可以在以下操作中找到=~運算子。你可以使用匹配運算子 =~ 檢查 String 是否與正則表示式匹配
puts "Yeah, there's a number in this one." if "C3-P0, human-cyborg relations" =~ /[0-9]/
顯示
Yeah, there's a number in this one.
String.match 方法的工作方式基本相同,除了它還可以接受一個 String 作為引數。如果你從程式碼外部獲取正則表示式,這很有用。以下是它的實際應用
puts "Yep, they mentioned Jabba in this one." if "Jabba the Hutt".match("Jabba")
好了,關於正則表示式的介紹就到這裡。即使你可以在接下來的兩個示例中使用正則表示式,我們也將只使用普通的舊 String。假設你在真理部工作,你需要用另一個詞替換 String 中的一個詞。你可以嘗試以下操作
string1 = "2 + 2 = 4"
string2 = string1.sub("4", "5")
現在 string2 包含 2 + 2 = 5。但是,如果 String 包含很多像你剛剛修正的謊言一樣的謊言呢?String.sub 只替換第一個出現的詞!我想你可以使用 String.match 方法和 while 迴圈來遍歷 String,但有一種更有效的方法可以實現這一點
winston = %q{ Down with Big Brother!
Down with Big Brother!
Down with Big Brother!
Down with Big Brother!
Down with Big Brother!}
winston.gsub("Down with", "Long live")
老大哥會非常高興!String.gsub 是 "全域性替代" 函式。所有出現 "Down with" 的地方現在都被替換成了 "Long live" 所以現在winston只宣稱它對老大哥的愛,而不是對老大哥的厭惡。
在這個快樂的音符上,讓我們繼續討論 Integer 和 Float。如果你想了解更多關於 String 類中的方法的資訊,請檢視本章末尾的快速參考表。
數字(整數和浮點數)
[edit | edit source]如果你瞭解所有標準的數字運算子,你可以跳過本段。對於那些不瞭解的人,這裡有一個速成課程。+將兩個數字加在一起。-將它們相減。/除。*乘。%返回兩個除數的餘數。
好了,整數是沒有小數點的數字。浮點數是有小數點的數字。10 / 3產生3因為將兩個整數相除會產生一個整數。由於整數沒有小數點,你得到的只是3。如果你嘗試10.0 / 3你將得到3.33333...如果你在混合中有一個浮點數,你將得到一個浮點數。明白了嗎?
好了,讓我們進入有趣的部分。Ruby 中的一切都是物件,讓我重申一遍。這意味著幾乎所有東西至少都有一種方法。整數和浮點數也不例外。首先我會展示一些整數方法。
這裡我們有久負盛名的times方法。無論何時你想做某事不止一次,都可以使用它。示例
puts "I will now count to 99..."
100.times {|number| puts number}
5.times {puts "Guess what?"}
puts "I'm done!"
這將打印出數字 0 到 99,打印出猜猜看?五次,然後說我完成了!它基本上是一個簡化的for迴圈。它比一個for迴圈慢幾百分之一秒;如果你要為 NASA 編寫 Ruby 程式碼,請記住這一點。;-)
好了,我們快完成了,還有六種方法要介紹。以下是其中的三種
# First a visit from The Count...
1.upto(10) {|number| puts "#{number} Ruby loops, ah-ah-ah!"}
# Then a quick stop at NASA...
puts "T-minus..."
10.downto(1) {|x| puts x}
puts "Blast-off!"
# Finally we'll settle down with an obscure Schoolhouse Rock video...
5.step(50, 5) {|x| puts x}
好了,這應該是有道理的。如果你不明白,upto從它被呼叫的數字開始,一直計數到它的引數中傳遞的數字。downto做同樣的事情,只是它向下計數而不是向上計數。最後,step從它被呼叫的數字開始,一直計數到它引數中的第一個數字,每次計數增加它引數中的第二個數字。所以5.step(25, 5) {|x| puts x}將輸出從五開始,以二十五結束的每個五的倍數。
最後三種方法
string1 = 451.to_s
string2 = 98.6.to_s
int = 4.5.to_i
float = 5.to_f
to_s將浮點數和整數轉換為字串。to_i將浮點數轉換為整數。to_f將整數轉換為浮點數。這就是 Ruby 中所有資料型別的概況。現在,這裡是我向你承諾的字串方法的快速參考表。
其他字串方法
[edit | edit source]# Outputs 1585761545
"Mary J".hash
# Outputs "concatenate"
"concat" + "enate"
# Outputs "Washington"
"washington".capitalize
# Outputs "uppercase"
"UPPERCASE".downcase
# Outputs "LOWERCASE"
"lowercase".upcase
# Outputs "Henry VII"
"Henry VIII".chop
# Outputs "rorriM"
"Mirror".reverse
# Outputs 810
"All Fears".sum
# Outputs cRaZyWaTeRs
"CrAzYwAtErS".swapcase
# Outputs "Nexu" (next advances the word up one value, as if it were a number.)
"Next".next
# After this, nxt == "Neyn" (to help you understand the trippiness of next)
nxt = "Next"
20.times {nxt = nxt.next}