Ruby是強類型動態語言,即Ruby中一旦某一個對象被定義類型,如果不通過強制轉換操作,那麼它永遠就是該數據類型,並且只有在Ruby解釋器運行時才會檢測對象數據類型,它的一切皆為對象(包括 nil 值對象),可以通過調用內置class屬性來獲取該對象的具體數據類型。對於 Ruby 而言,所有類型都繼 ...
Ruby是強類型動態語言,即Ruby中一旦某一個對象被定義類型,如果不通過強制轉換操作,那麼它永遠就是該數據類型,並且只有在Ruby解釋器運行時才會檢測對象數據類型,它的一切皆為對象(包括 nil 值對象),可以通過調用內置class屬性來獲取該對象的具體數據類型。對於 Ruby 而言,所有類型都繼承自 Object 類(根類為 BasicObject)。
數字(Numeric)
數字是最基本的數據類型之一,Ruby 中包含五種內置數字類型類: Numeric, Integer, Float, Fixnum 和 Bignum, 另外標準庫中還提供了三種數字類型:Complex, BigDecimal, Rational. 除 Numeric 類外其他數字類型類都繼承自 Numeric。
irb(main):005:0> 100.class
=> Integer
irb(main):004:0> Integer.superclass
=> Numeric
31位以內的整數為 Fixnum, 超過31位的數為Bignum, Bignum 沒有位數限制,可以理解為長整形。
Ruby3支持基本的數學運算符(+, -, *, /),及取餘(%), 求指數(**),等。
所有數字對象為不可變對象,因此 Ruby中沒有自增和自減操作符(++, –):
irb(main):006:0> x = 4/2
=> 2
irb(main):007:0> y = 6.0/2
=> 3.0
irb(main):008:0> x**2
=> 4
irb(main):009:0> x**-1
=> (1/2)
irb(main):010:0> x**(1/2.0)
irb(main):011:0> x**(1/3)
=> 1
irb(main):012:0> x
=> 2
在Ruby中,一元運算符+=、 -=、 *=等其它類似的操作,和對應的二元運算x = x + y是完全等價的,都會創建新的對象x。其它語言中,可能一元操作符是原處修改的,對應的二元運算是非原處修改的,所以其它語言中使用一元運算方式效率可能會稍高一些,但Ruby中是等價的,所以說變了,一元運算符在Ruby中的作用僅僅是減少代碼量,而並非有性能優化的空間。
對於浮點數來講,Ruby提供了BigDecimal類來解決精度丟失問題,使用該類可以按實際值運算,但效率上不占優勢:
irb(main):013:0> require 'bigdecimal'
irb(main):014:0> BigDecimal('0.2') - BigDecimal('0.1') === BigDecimal('0.1')
=> true
字元串(String)
普通字元串對象通常以雙引號的形式聲明,可轉義字元,單引號原樣輸出不轉義,字元串還可以包含變數或表達式(內嵌 #{ expr }):
irb(main):017:0> "360 degrees = #{2*Math::PI} radians"
=> "360 degrees = 6.283185307179586 radians"
註意#{ expr }方式需要雙引號引用。
也像Python那樣可以使用類似通配符的方式格式化輸出:
irb(main):022:0> "%s: %f" % ["pi", Math::PI]
=> "pi: 3.141593"
和其他語言不同的是,Ruby3中的字元串是可變對象:
irb(main):028:0> ss = "123"
=> "123"
irb(main):029:0> ss[0]
=> "1"
irb(main):030:0> ss[0] = "2"
=> "2"
irb(main):031:0> ss
=> "223"
也就是說,如果我們定義了一個字元串,可以隨時通過下標對字元串中的字元進行修改,而Python或者Golang中的字元串是不可變對象,所以只能通過重新賦值的方式進行修改。
常用的字元串方法:
# 獲取字元串長度
"Hello".length #=> 5
"Hello World!".length #=> 12
# 判斷字元串是否為空
"Hello".empty? #=> false
"!".empty? #=> false
" ".empty? #=> false
"".empty? #=> true
# 檢索字元數量
"HELLO".count('L') #=> 2
"HELLO WORLD!".count('LO') #=> 1
# 插入字元串
"Hello".insert(3, "hi5") #=> Helhi5lo # "hi5" is inserted into the string right before the second 'l' which is at index 3
# 轉大寫
"Hello".upcase #=> HELLO
# 轉小寫
"Hello".downcase #=> hello
# 交換大小寫
"hELLO wORLD".swapcase #=> Hello World
# 字元串翻轉
"Hello World!".reverse #=> "!dlroW olleH"
# 字元串切割數組
"Hello, how are you?".split #=> ["Hello,", "how", "are", "you?"]
# 字元刪除
name = "Batman"
name.chop
name == "Batma" #=> false
# 清除空格
" Hello ".strip #=> Hello
# 強轉整形
"15".to_i #=> 15 # integer
# 字元串拼接
"15" + "15" #=> "1515" # string
"15" << "15" #=> "1515" # string
"15".concat "15" #=> "1515" # string
# 獲取字元索引
"information".index('o') #=> 3
"information".index('mat') #=> 5
"information".index(/[abc]/) #=> 6
"information".index('o', 5) #=> 9
"information".index('z') #=> nil
可以看到,全部由字元串內置屬性完成,並不需要外部方法的參與。
與此同時,還可以通過對象的的frozen?屬性判斷類型是否可變。
irb(main):035:0> "123".frozen?
=> false
irb(main):036:0> 3.frozen?
=> true
返回true為不可變對象,而false則代表可變。
符號(symbol)
符號(symbol)和字元串很相似,符號也是對象,一般作為名稱標簽來使用,用來表示變數等對象的名稱,另外符號和字元串可以相互轉換。
聲明符號:
#聲明symbol對象
:test1
:'test'
其實就是字元串前面加個冒號: 就是符號。
字元串和符號區別:
#可以通過object_id方法來獲得一個對象的標識符
'test1'.object_id
=>70201737198340
'test1'.object_id
=>70201752605900
'test1'.object_id
=>70201752351880
:test2.object_id
=>8869148
:test2.object_id
=>8869148
:'test2'.object_id
=>8869148
在Ruby3中每一個對象都有唯一對象標識符,也可以理解為記憶體地址標識,每個字元串對象都是不同的,即使它們包含了相同的字元串內容,而對於符號對象,相同的字元串內容則只會指向唯一確定的一個符號對象,這樣實際上節約了記憶體,減少了性能損耗。
符號不可以像其他變數一樣對它進行賦值運算。比如這樣的寫法是錯誤的:myname = "test"。 相反符號可以作為值賦給其他變數比如mystring = :myname。
所有符號對象存放在 Ruby內部的符號表中,可以通過類方法 Symbol.all_symbols 得到當前 Ruby 程式中定義的所有 Symbol 對象,該方法返回一個 Symbol 對象數組。
符號與字元串相互轉換:
var1 = "test".to_sym #=>:test
var1 = :test.to_s #=>"test"
一般情況下,符號作為哈希的key進行取值操作,這樣效率和性能更高:
H = Hash[:"a" => 100, :"b" => 200]
puts H[:a]
程式返回:
100
因為 Ruby3對每一次字元串引用都會生成一個字元串對象,累積下來這個開銷是相當大的。
需要註意的是,符號是不可變對象。
哈希(Hash)
哈希是一種非常有用且廣泛使用的複合容器對象,可用於存儲其他對象。我們通過鍵(key)來查找哈希中的值(value)。好比我們有一個牛津詞典,我們通過查找“hello的單詞來找到中文意思"你好",此時,“hello“就是作為鍵,而“你好”就是值。
聲明哈希:
H = {}
可以單獨對key和value進行賦值操作:
H[:a] = "123"
puts H[:a]
也可以通過使用=>將鍵分配給值來創建哈希,用逗號分隔多個鍵值對,並用花括弧將整個內容括起來:
H = { "one" => "1", "two" => "2", "three" => "3" }
puts H
直接通過key就可以進行取值、修改等操作:
puts H["one"]
當我們查找的鍵沒有對應內容時,會返回一個nil。
也可以使用fetch方法,他和[]方法一樣都可以查找某一個鍵的值,但是如果鍵對應的值不存在,會拋出異常。
哈希可以進行合併操作:
a = { "one" => "eins" }
b = { "two" => "zwei" }
puts a.merge(b)
puts a
通過keys方法列印所有的鍵:
H = {}
H[:a] = "123"
puts H.keys()
也可以通過values返回一個帶有哈希所有值的數組:
H = {}
H[:a] = "123"
H["123"] = "123"
puts H.values()
判斷哈希是否為空:
{}.empty?
# ---- 輸出結果 ----
true
也可以使用size或者length方法,判斷哈希的大小是否為0:
dictionary = { "one" => "eins", "two" => "zwei", "three" => "drei" }
puts dictionary.size == 0
puts dictionary.length == 0
# ---- 輸出結果 ----
false
false
通過delete方法刪除鍵值對:
dictionary = { "one" => "eins", "two" => "zwei", "three" => "drei" }
dictionary.delete("one")
puts dictionary
# ---- 輸出結果 ----
{"two"=>"zwei", "three"=>"drei"}
需要註意的是,哈希是可變對象:
irb(main):041:0> {}.frozen?
=> false
數組(Array)
數組是一個包含許多元素的對象。這些元素可以是變數(例如 字元串,數字,哈希等),甚至可以是其他對象(包括構成多維數組的其他數組)。定義中索引指的是數組元素中的一個序號,它從0開始,每個索引對應一個元素。說白了,就是一個內部元素記憶體地址連續的線性結構。
聲明數組:
A = []
創建字元串數組:
> %w{ cat dog monkey }
=> ["cat", "dog", "monkey"]
創建符號數組:
> %i{ cat dog monkey }
=> [:cat, :dog, :monkey]
判斷數組是否為空:
# 定義一個空數組
> days_of_week = []
=> []
days_of_week.empty?
=> true
也可以使用length或者size:
> days_of_week.length == 0
=> true
> days_of_week.size == 0
=> true
通過索引訪問數組元素:
# 定義一個數組
> days_of_week = [ "Mon", "Tues", "Wed", "Thu", "Fri", "Sat", "Sun" ]
=> [ "Mon", "Tues", "Wed", "Thu", "Fri", "Sat", "Sun" ]
> days_of_week[0]
=> "Mon"
> days_of_week[1]
=> "Tues"
使用數組的first和last方法訪問首個和末尾元素:
> days_of_week.first
=> "Mon"
> days_of_week.last
=> "Sun"
通過index返回元素下標:
# 定義一個數組
> days_of_week = [ "Mon", "Tues", "Wed", "Thu", "Fri", "Sat", "Sun" ]
=> [ "Mon", "Tues", "Wed", "Thu", "Fri", "Sat", "Sun" ]
> days_of_week.index("Wed")
=> 2
提取子元素:
# 定義一個數組
> days_of_week = [ "Mon", "Tues", "Wed", "Thu", "Fri", "Sat", "Sun" ]
=> [ "Mon", "Tues", "Wed", "Thu", "Fri", "Sat", "Sun" ]
> days_of_week[1, 3]
=> ["Tues", "Wed", "Thu"]
也可以針對數組指定範圍:
> days_of_week[1..3]
=> ["Tues", "Wed", "Thu"]
合併數組:
days1 = ["Mon", "Tue", "Wed"]
days2 = ["Thu", "Fri", "Sat", "Sun"]
days = days1 + days2
=> ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
使用<<將元素動態附加到現有數組:
days1 = ["Mon", "Tue", "Wed"]
days1 << "Thu" << "Fri" << "Sat" << "Sun"
=> ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
數組的交集 &:
operating_systems = ["Fedora", "SuSE", "RHEL", "Windows", "MacOS"]
linux_systems = ["RHEL", "SuSE", "PCLinuxOS", "Ubuntu", "Fedora"]
operating_systems & linux_systems
=> ["Fedora", "SuSE", "RHEL"]
數組的差集 -
operating_systems = ["Fedora", "SuSE", "RHEL", "Windows", "MacOS"]
linux_systems = ["RHEL", "SuSE", "PCLinuxOS", "Ubuntu", "Fedora"]
operating_systems - linux_systems
linux_systems - operating_systems
數組的並集 |
operating_systems = ["Fedora", "SuSE", "RHEL", "Windows", "MacOS"]
linux_systems = ["RHEL", "SuSE", "PCLinuxOS", "Ubuntu", "Fedora"]
operating_systems | linux_systems
=> ["Fedora", "SuSE", "RHEL", "Windows", "MacOS", "PCLinuxOS", "Ubuntu"]
數組刪除重覆元素:
linux_systems = ["RHEL", "SuSE", "PCLinuxOS", "Ubuntu", "Fedora", "RHEL", "SuSE"]
linux_systems.uniq
=> ["RHEL", "SuSE", "PCLinuxOS", "Ubuntu", "Fedora"]
向數組中增加或減少元素(push和pop)
colors = ["red", "green", "blue"]
=> ["red", "green", "blue"]
colors.push "indigo"
=> ["red", "green", "blue", "indigo"]
colors.push "violet"
=> ["red", "green", "blue", "indigo", "violet"]
colors.pop
=> "violet"
colors.pop
=> "indigo"
基於先進後出原則。
數組插入元素:
colors = ["red", "green", "blue"]
=> ["red", "green", "blue"]
colors.insert( 1, "orange" )
=> ["red", "orange", "green", "blue"]
基於下標來刪除元素:
colors = ["red", "green", "blue"]
=> ["red", "green", "blue"]
colors.delete_at(1)
=> "green"
colors
=> ["red", "blue"]
基於元素內容來刪除:
colors = ["red", "green", "blue"]
=> ["red", "green", "blue"]
colors.delete("red")
=> "red"
colors
=> ["green", "blue"]
最後是排序:
numbers = [1, 4, 6, 7, 3, 2, 5]
=> [1, 4, 6, 7, 3, 2, 5]
numbers.sort
=> [1, 2, 3, 4, 5, 6, 7]
布爾和Nil
true 和 false 為兩個布爾型的值,與其他語言理解有差別的是,除了 false 和 nil 外,其他值都為 true:
!true # false
!false # true
!nil # true
!0 # false
![] # false
nil 表示空值對象。對於值判空操作可調用 nil? 方法:
false.nil? # false
nil.nil? # true
需要註意的是,Ruby3中的nil是一個對象,表示沒有任何東西的對象,而不是沒有對象。nil與nil的比較無論是==還是eql?都返回true。
結語
字元、數字、布爾是不可變對象,而字元串、數組、哈希是可變對象,Ruby3中所有不可變對象的多個同值對象,都會指向同一個對象的記憶體地址。例如所有的1數值都是同一個對象,所有的nil、布爾值相同的字元對象也都是指向同一個對象,這也導致了Ruby3中不支持++或者--這樣的操作,因為這要求在記憶體地址中指向的原對象進行增減操作,造成對象引用混亂的現象。