Ruby數組(1):基本用法

數組

Ruby中的數組是一個容器,數組中的每一個元素都是一個對象的引用html

注意,Array類中包含了Enumerable模塊,因此Enumerable中的方法也都能使用,例如Enumerable中的reduce()方法也是很是好用的方法。python

建立數組

字面常量建立

# 1.使用[xxx]方式建立
arr1 = ["Perl", "Python", "Ruby"]

# 2.空數組
arr = []

# 3.使用%w或%W能夠省略引號和逗號,而使用空格分隔
# %w(小寫)不解釋內插表達式,%W(大寫)解釋內插
# 此時中括號能夠換成其它符號,如%{}、%<>、%##
# 若是元素中要保留空格,使用反斜線轉義
arr2 = %w[Perl Python Ruby]   # 3個元素
arr3 = %w<Perl Python\ Ruby>  # 2個元素

a = "Perl"
arr4 = %w(#{a} Python Ruby)   # #{a}不作變量替換,第一個元素爲`\#{a}`
arr5 = %W(#{a} Python Ruby)   # #{a}會作變量替換

# 4.使用%i建立符號symbol數組
arr6 = %i(Perl Python Ruby)  # [:Perl, :Python, :Ruby]

建立arr時,容許解析表達式。例如,範圍表達式、變量等。數組

[-10...0, 0..10]

a=10;b=20
c=[a, b]

Array.new()建立

Array類的new()方法能夠建立數組。ruby

ary = Array.new    #=> []
Array.new(3)       #=> [nil, nil, nil]
Array.new(3, true) #=> [true, true, true]

當new()指定了第二個參數時,各初始化的元素將指向同一個對象app

arr=Array.new(3, "abc")
arr[1][0]="A"   #=> ["Abc", "Abc", "Abc"]

因此,建議初始化的元素採用不可變對象,如數值、symbol、bool的true和false。或者使用下面這種塊結構的初始化方式。函數

能夠將塊結構接管Array.new的第二個默認值參數,它將每一個元素的索引值傳遞到塊變量中。例如:測試

Array.new(4) {Hash.new}    #=> [{}, {}, {}, {}]
Array.new(4) {|i| i.to_s } #=> ["0", "1", "2", "3"]

例如,初始化互不影響的3個"abc"元素數組。fetch

arr = Array.new(3) {"abc"}
arr[0][1]="A"
p arr          # ["aAc", "abc", "abc"]

多維數組:code

Array.new(3){Array.new(2)} #=> [[nil, nil], [nil, nil], [nil, nil]]

Array()建立

經過Kernel模塊提供的Array(arg)方法建立數組。htm

該方法將給定參數arg轉換成數組。

Array({:a => "a", :b => "b"}) #=> [[:a, "a"], [:b, "b"]]
Array(1..5)  #=> [1, 2, 3, 4, 5]

to_a()建立

若是某個類定義了to_a()方法,能夠將對象轉換成數組結構。例如,hash類有to_a()方法,因此能夠將hash結構轉換成數組。

my_hash = {Perl: "Larry Wall", Ruby: "Matz"}
arr = my_hash.to_a()  # [[:Perl, "Larry Wall"],[:Ruby, "Matz"]]

split()從字符串建立

字符串對象有split()方法,能夠按照指定的分隔符將字符串切割成數組。

p "perl python ruby".split  # ["perl", "python", "ruby"]

訪問數組元素

索引/slice取元素

獲取數組的方式有多種。列出瞭如下幾種常見的(返回多個元素的方式將以新數組的方式返回):

  • arr[1]或arr.at(n)或arr.slice(n):獲取index=1的元素
  • arr[1..3]或arr.slice(1..3):獲取index=一、二、3的3個元素
  • arr[1...3]或arr.slice(1...3):獲取index=一、2的兩個元素
  • arr[2, 3]或arr.slice(2, 3):獲取從index=2開始的3個元素,即index=二、三、4的元素
  • arr[-1]:獲取最後一個元素
  • arr[-3]:獲取倒數第三個元素

其中,at()方法和slice()方法不多用,由於arr[]的方式已經足夠。

若是索引越界,則返回nil。可是有一個特殊狀況,當使用slice的操做時,由於要返回的是數組,因此有以下"異常"狀況:

a = %w(a b c d e)
a[4]       #=>"e"
a[4,0]     #=>[]
a[4,1]     #=>["e"]
a[4..100]  #=>["e"]

a[5]       #=>nil
a[5,0]     #=>[]
a[5,1]     #=>[]
a[5..100]  #=>[]

a[6]       #=>nil
a[6,0]     #=>nil
a[6,1]     #=>nil

上面a[5]返回nil,但a[5,x]返回空數組,而a[6]以及a[6,x]則直接返回nil。首先a[6]以及a[6,x]都是索引越界,因此返回nil。而a[5]的5是索引越界,因此返回nil。問題是a[5,x]返回的是空數組,而不是nil。這是由於a[5,x]是一個slice操做,它要求返回新數組。從下面的slice操做能夠理解爲何a[5,1]返回空數組。

p a[0, 5]   # ["a", "b", "c", "d", "e"]
p a[1, 4]   # ["b", "c", "d", "e"]
p a[2, 3]   # ["c", "d", "e"]
p a[3, 2]   # ["d", "e"]
p a[4, 1]   # ["e"]
p a[5, 0]   # []
p a[5, 1]   # []

也就是說,對於slice操做,arr[len, x]的arr[len]能夠認爲是最邊緣的元素,儘管arr[len]已經越界了。

其實slice()是按規則刪除元素,只不過它不影響原始數組。而slice!()則是刪除一些元素並直接影響原始數組,也就是在原處修改數組。

a = [1, 2, 3, 4]
a.slice!(1,2)  # 返回:[2, 3],a變成[1, 4]

Array.values_at()取分散元素

Array類提供了values_at()方法,能夠取得每一個數組對象中指定索引處的多個分散元素。

例如:

a = %w(a b c d e)
p a.values_at(1)          # ["b"]
p a.values_at(0, 0, 2, 4) # ["a", "a", "c", "e"]
p a.values_at(1, 2, 10)   # ["b", "c", nil]
p a.values_at(1..3)       # ["b", "c", "d"]

除了這種方式能夠取得分散元素,還可使用map()函數進行操做。例如,等價於values_at(0,0,2,4,10)的寫法爲:

p [0, 0, 2, 4, 10].map{|i| a[i]}

其它一些方法

好比,默認狀況下索引越界時將返回nil,使用fetch()方法能夠獲取指定索引的元素,且能夠指定越界時的默認值,不然直接報錯。

a=%w(a b c d e)
p a.fetch(10)  # 報錯:索引越界IndexError
p a.fetch(10, "ten") # 指定默認值

使用first()和last()能夠取得數組中的第一個元素和最後一個元素,它們會取得元素後當即退出。

a = %w(a b c d e)
p a.first     # "a"
p a.last      # "e"

使用take()能夠取得數組中的前n個元素,使用drop()能夠取得除了數組前n個元素外剩下的元素。它們都不會刪除元素。

a = %w(a b c d e f)
p a.take(2)  # ["a", "b"]
p a.drop(2)  # ["c", "d", "e", "f"]
p a          # ["a", "b", "c", "d", "e", "f"]

爲數組元素賦值

[] at slice方法均可覺得數組中指定位置處元素賦值。惟一須要注意的是範圍賦值操做,將其認爲是將範圍內的值設置爲新值。

a = %w(a b c d e)

# 單元素賦值
a[1] = "bb"  # 爲第2個元素賦值

# 範圍賦值
a[1, 2] = ["bb", "cc"]  # a=%w(a bb cc d e)
a[1, 2] = %w(bb)  # a=%w(a bb d e)
a[1, 2] = %w(bb cc dd)  # a=%w(a bb cc dd d e)

# 徹底插入元素:指定len爲0
a[1, 0] = %w(B C) # a=%w(a B C b c d e)
a[1..1] = %w[B C] # 與上式等價

# 徹底刪除元素:將右邊對象設置爲空數組
a[1, 2] = []  # a=%w(a d e)

賦值語句的返回值是所賦值內容。例如a[1,0] = %w[B C]的返回值是%w[B C],於此同時原始數組被修改。

擴展數組對象

數組能夠執行+ - * & |操做,不過- & |是將數組做爲集合進行操做的,相關內容見後文對應小節。而使用+*能夠擴展數組。

由於ruby中的一元運算符操做x += y和二元運算符操做x = x + y徹底等價,都會建立新對象x。因此,涉及到大數組時,可能效率會比較低下。

+能夠將兩數組加在一塊兒返回一個新數組:

arr = [1, 2, 3]
arr1 = arr + [3, 4, 5]  # arr不變,arr1=[1,2,3,3,4,5]

concat()方法也能將0到多個數組擴展到一個數組上。注意,concat()會將參數數組全都"壓平"而後追加到原數組尾部:

["a", "b"].concat(["c", "d"])   # ["a", "b", "c", "d"]
["a"].concat(["b"], ["c", "d"]) # ["a", "b", "c", "d"]
["a"].concat     # ["a"]

a = [1, 2, 3]
a.concat([4, 5])  # a=[1, 2, 3, 4, 5]

a = [1, 2]
a.concat(a, a)   # a=[1, 2, 1, 2, 1, 2]

*對於數組有兩種用法:

# 1.數組乘一個數值對象,返回新數組
# 數值必須在數組後面,不能放前面
arr = [1, 2, 3]
arr1 = arr * 2  # arr不變,arr1=[1,2,3,1,2,3]

# 2.數組乘一個字符串,將返回字符串而非新數組
arr = [1, 2, 3]
arr1 = arr * ","   # arr不變,arr1="1,2,3"
arr2 = arr * ",+"  # arr不變,arr2="1,+2,+3"

數組信息和數組測試

獲取數組長度,可使用count()、length()或size()方法,後二者等價。而count()方法經過參數或語句塊的方式還能獲取知足條件的元素個數。

a = %w(a b c d e)
p a.length      # 5
p a.count       # 5
p a.size        # 5

ary = [1, 2, 4, 2]
ary.count                  # 4,數組元素個數
ary.count(2)               # 2,等於2的元素個數
ary.count {|x| x%2 == 0}   # 3,偶元素個數

檢查數組是否爲空數組,使用empty?()方法:

p a.empty?      # false

檢查數組是否包含某元素,使用include?()方法:

p a.include?('c')  # true
p a.include?('x')  # false

向數組中增、刪元素

插入元素

push()/append()、unshift()、insert()或特殊符號<<,注意它們都是原處修改數組對象的:

# push()向尾部插入元素
# push()返回數組自身,能夠鏈式插入
# append()等價於push()
arr = [1, 2, 3, 4]
arr.push(5)   # arr = [1, 2, 3, 4, 5]
arr.push(6).push(7) #  arr = [1,2,3,4,5,6,7]

# <<向尾部插入元素
# <<返回數組自身,因此能夠進行鏈式插入
arr = [1, 2, 3, 4]
arr << 5          # arr = [1, 2, 3, 4, 5]
arr << 6 << 7     # arr = [1,2,3,4,5,6,7]
arr <<8 <<[9, 10] # [1,2,3,4,5,6,7,8,[9,10]]

# unshift()向頭部插入元素
arr = [1, 2, 3, 4]
arr.unshift(0) # arr = [0, 1, 2, 3, 4]

# insert()向給定索引位置處插入元素,可一次性插入多個
arr = [0, 1, 2, 3, 4]
arr.insert(3, "Ruby") # [0, 1, 2, 'Ruby', 3, 4]
arr.insert(3,"Perl","Shell") # # [0,1,2,'Perl','Shell','Ruby',3,4]

# 經過範圍賦值的方式插入元素
arr = [0, 1, 2, 3, 4]
arr[1,0] = [11, 22]  # [0, 11, 22, 1, 2, 3, 4]
arr[1..1] = [11, 22]  # 與上等價

刪除元素

按位置刪除元素

pop()、shift()、delete_at():

# pop()移除數組尾部元素並返回該元素
arr = [1, 2, 3, 4, 5, 6]
arr.pop   # 返回6, arr變成[1, 2, 3, 4, 5]

# shift()從數組頭部移除一個元素並返回該元素
arr.shift     # 返回1,arr變成[2, 3, 4, 5]

# delete_at()移除給定索引位置處的元素並返回該元素
arr.delete_at(2)  # 返回4,arr變成[2, 3, 5]

# 經過範圍賦值的方式刪除元素
arr = [1, 2, 3, 4, 5, 6]
arr[1, 2] = []   # arr = [1, 4, 5, 6]

按給定值刪除元素

delete():

# delete()刪除數組中等於某個值的元素
arr1=[1, 2, 2, 4]
arr2 = %w(Perl Shell Python Ruby)
arr1.delete(2)       # 返回2,arr1變成[1,4]
arr2.delete "Shell"  # 返回"Shell",arr2變成%w(Perl Python Ruby)

刪除重複元素

uniq()、compact()以及成對的帶感嘆號後綴的uniq!()、compact!():

# uniq()刪除重複元素,不是原處修改對象的
arr=%w(a b c x y a c b y)
uniq_arr = arr.uniq   # uniq_arr=%w(a b c x y),arr不變

# uniq!()刪除重複元素,直接修改原始對象且返回修改後的數組
arr=%w(a b c x y a c b y)
uniq_arr = arr.uniq!   # uniq_arr和arr都變成%w(a b c x y)
# compact()刪除全部nil元素,壓縮數組,不是原處修改對象
arr = ["a", "b", "c", nil, "a", "c", nil, "b"]
compact_arr = arr.compact
p compact_arr    # ["a", "b", "c", "a", "c", "b"]
p arr            # ["a","b","c",nil,"a","c",nil,"b"]

# compact!()刪除全部nil元素,壓縮數組,原處修改對象
arr = ["a", "b", "c", nil, "a", "c", nil, "b"]
compact_arr = arr.compact!
p compact_arr    # ["a", "b", "c", "a", "c", "b"]
p arr            # ["a", "b", "c", "a", "c", "b"]

清空數組

clear()直接清空數組:

a = [1,2,3]
a.clear()    # a=[]

迭代數組

迭代方式有不少,經過for、while、times、數組自帶的each、each_index、reverse_each,還有Mix-in Enumerator後獲取的迭代方式:each_cons、each_slice、each_entry、each_with_index、with_index、each_with_object等。

此處僅簡單介紹for/while/times迭代和數組自身幾個迭代方法,關於從Enumerator獲取的相關的迭代方式,參見對應文章:Enumerator各類迭代方式

數組自身迭代方法



for、while、times迭代

# for
x = %w(a b c d e)

for i in x
  puts "element: #{i}"
end

# while
x = %w(a b c d e)

i=0
while i < x.length
  puts "element: #{x[i]}"
  i += 1
end

# times
x = %w(a b c d e)

x.length.times do |n|
  puts "element: #{x[n]}"
end

each()

each {|item| block} → ary
each → Enumerator

迭代數組中每一個元素,並將每一個元素傳遞給語句塊中的變量。最後返回數組自身。

# each迭代數組,不會修改原始數組
arr = [1, 2, 3, 4, 5]
arr.each {|a| print a -= 10, " "}
## 輸出:-9 -8 -7 -6 -5
## arr=[1, 2, 3, 4, 5]

each_index()

each_index {|index| block} → ary
each_index → Enumerator

迭代數組中的每一個元素,並將每一個元素的索引傳遞給語句塊中的變量。最後返回數組自身。

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

a.each_index do |x|
  puts "index: #{x}"
end

## 輸出結果:
=begin
index: 0
index: 1
index: 2
index: 3
=end

reverse_each()

# reverse_each反序迭代數組
arr = [1, 2, 3, 4, 5]
arr.reverse_each {|a| print "#{a}-"}
## 輸出5-4-3-2-1-

數組轉換和測試

類方法:

  • Array.try_convert(arg):嘗試將arg轉換成數組,若是能轉換則返回轉換後的數組對象,不然返回nil。能夠用它來測試參數對象是不是數組
  • to_a():返回self,對於數組自身來講,和to_ary()基本沒區別
  • to_ary():返回self,對於數組自身來講,和to_ary()基本沒區別
  • to_s():將數組轉換成字符串格式
  • to_h():將數組轉換成hash類型
  • inspect():等價於to_s()
# try_convert(arg)將轉換成數組
arr = Array.try_convert([1])    # [1]
arr1 = Array.try_convert(1)     # nil
arr2 = Array.try_convert("1")   # nil

# 測試元素是不是數組、字符串
if tmp=Array.try_convert(arg)
  # arg is an array
elsif tmp = String.try_convert(arg)
  # arg is a string
end
# to_a()和to_ary(),都返回self
# 表示返回當前數組的引用
# 對於數組自身來講,這二者沒差異
arr = ["foo", "bar"]
arr1 = arr.to_a

p arr.eql?(arr1)    # true
p arr.object_id     # 42285320
p arr1.object_id    # 42285320
# to_s()等價於inspect()
# 將數組轉換成字符串
arr = ["foo", "bar"]
arr.to_s   # [\"foo\", \"bar\"]
# to_h()將數組轉換成hash
h1 = [["foo",1], ["bar", 2]].to_h

# to_h的語句塊形式,每一個元素都將做爲塊中的變量
h2 = [["foo", 1],["bar", 2]].to_h {|x| x}
h3 =  ["foo", "bar"].to_h {|s| [s, s * 2] }

p h1   # {"foo"=>1, "bar"=>2}
p h2   # {"foo"=>1, "bar"=>2}
p h3   # {"foo"=>"foofoo", "bar"=>"barbar"}

數組大小、等同的比較

四種比較方式:等值比較==、hash值比較eql?、大小比較<=>和是否同一數組對象的比較equal?()

數組還繼承了Object類的===,對於數組而言,它等價於==

==

只有兩數組長度相同、兩數組對應索引位置的元素使用==測試也相等,才斷定兩數組相等。

返回true/false。注意,若是含有nil元素,則nil與nil的比較結果爲true。

[ "a", "c" ]    == [ "a", "c", 7 ]     # false
[ "a", "c", 7 ] == [ "a", "d", "f" ]   # false
[ "a", "c", 7 ] == [ "a", "c", 7 ]     # true
["a", 1, 1.2] == ["a", 1.0, 1.2]       # true

eql?()

只有兩數組對象的內容徹底相同時返回true,不然返回false。

它是根據各對象計算出的hash值比較的,通常來講比==要嚴格一點。

[1, 2] == [1, 2.0]      # true
[1, 2].eql?([1, 2.0])   # false
[1, 2].eql?([1, 2])     # true

[1, 2].hash    # 2027605168499122706
[1, 2.0].hash  # 3393919734812826294

equal?()

繼承自Object類,它比較的是二者是不是同一對象。這個方法基本上不會被子類重寫。

這個方法比==eql?()都要嚴格。

[1,2].equal?([1,2])  # false

a = [1, 2]
b = a
a.equal?(b)  # true

<=>

這個比較符號,當執行的是obj1 <=> obj2時:

  • obj1大於obj2時,該比較表達式返回1
  • obj1等於obj2時,該比較表達式返回0
  • obj1小於obj2時,該比較表達式返回-1

對於數組而言,對數組中的每一個元素都一一對應的比較,直到找到某個數組中的元素不等於另外一個數組中對應索引處的元素就返回結果。若是數組長短不一致,且較短數組的全部元素都和另外一數組對應元素相等,則長數組更大。因而能夠推斷,只有兩數組長度、內容徹底相等(經過==號比較,例如1等於1.0),數組才相等。

arr = [1, 3, 5, 7]
arr1 = [1, 3.0, 5, 7]
arr2 = [1, 3, 5]
arr3 = [1, 3, 5, 7, 9]
arr4 = [1, 3, 7, 5]
arr5 = [1, 3, 7, 5, nil]

p arr <=> arr1   # 0
p arr <=> arr2   # 1
p arr <=> arr3   # -1
p arr <=> arr4   # -1
p arr <=> arr5   # -1
相關文章
相關標籤/搜索