Ruby數(shù)據(jù)類型

本文介紹 Ruby 基本的數(shù)據(jù)類型,主要參考《Ruby編程語言》。
Ruby支持的數(shù)據(jù)類型包括基本的Number、String、Ranges、Symbols,以及true、false和nil這幾個特殊值,同時還有兩種重要的數(shù)據(jù)結(jié)構(gòu)——Array和Hash。

數(shù)字

Ruby 中所有的數(shù)字都是 Numberic 類的實例,所有整數(shù)都是 Integer 的實例。Fixnum 和 Bignum之間的轉(zhuǎn)換是透明的。Ruby 利用 Float 來近似的表示實數(shù),該類會利用本地平臺的浮點數(shù)表示形式。各個類之間的關(guān)系如下,圖片來源于《Ruby編程語言》。

圖片
圖片

補充以下幾點,以免忘記。

  • 除法、取模與負數(shù)。

當一個操作數(shù)為負數(shù)時,Ruby的整數(shù)除法和取模操作不同于C/C++和Java。例如,-7/3,可以用浮點表示為-2.33。Ruby采取的是向負無窮大圓整,于是整除結(jié)果為-3,但是C的做法是向0圓整,所得結(jié)果為-2??梢缘弥?,在Ruby中,-a/b和a/-b相等,但是卻不一定等于-(a/b)。

而在Ruby取模操作中,-7/3的結(jié)果為2,而C中結(jié)果卻是為-1。在Ruby中,結(jié)果符號始終和第二個數(shù)操作符號保持一致。在C中結(jié)果符號始終和第一個操作數(shù)保持一致。另外Ruby還定義了remainder方法,在結(jié)果的量和符號方面都和C保持一致。

  • 實數(shù)的表示

和大多數(shù)硬件和計算機語言一樣,Ruby的Float類為了高效的使用硬件,大多數(shù)浮點數(shù)都表示成二進制的,可以精確的表示1/2 和1/4這類分數(shù),但是連0.1都無法用精確的表達。所以下面一個簡單的表達式或許并不是表現(xiàn)的想你想象的那樣。

   0.4 - 0.3 == 0.1

Ruby在標準庫中提供了BigDecimal類來解決這個問題,它采用十進制來表示實數(shù)。但是,針對BigDecimal對象的算術(shù)運算要比針對Float的算術(shù)運算慢上許多倍。

文本

字符串表示

  • 單引號

單引號引入字符串字面量來表示字符串,其中如果需要表示單引號,只需要在之前加入一個反斜線。

'it\'s just a test!'

如果需要表示反斜線,那么只需要在反斜線之前再加入一個反斜線就可以了。

'This is a backslash: \\'

在一個單引號表示的字符串中,如果一個反斜線后邊既不是單引號又不是雙引號,那么該反斜線就表示反斜線。意味著,不需要成對的出現(xiàn)反斜線。

'a\b' == 'a\\b'

單引號字符串可以跨行,得到的字符串會包含換行符,也不能通過 反斜線來轉(zhuǎn)義行尾的換行符。

'This is a long string literal \
that include a backslash and a new line.'

如果不希望加入換行符,那么只需將其劃分成為相鄰的字符串字面量,同時在其結(jié)尾加入反斜線轉(zhuǎn)義末尾的換行符就可以了。

'The three literal are' \ 
'concatenated into one by interpreter' \
'The resulting string contains no newlines.'
  • 雙引號

雙引號引用的字符串字面量則支持更多的轉(zhuǎn)義,比如換行\(zhòng)n,制表\t和雙引號"。
同時還支持Ruby特有的“字符串內(nèi)插”。

"The resulte is #{8 + 9}"

表達式位于花括號中,并且前邊有一個#字符。當插入的表達式只是一個全局變量、實例或類變量的引用時,花括號可以省略。

當不希望#字符被特殊處理時,只需在之前加入一個反斜線即可。

Ruby 也支持printf和sprintf這兩個方法。

printf("Pi is about %.4f", Math::PI)

但是,Ruby中還有一個與sprintf等價的操作符,只需要簡單的在一個格式字符串和帶插入的其中的參數(shù)之間放置一個%即可。

"Pi is about %.4f" % Math::PI
  • Unicode 轉(zhuǎn)義序列

在Ruby1.9以后,在雙引號引用的字符串中通過\u可以轉(zhuǎn)義任意的Unicode字符,最簡單的形式是在\u后邊添加一個十六進制數(shù)字(不區(qū)分大小寫),它們代表了0000到FFFF之間的Unicode碼點。

"\u00D7"   # => "x"

第二種形式是在它后邊加一對花括號,花括號之間的數(shù)字可以表示0到10FFFF之間的任何Unicode碼點。

"\u{A5}" # => same as "\u00A5"

最后一種形式允許多個碼點同時轉(zhuǎn)義,只需要在花括號內(nèi)放入多組1到6個十六進制數(shù)字組成的序列即可,各組之間用空格或tab符號分割,在開始花括號之后和結(jié)束花括號之前均不允許有空格。

"\u{20AC A3 A5}"
  • 字符串字面量分界符

Ruby除了支持單引號和雙引號以外,還支持一種更一般化的語法來引用字符串字面量。以%q開頭的遵循單引號引用字符串規(guī)則,以%Q(或%)開頭的遵循雙引號引用字符串規(guī)則。其后緊接的第一個字符為分界符,知道下一個分界符(未被轉(zhuǎn)義)的內(nèi)容組成了改字符串。

%q(Don't worry about ' character!)
%Q|"How are you?", he said|
%-This string ends with a newline\n-
  • Here document

Here document 以 << 或 <<- 開頭,后邊緊跟(為了避免與左移操作符混淆,不允許有空格)一個用于指定結(jié)尾分界符的標示符或字符串,從下一行開始一直待該分界符單獨出現(xiàn)到一行為止。

document  = <<HERE
This is a string literal.
It has two lines and abruptly ends. . .
HERE

Ruby 解釋器解釋到 << 和分界符之后并不會停止,事實上,在讀取一個 here document 的內(nèi)容之后, Ruby 解釋器會回到該 here document 的開頭部分所在行,并繼續(xù)解析后邊的內(nèi)容。

greeting = <<HERE + <<HTERE + 'world'
Hello
HERE
there
HTERE

結(jié)尾分界符必須單獨出現(xiàn)在一行上,該分界符后邊甚至連注釋不能接注釋。如果以 << 開頭,那么結(jié)尾分界符必須出現(xiàn)在一行的開頭。如果以 <<- 開頭,那么在結(jié)尾分界符之前可以出現(xiàn)空白。除了空的 here document,每個 here document 都以一個換行符結(jié)尾。

如果采用一個沒有被引號括起來的標示符作為分界符,那么 here document 就會表現(xiàn)的像被雙引號應(yīng)用的字符串一樣,其中反斜線用于轉(zhuǎn)義,而 # 用于內(nèi)插字符串。

如采用一個雙引號引用的字符串字面量來作為 here document 的分界符。除了分界符里面可以出現(xiàn)空格,這與使用單個分界符的情形是一樣的。

如果采用單引號引用的字符串字面量作為分界符,它會表現(xiàn)的 比單引號引用的字符串還要嚴格一些。因為單引號本身不是分界符,所以反斜線不在作為轉(zhuǎn)義字符,因此,反斜線也是字符串字面量的一部分。

document = <<'THIS IS THE END, MY ONLY FRIEND, THE END'
lots an lots of test goes here
THIS IS THE END, MY ONLY FRIEND, THE END
  • 反引號所引用命令執(zhí)行

當使用反引號來引用文本時,該文本作為一個由雙引號引用的字符串字面量來處理。改文本的值將被傳遞給一個名為 Kernel.` 的方法,該方法將該文本的值作為一個操作系統(tǒng)的 shell 命令來執(zhí)行。

`ls`

另外,Ruby 也支持一種泛型化的引用語法,可以用來替代反引號,類似于 %Q 的語法,這里使用 %x 來替代反引號。

%x[ls]
  • 字符串字面量和可變性

Ruby 的字符串是可變的。因此,Ruby 無法用同一個對象來表達兩個相同的字符串字面量。每當 Ruby 遇見一個字符串字面量的時候,它都會創(chuàng)建一個對象。

10.times { puts "test".object_id }

因此,為了獲得更好的運行效率,應(yīng)該避免在循環(huán)中使用字符串字面量。

字符字面量

在 Ruby 中, 你可以用一個字符前面加問號的方式來表示單個字符構(gòu)成的字面量。

?A

因為 Ruby1.9 對字符串字面量的解釋方式不同于 Ruby1.8 , 字符就是長度為一的字符串,所以上邊例子完全等價于 'A',也就是說,我們完全沒有必要再使用這種語法了。

字符串的操作

Ruby 通過 + 操作符連接兩個字符串,不同 JavaScript,其并不會將右側(cè)操作數(shù)自動轉(zhuǎn)換成為字符串。
一般情況下,采用字符串內(nèi)插比采用+操作符簡單一些,并會自動調(diào)用其 to_s。
<< 操作符會將第二個參數(shù)添加到第一個參數(shù)后面,不同于+操作符,他會修改左側(cè)的操作數(shù),而不是返回一個對象。<<操作符同樣不會對右側(cè)的操作數(shù)做任何類型的轉(zhuǎn)換,但是,如果右側(cè)的操作數(shù)是一個整數(shù),那么它將會被當做一個字符編碼來處理。

* 操作符期待右側(cè)操作數(shù)是一個整數(shù)。如果左側(cè)的操作數(shù)是一個字符串字面量,那么任何內(nèi)插操作都只會在重復操作被執(zhí)行之前被執(zhí)行一次。如下:

a = 0
"#{a=a+1} " * 3    # Returns "1 1 1 ", not "1 2 3 "

Ruby 字符串同時定義了 == 、 != 、< 、<= 、> 和 >= 來比較字符串。當且僅當兩個字符串擁有相同的長度,而且所有的字符都相等的時候,它們才是相等的。字符串之間的比較會嚴格的基于字符編碼,不會對將要比較的字符串進行任何格式化。字符串是大小寫敏感的。在 ASCII 里,大寫字符的編碼值均小于小寫字符,比如 "Z" < "a"。對于 ASCII 字符進行大小不敏感的比較可以使用 casecmp,也可以是在比較前先通過 downcase 或 upcase 方法將字符串轉(zhuǎn)換成為相同的形式。記住,Ruby 關(guān)于大寫或小寫字符的理解僅限于 ASCII 字符集。

訪問字符和子字符串

在 Ruby1.9 中,亦可以這樣索引單個字符。例如:

s = "hello";
s[0]    # 'h'
s[s.length -1]    # 'o'
s[-1]    # '0'
s[-2]    # 'l'
s[s.length] # nil

而如果需要改變字符串里面的單個字符串,字需要簡單地講方括號置于表達式的左側(cè)即可。同時也支持是一個任意長度的字符串。

s[0] = 'H'

如果希望獲得一個子字符串,你只需要在方括號里使用由兩個逗號分割的操作數(shù)即可。第一個操作數(shù)指定索引值(可能為負數(shù)),第二個操作數(shù)指定長度值(必須是非負值)。

s = "hello"
s[0, 2]    # "he"
s[-1, 1]    # "o"
s[0, 0]     # ""
s[0, 10]    # "hello"  返回所有可用的字符串
s[s.length, 1]    # "" 在剛好超過字符串長度的地方是一個空字符串(方便賦值時在字符串末尾添加字符串)
s[s.length+1, 1]    # nil
s[0, -1]    # nil

同樣,你也可以將任意的字符串復制給個上述方式索引的子字符串。

另一種提取、插入、刪除和替換子字符串的方法是通過一個 Range 對象索引一個字符串。

s = "hello"
s[2..3]    # "ll"
s[-3..-1]    # "llo"
s[0..0]    # "h"
s[0...0]    # ""
s[2..1]    # ""
s[7..10]    #nil

你還可以通過一個字符串來索引另一個字符串,那么第一個被匹配的字符串將被返回,否則返回nil。事實上這種形式的字符串索引只有出現(xiàn)在一個賦值語句的左側(cè)時采用用武之地。

s = "hello"
while (s["l"])
    s["l"] = "L";
end

同樣,你還可以使用一個正則表達式來索引一個字符串。

s[/[aeiou]/] = "*"

對字符串進行迭代

在 Ruby1.9 里定義了三個迭代字符串的方法,each_byte 按照字節(jié)對一個字符串進行迭代,each_char 按照字符進行迭代,each_line 按照行進行迭代。另外,each_char 比使用[]操作符和字符索引更加高效。

字符編碼

Ruby 在1.9版之前堪稱是對字符編碼支持最差的語言之一,而現(xiàn)在變成了支持最好的語言之一。情況比較復雜,參見我的另一篇筆記《Ruby 與字符編碼》。

數(shù)組

Ruby 的數(shù)組可以用負值進行索引,如果賦值操作時使用負值且超出范圍,那么報錯。

另外Ruby 支持一種更加通用的數(shù)組字面量特殊語法。

%w[this is a test]
%W| ( [ { < |

同時也可以通過 Array.new 構(gòu)造函數(shù)來構(gòu)造數(shù)組。

empty = Array.new   # 返回空數(shù)組 []
nils = Array.new(3)    # [nil, nil, nil]
zeros = Array.new(4, 0)    # [0, 0, 0, 0]
count = Array.new(3) { |i| i + 1}    # [1, 2, 3]

Ruby 還定義了其他有用的操作符。

[1, 2] + [3, 4, 5]    # [1, 2, 3, 4, 5]
["a", "b", "c", "b", "a"] - ["b", "c", "d"]     # ["a", "a"]
[0] * 4     # [0, 0, 0, 0]

同時,還定義了 | 和 & 操作符。不過,操作符不具有傳遞性,a | b 不等于 b | a 。同時,Ruby 不保證返回數(shù)組中元素的順序。

a = [1, 1, 2, 2, 3]
b= [4, 4, 3, 3, 2]
a | b    #  [1, 2, 3, 4]
b | a    #  [4, 3, 2, 1]
a & b    #  [2, 3]
b & a    # [3, 2]

同時數(shù)組還定義了一系列有用的 API,例如 each 等等。

哈希

Ruby 里的 hash 采用一種哈希表的數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)的。那些作為哈希鍵的對象必須有一個 hash 的方法,改方法返回一個 Fixnum 的哈希碼。如果兩個鍵相同,那么它們必須具有相同的哈希碼。不相等的鍵也可以擁有相同的哈希碼,但是僅當哈希表有極少的重復時,其效率才是最高的。

Hash 類采用 eql? 方法比較鍵之間的相等性,對于大多數(shù) Ruby 類而言,eql? 方法與 == 操作符一樣。如果你新定義的類重寫了 eql? 方法,那么你必須同時重寫 hash 方法,否則你的類無法作為一個哈希鍵。

如果使用一個可變對象作為哈希鍵會帶來一些問題,改變一個對象的內(nèi)容通常會改變其哈希碼,那么內(nèi)部的哈希表將會被破壞,而且該哈希的行為也將不正確。由于字符串是可變的,但是有常常作為哈希鍵,所以 Ruby 將它們作為特例進行了處理。對那些作為鍵的字符串,Ruby 會生成它們的私有拷貝。但是這是唯一的特例,如果使用任何可變對象作為哈希鍵時,可以考慮為這些可變對象生成私有拷貝,或者調(diào)用 freeze 方法。如果你必須使用可變的哈希鍵,那么請在每次更改鍵后,調(diào)用 Hash 類的 rehash 方法。

范圍

一個 Range 對象表示位于開始值和結(jié)束值之間的一些值。begin..end 范圍對象中值滿足:

begin <= x <= end

begin...end 范圍對象中的值滿足:

begin <= x < end

所有范圍的端點值都必須實現(xiàn) <=> 操作符。如果端點沒有實現(xiàn) succ 方法,那么我們稱之為連續(xù)的成員關(guān)系測試。否則,即離散的成員關(guān)系測試。該集合包含 begin、begin.succ、begin.succ.succ 等等。范圍的成員關(guān)系是一種集合的關(guān)系。值得注意的是,對離散的成員關(guān)系測試開銷要遠大于對連續(xù)的成員關(guān)系測試開銷。

在Ruby1.9中,cover? 方法使用連續(xù)的成員關(guān)系測試。include? 和 member? 是同義詞,只有在范圍端點是數(shù)字時使用連續(xù)的成員關(guān)系測試,否則它們就會使用離散性的成員測試。

符號

一個 Ruby 解釋器的典型實現(xiàn)會維護一個符號表,在這個表中它存儲了所知曉的所有類、方法及變量名稱,是這樣一個解釋器可以避免大多數(shù)字符串比較:比如,它可以通過一個方法名在符號表中的位置來對其進行引用。這樣一來,就將一個開銷較大的字符串操作轉(zhuǎn)換成了一個開銷較小的整數(shù)操作。

Symbol 也有一個 %s 字面量語法。你可以使用 intern 和 to_sym 方法將一個 String 對象轉(zhuǎn)換成一個 Symbol,而且你也可以使用 to_s 的方法或其別名 id2name 將一個 Symbol 轉(zhuǎn)回一個字符串。

當你使用字符串的目的是在于將其作為獨一無二的標識符,而不在于它文本的內(nèi)容時,請改用符號。比較兩個 Symbol 對象的相等性要遠遠快于比較兩個字符串的相等性。

在 Ruby1.9 中,Symbol 類定義了一些 String 方法,比如 length、size、比較操作符,甚至 [] 和 =~ 操作符,而且符號也可以被作為一種不可變的(也是不可被垃圾回收的)字符串來使用。

True、False 和 Nil

True、False 和 Nil 分別是 TrueClass、FalseClass 和 NilClass 的單鍵實例。Ruby 中并沒有 Boolean 類。

當 Ruby 需要一個布爾值時,nil 表現(xiàn)為 false,而 nil 和 false 之外的所有值都表現(xiàn)為 true。

判斷一個值是否為 nil :

o == nil
o.nil?

對象

對象引用

Ruby 中使用對象時,都是在使用對象的引用,所有的對象都通過引用來操作。但是,在實現(xiàn)時,F(xiàn)ixnum 和 Symbol 對象實際上是“立即值”,而非引用。然而,F(xiàn)ixnum 和 Symbol 都沒有可變的方法,也就是不可變的,因此又可以把它當做是按引用來操作的(唯一區(qū)別在于,不能為立即值定義單鍵方法)。

t = s = "Ruby"
s[-1] = ""
print t     # "Rub"

Ruby 中方法實參是通過值而不是引用來傳遞的,因此,當把一個對象傳遞給一個方法時,只不過被傳遞的值正好是對象的引用罷了。

對象標識

在 Ruby1.9 中,可以通過 id 和 object_id 來獲得對象獨一無二的標識符。

對象的類和類型

通過 class 方法來確定一個對象所屬的類。也通過 instance_of 方法來檢查對象是否為某個類的實例。

o.class == string
o.instance_of? String

通過 superclass 可以確定某個類型的超類。通過 is_a? 可以確定某個對象是否為某個類和其子類的實例。

o.class.superclass
String.superclass
o.is_a? Comparable    #  也可以判斷 mixin 模塊
o.is_a? Object

對象的相等性

Ruby 擁有很多的方法用來比較對象的相等性。

equal? 方法

equal? 方法由 Object 定義,用于確定兩個值是否引用了同一個對象。

a = "Ruby"
b = c = "Ruby"
a.equal? b    # false
b.equal? c    #true

一般而言,子類永遠不要重寫 equal? 方法。

另一種檢查兩個值時候引用同一個對象的方法是比較他們的 object_id 。

== 操作符

在 Object 類里,他是 equal? 方法的同義詞。但大多數(shù)的類都重定義了這個操作符,使得不同的實例之間也能進行相等性測試。

Array 和 Hash 都定義了 == 操作符。如果兩個數(shù)組擁有相同的數(shù)量的元素,并且對應(yīng)位置上的元素通過 == 比較也相等,那么兩個數(shù)組通過 == 比較是相等的。如果兩個哈希擁有相同數(shù)量的鍵值對,額況且對應(yīng)的鍵和值也相等那么這兩個值通過 == 比較之后是相等。

Number 類在它們的 == 操作符里將進行簡單的數(shù)值轉(zhuǎn)換,比如 Fixnum 1 和 Float 1.0 通過 == 比較之后是相等。

諸如 String 、 Array 和 Hash 類的 == 操作符,通常要求兩個操作符屬于同一個類。但如果右側(cè)的操作數(shù)定義了一個 to_str、 to_ary 和 to_hash 的方法,那么原先的 == 操作符會調(diào)用右側(cè)的操作數(shù)所定義的 ==。

class MyArray

  def initialize(array)
    @array = array
  end

  def to_ary
    @array
  end

  def ==(array)
    to_ary == array
  end
end

array = [1, 2, 3]
my_array = MyArray.new([1, 2, 3])

array == my_array    # true

!= 操作符會簡單的使用 == 操作符并且反轉(zhuǎn)其結(jié)果。但在 1.9 中,類也可以顯示的定義自己的 != 操作符。

eql?

Object 將 eql? 方法定義為 equal? 方法的同義詞,那些重寫了 eql? 方法的類通常將其作為更加嚴格的 == 操作符,即不允許類型轉(zhuǎn)換。

1 == 1.0    # true
1.eql? 1.0    # false

Hash 類采用 eql? 檢查兩個哈希鍵是否相等,如果兩個對象通過 eql? 結(jié)果比較是真,那么它們的 hash 方法必須返回相同的值。

=== 操作符

=== 操作符被稱為“條件相等性”操作符,用于測試一個 case 語句的目標值是否和某個 when 從句相匹配。

Object 類定義了一個默認的 === 操作符,它會調(diào)用 == 操作符。某些關(guān)鍵的類重寫了 === 操作符:Range 用來測試一個值是否在范圍內(nèi); Regexp 類用于測試一個字符串是否與某個正則表達式相匹配;Class 類用于測試一個對象是否該類的一個實例;Symbol 類在當其右側(cè)操作數(shù)和左側(cè)操作數(shù)是同一個符號對象時,或者當右側(cè)操作數(shù)是一個持有和左側(cè)符號對象相同文本的字符串時,改操作符返回 true。

=~ 操作符

String、Regexp 和 Symbol 定義了 =~ 操作符用于進行模式匹配。Object 也定義了這個操作符,它直接返回 false。!~ 被定義為 =~ 的反義,在 Ruby1.9 以后,你也可顯式自定義。

對象的順序

類通過實現(xiàn) <=> 來定義其順序性。當左側(cè)操作數(shù)小于右側(cè)操作數(shù)時,返回-1,反之返回1,相等返回0。一般情況下,定義了 <=> 操作符的類會將 Comparable 模塊作為一個 mixin 包含進來,以同時獲得 <、<=、==、>= 和 > 操作符。還有一個比較方法 betwee?

如果 <=> 返回 nil 。那么所有基于它的操作符都返回 false。例如 NaN這個特殊的 Float。

nan = 0.0/0.0
nan < 0 # false
nan > 0 # false
nan == 0 # false
nan == nan #false
nan.equal?(nan) # true

對象轉(zhuǎn)換

顯式轉(zhuǎn)換

一般情況下,Ruby 中的方法不會對自動對類型進行轉(zhuǎn)換,如果需要,你應(yīng)該手動調(diào)用轉(zhuǎn)換方法。最為常見的就是to_s、to_i、to_f 及 to_a 方法,他們分別將對象轉(zhuǎn)換成為 String、Integer、Float 和 Array。

關(guān)于 # 的字符串內(nèi)插,Ruby 會主動調(diào)用其 to_s 的方法。to_s 的另一個重要的選擇性替代是 inspect 方法。通常而言,to_s 用于返回人類可讀的對象表現(xiàn)形式,而 inspect 方法用于調(diào)試,它能返回一個給開發(fā)人員帶來幫助的表現(xiàn)形式。而 Object 定義的 inspect 方法只是簡單的調(diào)用 to_s。

隱式轉(zhuǎn)換

Ruby 中還定義了 to_str、to_int、to_ary 和 to_hash,用于方法進行隱式的轉(zhuǎn)換。不過在內(nèi)建類里,這些轉(zhuǎn)換方法并沒有被普遍的實現(xiàn)。

在 Ruby1.9 里,內(nèi)建的 String、Array、Hash、Regexp 及 IO 類都定義了一個名為 try_convert 的類方法。如果這個方法的實參定義了以上一個合適的隱式轉(zhuǎn)換方法,那么就會對其方法進行調(diào)用,否則返回 nil。例如,對象 o 定義了 to_ary 的方法,那么 Array.try_conver(o) 將返回 o.to_ary,否則返回 nil .

轉(zhuǎn)換函數(shù)

Kernel 模塊還定義了4個全局轉(zhuǎn)換函數(shù)——Array、Float、Integer 及 String。

Array 函數(shù)視圖調(diào)用其實參的 to_ary 方法來將其轉(zhuǎn)換成為一個數(shù)組。如果沒有定義 to_ary 方法,或者該方法返回 nil。那么就試著調(diào)用其 to_a。如果沒有定義 to_a 方法,或者改方法返回nil。這簡單的返回一個數(shù)組,并把該實參作為該數(shù)組的第一個元素。

Float 對象會把 Number 的實參直接轉(zhuǎn)換成一個 Float 對象。任何非 Numeric 的值,都會調(diào)用其 to_f 的方法。

Interger 函數(shù)將其實參轉(zhuǎn)換成為一個 Fixnum 或 Bignum。如果其實參是一個 Numeric 它直接進行轉(zhuǎn)換,浮點值被截斷。如果是一個字符串,它會尋找一個基數(shù)指示符(以0開頭八進制,以0x開頭十六進制,以0b開頭二進制),并據(jù)此對字符串進行轉(zhuǎn)換。與String.to_i 不同,Integer 函數(shù)不允許末端出現(xiàn)非數(shù)值字符。對于任何其他類型轉(zhuǎn)換,Integer 會首先嘗試 to_int ,然后調(diào)用 to_i 。

String 只是簡單的調(diào)用其參數(shù)的 to_s 的方法。

算數(shù)操作符的強制轉(zhuǎn)換

數(shù)值類型定義了一個 coerce 的方法。意圖將其實參類型轉(zhuǎn)換成為其調(diào)用者類型,或者這兩個對象的更一般兼容形式。coerce 返回一個數(shù)組,第一個元素由 coerce 方法轉(zhuǎn)換而來,第二個元素來自其調(diào)用者(如果需要,就進行轉(zhuǎn)換)。

1.1.coerce(1)    # [1.0, 1.1]
requier "rational"
r = Rational(1, 3)
r.coerce(2)    # [2/1, 1/3]

算數(shù)操作符會使用 coerce 方法。例如,F(xiàn)ixnum 的 + 操作符不知道如果操作 Rational 數(shù)字,于是右側(cè)的操作數(shù)會調(diào)用 coerce 方法,并將左側(cè)的操作數(shù)作為實參傳進去,然后在返回數(shù)組的兩個值上簡單的調(diào)用 + 即可。

對象拷貝

clone 和 dup 方法都會返回一個調(diào)用它們對象的淺拷貝,即如果拷貝對象有指向其他對象的引用,那么只有這些引用被拷貝,而引用的對象本身不被拷貝。

如果拷貝對象定義了 initialize_copy (拷貝構(gòu)造函數(shù))的方法,那么 clone 和 dup 就會簡單的分配一個新的實例空間(該實例所屬類與被拷貝對象相同),然后在其上調(diào)用 initialize_copy 的方法。

類也可以直接重寫 clone 和 dup 的方法。

clone 和 dup 的區(qū)別有兩點,其一,clone 會拷貝一個對象被凍結(jié)和受污染的狀態(tài),而 dup 僅僅拷貝受污染的狀態(tài); 其二,clone 方法拷貝一個對象所有的單鍵方法,而 dup 不會。

編組對象

Marshal.dump 用來保存一個對象的狀態(tài),它返回一個二進制的字符串。你可以將一個 I/O 流對象作為第二個實參傳遞給 Marshal.dump 的方法,它會把保存的對象的狀態(tài)寫入這個流中。

為了恢復一個編組后的對象,你可以將一個包含了該對象的字符串或 I/O 流傳遞給 Marshal.load。

值得注意的是,這兩個方法存在版本依賴。

我們可以利用這兩個方法來創(chuàng)建對象的深度拷貝。

def deepcopy(o)
  Marshal.load(Marshal.dump(o))
end

值得一體的是,YAML 是 Marshal 模塊的一種被廣泛采用的替代方案。

凍結(jié)對象

freeze 方法可以將一個對象凍結(jié)起來,一個被凍結(jié)的對象將變得不可改變——它所有的內(nèi)部狀態(tài)都不能被改變,而且對其可改變的方法的調(diào)用也會失敗。

s = "ice"
s.freeze
s.frozen?    # true
s.upcase!    # RuntimeError: can't modify frozen String

污染對象

taint 方法將任何對象標記為受污染的。一旦一個對象受污染,那么任何源自它的對象都變成受污染的??梢酝ㄟ^ tainted? 方法測試一個對象是否受污染。

s = "untrusted"
s.taint
s.tainted?    # true
s.upcase.tainted?    # true
s[3, 4]    # true 

用戶輸入(命令行參數(shù)、環(huán)境變量以及 get 方法讀取的字符串)都會自動成為受污染的。

一個受污染的對象可以通過 untaint 方法變成為受污染的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容