第 13 章 数组类
13.1 复习数组
数组是带索引的对象的集合。
# 使用 [] 创建数组
>> names = ["Mike", "Tom", "Jim", "Ken"]
=> ["Mike", "Tom", "Jim", "Ken"]
# 可以从数组中获取某个索引的元素(对象)
>> names[2]
=> "Jim"
# 可以将任意的值(对象)保存到数组的某个索引的元素中
>> names[0] = "Alex"
=> "Alex"
>> names
=> ["Alex", "Tom", "Jim", "Ken"]
# 使用迭代器可以逐个取出数组中的元素
=> ["Alex", "Tom", "Jim", "Ken"]
>> names.each{|name| puts name}
Alex
Tom
Jim
Ken
13.2 数组的创建方法
使用 []
创建数组
>> nums = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> strs = ["a", "b", "c", "d"]
=> ["a", "b", "c", "d"]
13.2.1 使用 Array.new
创建类的实例时使用的 new
方法,创建数组时也同样可以使用。
# 若 new 方法没有参数,则会创建元素个数为 0 的数组
>> a = Array.new
=> []
# 若 new 方法只有 1 个参数,则会创建元素个数为该参数个数,且各元素初始值都为 nil 的数组。
>> Array.new(5)
=> [nil, nil, nil, nil, nil]
# 若 new 方法有两个参数, 则第 1 个参数代表元素的个数,第 2 个参数代表元素值的初始值。
>> Array.new(5, 0)
=> [0, 0, 0, 0, 0]
13.2.2 使用 %w
与 %i
使用 %w
创建不包含空白的字符串数组
>> lang = %w(Ruby Perl Python Scheme Pike REBOL)
=> ["Ruby", "Perl", "Python", "Scheme", "Pike", "REBOL"]
使用 %i
创建符号数组
>> lang = %i(Ruby Perl Python Scheme Pike REBOL)
=> [:Ruby, :Perl, :Python, :Scheme, :Pike, :REBOL]
13.2.3 使用 to_a
方法
很多类都定义了 to_a
方法,该方法能把该类的对象转换为数组。
# 对散列对象使用 to_a 方法,结果就会得到相应的数组的数组。
>> color_table = {black: "#000000", white: "#FFFFFF"}
=> {:black=>"#000000", :white=>"#FFFFFF"}
>> color_table.to_a
=> [[:black, "#000000"], [:white, "#FFFFFF"]]
13.2.4 使用字符串的 split
方法
对用逗号或者空白间隔的字符串使用 split
方法,也可以创建数组。
>> column = "2013/05/30 22:33 foo.html proxy.example.jp".split()
=> ["2013/05/30", "22:33", "foo.html", "proxy.example.jp"]
13.3 索引的使用方法
13.3.1 获取元素
通过 []
指定索引,获取元素。
>> alpha = ["a", "b", "c", "d", "e"]
=> ["a", "b", "c", "d", "e"]
# 数组的索引从 0 开始
>> alpha[1]
=> "b"
# 索引值为负数时,从数组的末尾开始获取元素。
>> alpha[-1]
=> "e"
>> alpha[-2]
=> "d"
# 指定索引范围,创建新数组并返回。
>> alpha[1..3] # 包括索引为 3 的元素
=> ["b", "c", "d"]
>> alpha[1...3] # 不包括索引为 3 的元素
=> ["b", "c"]
>> alpha[1..7] # 索引值比数组长度大时,返回数组最后一个元素。
=> ["b", "c", "d", "e"]
# 从某个元素开始,获取多个元素。
>> alpha[2, 2]
=> ["c", "d"]
>> alpha[2, 3]
=> ["c", "d", "e"]
通过数组方法指定索引,获取元素。
>> alpha.at(1)
=> "b"
>> alpha.at(-1)
=> "e"
>> alpha.at(-2)
=> "d"
>> alpha.slice(1)
=> "b"
>> alpha.slice(-1)
=> "e"
>> alpha.slice(-2)
=> "d"
>> alpha.slice(1..3)
=> ["b", "c", "d"]
>> alpha.slice(1...3)
=> ["b", "c"]
>> alpha.slice(1..7)
=> ["b", "c", "d", "e"]
>> alpha.slice(2, 2)
=> ["c", "d"]
>> alpha.slice(2, 3)
=> ["c", "d", "e"]
13.3.2 元素赋值
对一个元素赋值
>> alpha = ["a", "b", "c", "d", "e", "f"]
=> ["a", "b", "c", "d", "e", "f"]
>> alpha[1] = "B"
=> "B"
>> alpha[4] = "E"
=> "E"
>> alpha
=> ["a", "B", "c", "d", "E", "f"]
对多个元素赋值
>> alpha = ["a", "b", "c", "d", "e", "f"]
=> ["a", "b", "c", "d", "e", "f"]
>> alpha[2, 3] = ["C", "D", "E"]
=> ["C", "D", "E"]
>> alpha
=> ["a", "b", "C", "D", "E", "f"]
13.3.3 插入元素
插入元素其实也可以被认为是对 0 个元素进行赋值。因此,指定 [0, n]
后,就会在索引值为 n 的元素前插入元素。
>> alpha = ["a", "b", "c", "d", "e", "f"]
=> ["a", "b", "c", "d", "e", "f"]
>> alpha[2, 0] = ["X", "Y"]
=> ["X", "Y"]
>> alpha
=> ["a", "b", "X", "Y", "c", "d", "e", "f"]
13.3.4 通过多个索引创建数组
使用 values_at
方法,可以利用多个索引来分散获取多个元素,并用它们创建新数组。
>> alpha = %w(a b c d e f)
=> ["a", "b", "c", "d", "e", "f"]
>> alpha.values_at(1, 3, 5)
=> ["b", "d", "f"]
13.4 作为集合的数组
>> ary1 = ["a", "b", "c"]
=> ["a", "b", "c"]
>> ary2 = ["b", "c", "d"]
=> ["b", "c", "d"]
# 交集运算
>> ary1 & ary2
=> ["b", "c"]
# 并集运算
>> ary1 | ary2
=> ["a", "b", "c", "d"]
# 差集运算
>> ary1 - ary2
=> ["a"]
|
与 +
的不同点
>> num = [1, 2, 3]
=> [1, 2, 3]
>> even = [2, 4, 6]
=> [2, 4, 6]
>> num + even
=> [1, 2, 3, 2, 4, 6]
>> num | even
=> [1, 2, 3, 4, 6]
13.5 作为列的数组
队列:按元素被追加时的顺序来获取元素的数据结构,也就是“先进先出”。
栈:按与元素被追加时的顺序相反的顺序来获取元素的数据结构,也就是“后进先出”。
操作数组开头与末尾的元素的方法
元素操作 | 对数组开头的元素操作 | 对数组末尾的元素操作 |
---|---|---|
追加元素 | unshift | push |
删除元素 | shift | pop |
引用元素 | first | last |
>> alpha = ["b", "c", "d", "e"]
=> ["b", "c", "d", "e"]
# 在数组的开头插入元素
>> alpha.unshift("A")
=> ["A", "b", "c", "d", "e"]
>> alpha
=> ["A", "b", "c", "d", "e"]
# 删除数组开头的元素并将其作为返回值
>> alpha.shift
=> "A"
>> alpha
=> ["b", "c", "d", "e"]
>> alpha = ["a", "b", "c", "d"]
=> ["a", "b", "c", "d"]
# 在数组的末尾插入元素
>> alpha.push("E")
=> ["a", "b", "c", "d", "E"]
>> alpha
=> ["a", "b", "c", "d", "E"]
# 删除数组末尾的元素并将其作为返回值
>> alpha.pop
=> "E"
>> alpha
=> ["a", "b", "c", "d"]
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
# 获取数组开头的元素
>> a.first
=> 1
# 获取数组末尾的元素
>> a.last
=> 5
队列的实现:先进先出
>> queue = []
=> []
>> queue.push("a")
=> ["a"]
>> queue.push("b")
=> ["a", "b"]
>> queue.push("c")
=> ["a", "b", "c"]
>> queue.shift
=> "a"
>> queue
=> ["b", "c"]
>> queue.shift
=> "b"
>> queue
=> ["c"]
>> queue.shift
=> "c"
>> queue
=> []
栈的实现:后进先出
>> stack = []
=> []
>> stack.push("a")
=> ["a"]
>> stack.push("b")
=> ["a", "b"]
>> stack.push("c")
=> ["a", "b", "c"]
>> stack.pop
=> "c"
>> stack
=> ["a", "b"]
>> stack.pop
=> "b"
>> stack
=> ["a"]
>> stack.pop
=> "a"
>> stack
=> []
13.6 主要的数组的方法
13.6.1 为数组添加元素
a.unshift(item)
将 item 元素添加到数组的开头
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.unshift(0)
=> [0, 1, 2, 3, 4, 5]
>> a
=> [0, 1, 2, 3, 4, 5]
-
a << item
与a.push(item)
是等价的方法
在数组的末尾插入元素
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a << 6
=> [1, 2, 3, 4, 5, 6]
>> a.push(7)
=> [1, 2, 3, 4, 5, 6, 7]
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
# concat 是具有破坏性的方法,原有数组会被改变。
>> a.concat([8, 9])
=> [1, 2, 3, 4, 5, 8, 9]
>> a
=> [1, 2, 3, 4, 5, 8, 9]
# 而 + 则会返回一个新创建的数组,原有数组不会被改变。
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a + [8, 9]
=> [1, 2, 3, 4, 5, 8, 9]
>> a
=> [1, 2, 3, 4, 5]
-
a[n] = item
a[n..m] = item(s)
a[n, len] = item(s)
把数组 a
指定范围的元素替换为 item(s)
>> a = [1, 2, 3, 4, 5, 6, 7, 8]
=> [1, 2, 3, 4, 5, 6, 7, 8]
>> a[2..4] = 0
=> 0
>> a
=> [1, 2, 0, 6, 7, 8]
>> a[1, 3] = 9
=> 9
>> a
=> [1, 9, 7, 8]
专栏:具有破坏性的方法
会改变方法(也成为消息)接收者对象值的方法称为具有破坏性的方法。使用具有破坏性的方法时要特别小心,因为当有变量也引用了接收者对象时,如果接收者对象发生了改变,变量值也会随之发生变化。
>> a = [1, 2, 3, 4]
=> [1, 2, 3, 4]
>> b = a
=> [1, 2, 3, 4]
>> b.pop
=> 4
>> b
=> [1, 2, 3]
>> a
=> [1, 2, 3]
在 Ruby 的方法中,有像 sort
和 sort!
这样,在相同方法名后加上 !
的方法。为了区分方法是否具有破坏性,在具有破坏性的方法末尾添加 !
。
# 不具有破坏性的方法
>> a = [1, 3, 2, 5, 4]
=> [1, 3, 2, 5, 4]
>> a.sort
=> [1, 2, 3, 4, 5]
>> a
=> [1, 3, 2, 5, 4]
# 具有破坏性的方法
>> a = [1, 3, 2, 5, 4]
=> [1, 3, 2, 5, 4]
>> a.sort!
=> [1, 2, 3, 4, 5]
>> a
=> [1, 2, 3, 4, 5]
13.6.2 从数组中删除元素
- 从数组中删除所有
nil
元素
a.compact
a.compact!
>> a = [1, nil, 3, nil, nil]
=> [1, nil, 3, nil, nil]
>> a.compact!
=> [1, 3]
>> a
=> [1, 3]
- 从数组中删除指定索引的元素
a.delete_at(n)
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.delete_at(2)
=> 3
- 删除满足条件的元素
a.delete_if{ |item| ... }
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.delete_if{|i| i > 3}
=> [1, 2, 3]
>> a
=> [1, 2, 3]
a.reject{ |item| ... }
a.reject!{ |item| ... }
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.reject!{|i| i > 3}
=> [1, 2, 3]
>> a
=> [1, 2, 3]
- 删除指定范围的元素
a.slice!(n)
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.slice!(1)
=> 2
>> a
=> [1, 3, 4, 5]
a.slice!(n..m)
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.slice!(1..3)
=> [2, 3, 4]
>> a
=> [1, 5]
a.slice!(n, len)
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.slice!(1, 2)
=> [2, 3]
>> a
=> [1, 4, 5]
- 删除数组中重复的元素
a.uniq
a.uniq!
>> a = [1, 2, 3, 4, 2, 1]
=> [1, 2, 3, 4, 2, 1]
>> a.uniq!
=> [1, 2, 3, 4]
>> a
=> [1, 2, 3, 4]
- 删除数组开头的元素
a.shift
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.shift
=> 1
>> a
=> [2, 3, 4, 5]
- 删除数组末尾的元素
a.pop
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.pop
=> 5
>> a
=> [1, 2, 3, 4]
13.6.3 替换数组元素
- 数组映射
a.collect { |item| ... }
a.collect!{ |item| ... }
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.collect!{|item| item * 2}
=> [2, 4, 6, 8, 10]
>> a
=> [2, 4, 6, 8, 10]
a.map { |item| ... }
a.map! { |item| ... }
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.map!{|item| item * 2}
=> [2, 4, 6, 8, 10]
>> a
=> [2, 4, 6, 8, 10]
- 数组平铺
a.fill(value)
>> [1, 2, 3, 4, 5].fill(0)
=> [0, 0, 0, 0, 0]
a.fill(value, begin)
>> [1, 2, 3, 4, 5].fill(0, 2)
=> [1, 2, 0, 0, 0]
a.fill(value, begin, len)
>> [1, 2, 3, 4, 5].fill(0, 2, 2)
=> [1, 2, 0, 0, 5]
a.fill(value, n..m)
>> [1, 2, 3, 4, 5].fill(0, 2..3)
=> [1, 2, 0, 0, 5]
- 展开嵌套数组
a.flatten
a.flatten!
>> a = [1, [2, [3]], [4], 5]
=> [1, [2, [3]], [4], 5]
>> a.flatten!
=> [1, 2, 3, 4, 5]
>> a
=> [1, 2, 3, 4, 5]
- 反转数组的顺序
a.reverse
a.reverse!
>> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
>> a.reverse!
=> [5, 4, 3, 2, 1]
>> a
=> [5, 4, 3, 2, 1]
- 对数组进行排序
a.sort
a.sort!
a.sort{ |i, j| ... }
a.sort!{ |i, j| ... }
没有块时,使用 <=>
运算符比较
>> a = [2, 4, 3, 5, 1]
=> [2, 4, 3, 5, 1]
>> a.sort!
=> [1, 2, 3, 4, 5]
>> a
=> [1, 2, 3, 4, 5]
- 根据数组映射对数组的所有元素进行排序
a.sort_by{ |i| ... }
>> a = [2, 4, 3, 5, 1]
=> [2, 4, 3, 5, 1]
>> a.sort_by{|i| -i}
=> [5, 4, 3, 2, 1]
>> a
=> [2, 4, 3, 5, 1]
13.7 数组与迭代器
典型的迭代器 each
>> [1, 4, 5, 3, 2].each{|item| puts item}
1
4
5
3
2
=> [1, 4, 5, 3, 2]
接收者不是数组时,为了让迭代器的执行结果作为某个对象返回,也会用到数组。
>> a = 1..5
=> 1..5
>> a.collect{|i| i += 2}
=> [3, 4, 5, 6, 7]
>> a
=> 1..5
13.8 处理数组中的元素
13.8.1 使用循环与索引
list = ["a", "b", "c", "d"]
for i in 0..3
print "第 ", i+1, " 个元素是 ", list[i], "。\n"
end
结果:
第 1 个元素是 a。
第 2 个元素是 b。
第 3 个元素是 c。
第 4 个元素是 d
list = [1, 3, 5, 7, 9]
sum = 0
for i in 0..4
sum += list[i]
end
print "合计:", sum, "\n"
结果:
合计:25
13.8.2 使用 each
方法逐个获取元素
list = [1, 3, 5, 7, 9]
sum = 0
list.each do |elem|
sum += elem
end
print "合计:", sum, "\n"
结果:同上
list = ["a", "b", "c", "d"]
list.each_with_index do |elem, i|
print "第 ", i+1, " 个元素是 ", elem, "。\n"
end
结果:同上
13.8.3 使用具有破坏性的方法实现循环
a = [10, 20, 30, 40, 50]
while item = a.pop
puts item
end
puts "After While: a = #{a}"
结果:
50
40
30
20
10
After While: a = []
13.9 数组的元素
13.9.1 使用简单的矩阵
>> a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>> a[1][2]
=> 6
13.9.2 初始化时的注意事项
各个元素引用同一个对象
>> a = Array.new(3, [0, 0, 0])
=> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>> a[0][1] = 2
=> 2
>> a
=> [[0, 2, 0], [0, 2, 0], [0, 2, 0]]
指定 new
方法的块和元素个数,程序调用与元素个数一样次数的块,然后再将块的返回值赋值给元素。每次调用块都会生成新的对象,这样一来,各个元素引用同一个对象的问题就不会发生了。
a = Array.new(3) do
[0, 0, 0]
end
p a
a[0][1] = 2
p a
结果:
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[0, 2, 0], [0, 0, 0], [0, 0, 0]]
另一个例子
>> a = Array.new(5){|i| i + 1}
=> [1, 2, 3, 4, 5]
13.10 同时访问多个数组
ary1 = [1, 2, 3, 4, 5]
ary2 = [10, 20, 30, 40, 50]
ary3 = [100, 200, 300, 400, 500]
result = []
ary1.zip(ary2, ary3) do |a, b, c|
result << a + b + c
end
p result
结果
[111, 222, 333, 444, 555]