POSTS
第十章、ruby中的数组
数组
这一部分的教程将介绍数组。数组是有序对象的集合。
一个这是在某个时刻只能保存一项数值。不过数组可以保存多项。这些数据项被称为数组的元素。数组可以保存任何类型的数据。每个元素可以使用索引来引用。第一个元素的索引值为0。
注意Ruby的数组与C、C++或者Java中的数组有很大的不同。
#!/usr/bin/ruby
nums = [1, 2, 3, 4, 5]
nums.each do |num|
puts num
end
我们的第一个例子创建了一个包含5个整数的数组,然后在终端上打印这些元素。
nums = [1, 2, 3, 4, 5]
这行创建了一个包含5个整数的数组,每个元素使用逗号分隔开来。
nums.each do |num|
puts num
end
我们使用each方法遍历数组并在终端上打印每个元素。
$ ./array.rb
1
2
3
4
5
程序的输出结果。
创建数组
Ruby中数组是一个对象,可以使用new方法进行实例化。
#!/usr/bin/ruby
nums = Array.new
nums.push 1
nums.push 2
nums.push 3
nums.push 4
nums.push 5
puts nums
这个脚本中我们创建了一个数组nums,然后往里面添加了5个整数。
nums = Array.new
创建数组对象。
nums.push 1
push方法是往数组尾部添加一项数据。
我们继续使用new方法创建数组。
#!/usr/bin/ruby
a1 = Array.new
a2 = Array.new 3
a3 = Array.new 6, "coin"
a4 = Array.new [11]
a5 = Array.new (15) {|e| e*e}
puts [a1, a2, a3, a4, a5].inspect
Array类的new方法有一些选项。
a1 = Array.new
创建一个空的数组,支持稍后再往其中填充数据。
a2 = Array.new 3
创建一个数组包含3个nil对象。
a3 = Array.new 6, "coin"
创建一个数组包含6个”coin”字符串。第一个选项是数组的大小;第二个选项是填充的对象。
a4 = Array.new [11]
第四个数组只有一项数据。
a5 = Array.new (15) {|e| e*e}
创建一个有15个元素的数组,每个元素都是在代码块中创建。这里计算了序号数的平方。
puts [a1, a2, a3, a4, a5].inspect
将所有的数组放在一个数组中。数组是可以放入另一个数组里的。然后调用数组的inspect方法,它将对其所有的元素都调用该方法。inspect方法将返回代表这个数组的字符串。当我们需要快速检查数组内容时它非常有用。
$ ./arraynew.rb
[[], [nil, nil, nil], ["coin", "coin", "coin", "coin", "coin", "coin"],
[11], [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196]]
我们可以所有创建的数组。
接下来的脚本展示了多种创建数组的方法。
#!/usr/bin/ruby
integers = [1, 2, 3, 4, 5]
animals = %w( donkey dog cat dolphin eagle )
weights = Array.new
weights << 4.55 << 3.22 << 3.55 << 8.55 << 3.23
puts integers.inspect
puts animals.inspect
puts weights.inspect
我们创建了三个数组分别包含了整数、字符串和小数。
integers = [1, 2, 3, 4, 5]
这行创建了一个包含5个整数的数组。这是最经典的方法。数组的每个元素放在中括号中用逗号隔开。
animals = %w( donkey dog cat dolphin eagle )
这行代码创建了一个有5个元素的字符串数组。这种方式我们不需要逗号和双引号,减少了按键次数。
weights = Array.new
weights << 4.55 << 3.22 << 3.55 << 8.55 << 3.23
第三种方法有两步。首先创建一个数组对象,然后用数据将其初始化。这是正式的数组创建方法。上面方法实际上是这个的简写。
puts integers.inspect
$ ./creation.rb
[1, 2, 3, 4, 5]
["donkey", "dog", "cat", "dolphin", "eagle"]
[4.55, 3.22, 3.55, 8.55, 3.23]
例子的输出结果。
数组的数据项没限制必须是数字和字符串。数组可以包含Ruby中的所有类型的数据。
#!/usr/bin/ruby
class Empty
end
nums = [1, 2, 3, 4, 5]
various = [1, -1, "big", 3.4, Empty.new, nums, :two]
puts various.inspect
我们在数组中放了多种Ruby对象。
various = [1, -1, "big", 3.4, Empty.new, nums, :two]
这个数组包含了数字、字符串、自定义对象、另一个数组和一个符号。
$ ./arrayobjects.rb
[1, -1, "big", 3.4, #<Empty:0x987f704>, [1, 2, 3, 4, 5], :two]
arrayobjects.rb脚本的运行结果。
下一个例子展示嵌套数组;即一个数组包含另一个数组。Ruby中可以在数组中嵌套数组。
#!/usr/bin/ruby
numbers = [1, 2, 3, [2, 4, 6, [11, 12]]]
puts numbers.length
puts numbers[0], numbers[1]
puts numbers[3][0]
puts numbers[3][1]
puts numbers[3][3][0]
puts numbers[3][3][1]
puts numbers.flatten!.inspect
数组[11, 12]被嵌套在[2, 4, 6, …]里,这个数组又被嵌套在[1, 2, 3, …]数组里。
puts numbers.length
length方法返回4。内嵌数组只算一个元素。
puts numbers[0], numbers[1]
这里[]字符用于访问数组的元素。上面这行代码返回第一个和第二个元素。
puts numbers[3][0]
puts numbers[3][1]
这里我们访问内嵌数组的元素。[3][0]返回内嵌数组的第一个元素,在这里是2。同样的[3][1]返回内嵌数组的第二个元素,这里是4。
puts numbers[3][3][0]
puts numbers[3][3][1]
现在我们进入更深一层。我们访问更深一层数组的元素。[3][3]返回[11, 12]数组。然后从这个数组获取第一个和第二个元素。
puts numbers.flatten!.inspect
flatten!方法将数组变为平坦,它将所有的内嵌数组元素创建成一个新的数组。
$ ./arrayofarrays.rb
4
1
2
2
4
11
12
[1, 2, 3, 2, 4, 6, 11, 12]
输出结果。
打印数组内容
要在终端上打印数组的元素有多种方法可以完成。
#!/usr/bin/ruby
integers = [1, 2, 3, 4, 5]
puts integers
puts integers.inspect
integers.each do |e|
puts e
end
这个脚本我们将数组的元素打印了三次。
puts integers
数组作为puts/print方法的参数是最简单的方式。每行将打印一个元素。
puts integers.inspect
使用inspect方法输出结果的可读性更好。
integers.each do |e|
puts e
end
each方法为每个元素都调用一次一个代码块,元素作为参数传递。我们简单的参元素使用puts方法。
$ ./printarray1.rb
1
2
3
4
5
[1, 2, 3, 4, 5]
1
2
3
4
5
数组在终端上打印了三次。
第二个例子我们提供了两个额外的方法打印数组。
#!/usr/bin/ruby
integers = [1, 2, 3, 4, 5]
integers.length.times do |idx|
puts integers[idx]
end
integers.each_with_index do |num, idx|
puts "value #{num} has index #{idx}"
end
第一种情况我们组合使用了length和times方法。第二种情况我们使用了each_with_index方法。
integers.length.times do |idx|
puts integers[idx]
end
length方法返回数组的长度。times方法将接下来的代码块迭代length次,传递的值从0到length-1。这些数字作为数组的索引使用。
integers.each_with_index do |num, idx|
puts "value #{num} has index #{idx}"
end
each_with_index方法迭代数组并将元素及其索引传递给代码块。用这种方法我们可以简单的打印元素和它的索引。
$ ./printarray2.rb
1
2
3
4
5
value 1 has index 0
value 2 has index 1
value 3 has index 2
value 4 has index 3
value 5 has index 4
输出结果。
读取数组元素
这节我们将从数组读取数据。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.first
puts lts.last
puts lts.at(3)
第一个例子我们展示了三个简单的方法进行数据检索。
puts lts.first
puts lts.last
first方法读取数组的第一个元素;last方法读取数组的最后一个元素。
puts lts.at(3)
at方法返回指定索引的元素。
$ ./retrieval.rb
a
h
d
retrieval.rb程序的输出结果。
[]符号可以用于访问数据。这是传统访问数据的方法,许多语言都使用这种方法。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h }
puts lts[0]
puts lts[-1]
puts lts[0, 3].inspect
puts lts[2..6].inspect
puts lts[2...6].inspect
我们展示了5个使用[]符号的例子。
puts lts[0]
puts lts[-1]
我们获取数组的第一个和最后一个元素。第一项的索引为0,最后一项的索引为-1。
puts lts[0, 3].inspect
当中括号里有两个数时,第一个是开始的索引,第二个是长度。这行代码返回从0开始的3个元素。注意inspect方法仅是为了让输出可读而已。
puts lts[2..6].inspect
puts lts[2...6].inspect
我们可以在中括号使用范围操作符。第一行读取第2个到第6个元素,第二行读取第2个到第5个元素。
接下来展示values_at方法。这个方法的优势是可以选择多个位置的元素。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.values_at(1..5).inspect
puts lts.values_at(1, 3, 5).inspect
puts lts.values_at(1, 3, 5, 6, 8).inspect
puts lts.values_at(-1, -3).inspect
values_at方法返回一个数组包含选中的元素。inspect方法仅是为了让输出可读而已。
puts lts.values_at(1..5).inspect
这行代码返回索引为1到5的元素。
puts lts.values_at(1, 3, 5).inspect
这里我们读取索引为1、3、5的元素。
puts lts.values_at(1, 3, 5, 6, 8).inspect
我们可以指定多个索引,如果指定的索引没有元素则返回nil。
puts lts.values_at(-1, -3).inspect
负数的索引表示从数组尾部开始。
$ ./retrieval3.rb
["b", "c", "d", "e", "f"]
["b", "d", "f"]
["b", "d", "f", "g", nil]
["h", "f"]
脚本的输出结果。
我们使用fetch方法从数组读取数据。
#!/usr/bin/ruby
lts = [0, 1, 2, 3, 4, 5, 6]
puts lts.fetch(0)
puts lts.fetch(-2)
puts lts.fetch(8, 'undefined')
puts lts.fetch(8) { |e| -2*e }
我们展示了fetch方法的一些形式的用法。
puts lts.fetch(0)
puts lts.fetch(-2)
第一行打印了数组的第一个元素。第二行打印了倒数第二个元素。
puts lts.fetch(8, 'undefined')
第三种形式是返回指定索引的元素,如果索引超出范围则返回默认值,这里是’undefined’。没有第二个参数则会抛出IndexError错误。
puts lts.fetch(8) { |e| -2*e}
最后一种形式我们定义了一个代码块,传递了索引对应的值,这个方法返回了代码块调用的结果。
$ ./retrieval4.rb
0
5
undefined
-16
脚本的输出结果。
我们将展示take和take_while方法的用法。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.take(4).inspect
lts2 = lts.take_while { |e| e < 'f' }
puts lts2.inspect
take n方法返回开头的n个元素。take_while方法将元素传递给一个代码块,直到代码块返回nil或者false才停止迭代,并返回之前的元素。
puts lts.take(4).inspect
这里我们返回开头的4个元素。
lts2 = lts.take_while { |e| e < 'f' }
puts lts2.inspect
这里我们从原数组创建了一个新的数组,这个新数组包含了所有小于’f’的字符。
$ ./retrieval5.rb
["a", "b", "c", "d"]
["a", "b", "c", "d", "e"]
retrieval5.rb程序的输出结果。
slice方法与[]符号相同。返回一个或者多个元素。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.slice(0)
puts lts.slice(-1)
puts lts.slice(0, 3).inspect
puts lts.slice(2..6).inspect
puts lts.slice(2...6).inspect
展示了5个slice 方法的例子。
puts lts.slice(0)
puts lts.slice(-1)
这些形式的slice方法返回一个数组元素。第一行代码返回第一个元素,第二行代码返回最后一个元素。
puts lts.slice(0, 3).inspect
第一个参数是起始索引,第二个参数是长度。这一行代码返回从0开始的3个元素。
puts lts.slice(2..6).inspect
puts lts.slice(2...6).inspect
我们可以在slice方法中使用范围操作符。第一行读取第2到6的元素,第二行读取第2到5的元素。
$ ./retrieval6.rb
a
h
["a", "b", "c"]
["c", "d", "e", "f", "g"]
["c", "d", "e", "f"]
slice方法返回数组的一部分,一个或者多个元素。
可以随机选择数组的一个元素。Ruby中的sample方法可以实现。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.sample
puts lts.sample(3).inspect
sample方法有两种形式。第一种我们选择一个随机元素。第二种我们选择n个随机元素。
$ ./random.rb
b
["c", "f", "d"]
$ ./random.rb
d
["c", "d", "e"]
执行两次结果不同。
使用数组
接下来的例子介绍一些Ruby数组的方法。
#!/usr/bin/ruby
num1 = [1, 2, 3, 4, 5]
num2 = [6, 7, 8, 9, 10]
puts num1 + num2
puts num1.concat num2
我们定义了两个数组,将它们相加。
puts num1 + num2
puts num1.concat num2
有两个数组相加的方法。使用+操作符或者concat方法。
Ruby中数组有丰富的方法。例如length方法返回数组的元素个数。
#!/usr/bin/ruby
lts = %w{ a b c d e f}
puts lts.inspect
puts "Array has #{lts.length} elements"
puts "The first element is #{lts.first}"
puts "The last element is #{lts.last}"
puts lts.eql? lts.dup
puts lts.eql? lts.dup.delete_at(0)
lts.clear
puts lts.inspect
puts lts.empty?
上面的脚本我们介绍了7个新方法。
puts "Array has #{lts.length} elements"
length方法决定数组的大小。
puts "The first element is #{lts.first}"
puts "The last element is #{lts.last}"
这里我们获取第一个和最后一个元素。
puts lts.eql? lts.dup
eql?方法指出两个数组是否相等。这里是返回true。dup方法创建一个浅复制对象。
puts lts.eql? lts.dup.delete_at(0)
delete_at方法从数组的开头删除元素。现在两个数组不相同了。
lts.clear
clear方法删除数组的所有元素。
puts lts.empty?
empty?方法检查数组是否为空。这里返回true,因为我们已经将所有元素都删除了。
$ ./basics.rb
["a", "b", "c", "d", "e", "f"]
Array has 6 elements
The first element is a
The last element is f
true
false
[]
true
输出结果。
一些Ruby的数组方法以感叹号结尾。这是Ruby的习惯。感叹号告诉程序员这个方法会修改数据。感叹号本身没有什么作用,它只是名字的约定。
#!/usr/bin/ruby
chars = %w{a b c d e}
reversed_chars = chars.reverse
puts reversed_chars.inspect
puts chars.inspect
reversed_chars = chars.reverse!
puts reversed_chars.inspect
puts chars.inspect
Ruby中有一些比较相似的方法。reverse和reverse!方法都是改为数组元素的顺序,将它们反转。不同在于reverse方法返回反转后的数组,原数组不变。reverse!方法会同时修改原数组。
$ ./twotypes.rb
["e", "d", "c", "b", "a"]
["a", "b", "c", "d", "e"]
["e", "d", "c", "b", "a"]
["e", "d", "c", "b", "a"]
我们清晰的看到前两个数组不同,第三和第四个数组是相同的。
一些其他的方法展示。
#!/usr/bin/ruby
numbers = [1, 2, 2, 2, 3, 4, 5, 8, 11]
puts numbers.index 2
puts numbers.index 11
puts numbers.rindex 2
puts numbers.include? 3
puts numbers.include? 10
puts numbers.join '-'
puts numbers.uniq!.inspect
介绍5个额外的方法。
puts numbers.index 2
puts numbers.index 11
index方法返回数组中元素对应的索引。索引是从左算起。第一行返回1,它是第一个2的索引。数组中仅有一个11,它的索引是8。
puts numbers.rindex 2
rindex方法返回从右边开始的索引。这里2的最右索引为3。
puts numbers.include? 3
puts numbers.include? 10
include?方法检查一个元素是否在数组中。第一行返回true,3在数组中。第二行返回false,数组中没有10。作为约定Ruby中以问号结尾的方法返回一个布尔值,并且对数组没有影响。
puts numbers.join '-'
join方法返回一个字符串,它是将数组的元素用指定的符号分隔开来。
puts numbers.uniq!.inspect
uniq!方法移除重复的元素。在数组中有3个2,调用方法之后就只剩一个2了。
$ ./methods2.rb
1
8
3
true
false
1-2-2-2-3-4-5-8-11
[1, 2, 3, 4, 5, 8, 11]
注意join方法产生的是一个字符串,它是数组的数字用*-*符号分隔。
修改数组
这一节我们介绍与数组修改相关的方法。基本的我们做一些插入和删除操作。
#!/usr/bin/ruby
lts = []
lts.insert 0, 'E', 'F', 'G'
lts.push 'H'
lts.push 'I', 'J', 'K'
lts << 'L' << 'M'
lts.unshift 'A', 'B', 'C'
lts.insert(3, 'D')
puts lts.inspect
从一个空数组开始,我们使用不同的插入方法。
lts.insert 0, 'E', 'F', 'G'
insert方法往lts数组插入了3个元素。
lts.push 'H'
lts.push 'I', 'J', 'K'
push方法往数组添加元素,我们可以添加一个或多个元素。
lts << 'L' << 'M'
<<与push方法相同。这个操作可以链式调用。
lts.unshift 'A', 'B', 'C'
unshift方法将元素插入在数组前端。
lts.insert(3, 'D')
这里insert方法在指定的位置插入’D’字符。
$ ./insertion.rb
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"]
使用上面的方法,我们创建了一个包含大写字母的数组。
一些删除数组元素的方法。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
lts.pop
lts.pop
puts lts.inspect
lts.shift
lts.shift
puts lts.inspect
lts.delete_at(0)
lts.delete('d')
puts lts.inspect
puts lts.clear
puts lts.inspect
这个脚本展示了5个用于删除数组元素的方法。
lts = %w{ a b c d e f g h}
创建一个有8个元素的数组。
lts.pop
pop方法移除最后一个元素。
lts.shift
shift方法移除数组的第一个元素。
lts.delete_at(0)
delete_at方法删除指定位置的元素。我们删除剩余元素的第一个元素。
puts lts.clear
clear方法清除所有元素。
lts.delete('d')
delete方法删除指定的一项数据。
$ ./deletion.rb
["a", "b", "c", "d", "e", "f"]
["c", "d", "e", "f"]
["e", "f"]
[]
输出结果。
目前为止我们使用的方法同时只修改一个数组项。Ruby中有一些方法可以一次修改多个数组项的。
#!/usr/bin/ruby
nms = [2, -1, -4, 0, 4, 3, -2, 3, 5]
nms.delete_if { |x| x < 0 }
puts nms.inspect
例子介绍了delete_if方法用于删除所有符合条件式的数据项。
nms.delete_if { |x| x < 0 }
这行删除所有的负数。
$ ./delete_if.rb
[2, 0, 4, 3, 3, 5]
我们从nms数组中删除了所有的负数。
我们展示两个其他的方法处理多数据项。
#!/usr/bin/ruby
lts = %w{ a b c d e f g}
puts lts.inspect
lts.reject! do |e|
e =~ /[c-y]/
end
puts lts.inspect
lts.replace(["x", "y", "z"])
puts lts.inspect
我们使用了两个方法,reject!和replace。
lts.reject! do |e|
e =~ /[c-y]/
end
reject!方法移除了所有项满足条件式的数据。这里我们删除所有符合正则式的字母。
lts.replace(["x", "y", "z"])
replace方法将使用给定的数据替换数组项。如果可能它会截断或者扩展数组。
$ ./modify.rb
["a", "b", "c", "d", "e", "f", "g"]
["a", "b"]
["x", "y", "z"]
modify.rb例子的输出结果。
集合操作
这一节中我们展示Ruby数组的集合操作。数学中集合是独立对象的收集。
#!/usr/bin/ruby
A = [1, 2, 3, 4, 5]
B = [4, 5, 6, 7, 8]
union = A | B
isect = A & B
diff1 = A - B
diff2 = B - A
sdiff = (A - B) | (B - A)
puts "Union of arrays: #{union}"
puts "Intersection of arrays: #{isect}"
puts "Difference of arrays A - B: #{diff1}"
puts "Difference of arrays B - A: #{diff2}"
puts "Symmetric difference of arrays: #{sdiff}"
上面的脚本我们展示了一些集合操作,并集、交集、差集和对称差集。
nums1 = [1, 2, 3, 4, 5]
nums2 = [4, 5, 6, 7, 8]
字义了两个整数数组。都是集合,因此每个元素都只出现了一次。两个数组有两个相同的数,4和5。
union = nums1 | nums2
数组的并集。两个数组相加,每个元素最终也只出现一次。
isect = A & B
数组交集。输出两个数组都存在的元素。这里是4和5。
diff1 = A - B
diff2 = B - A
两个差集,也称补集。第一行我们得到了所有在A中出现B中没有出现的元素。第二行我们得到B中出现A中没有出现的元素。
sdiff = (A - B) | (B - A)
对称差集。A或B中存在,但不同时存在于A和B。
$ ./setoperations.rb
Union of arrays: [1, 2, 3, 4, 5, 6, 7, 8]
Intersection of arrays: [4, 5]
Difference of arrays A - B: [1, 2, 3]
Difference of arrays B - A: [6, 7, 8]
Symmetric difference of arrays: [1, 2, 3, 6, 7, 8]
输出结果。
select、collect、map方法
下面的例子我们展示三个方法:select、collect和map。
#!/usr/bin/ruby
nums = [1, 3, 2, 6, 7, 12, 8, 15]
selected = nums.select do |e|
e > 10
end
puts selected.inspect
collected = nums.collect do |e|
e < 10
end
puts collected.inspect
mapped = nums.map do |e|
e*2
end
puts mapped.inspect
所有这些方法都对数组的元素执行许多操作。
selected = nums.select do |e|
e > 10
end
上面的代码使用select方法创建了一个新数组。我们只选择了满足条件式的元素。这里我们选择了所有大于10的元素。
collected = nums.collect do |e|
e < 10
end
collect方法稍微不同。它只收集代码块的返回值。新的数组只包含true和false。
mapped = nums.map do |e|
e*2
end
map方法与collect方法相同。上面的代码根据已存在的数组创建了一个新的数组。每个元素都乘以2.
$ ./mass.rb
[12, 15]
[true, true, true, true, true, false, true, false]
[2, 6, 4, 12, 14, 24, 16, 30]
创建了新数组。
元素排序
最后我们对数组元素进行排序。
#!/usr/bin/ruby
planets = %w{ Mercury Venus Earth Mars Jupiter
Saturn Uranus Neptune Pluto }
puts "#{planets.sort}"
puts "#{planets.reverse}"
puts "#{planets.shuffle}"
例子中使用了三个Ruby数组的方法对数组元素进行重组。
puts "#{planets.sort}"
sort方法按字母顺序进行排序。
puts "#{planets.reverse}"
reverse方法反转元素并返回新的数组。
puts "#{planets.shuffle}"
shuffle方法将数组元素随机重组。
$ ./ordering.rb
["Earth", "Jupiter", "Mars", "Mercury", "Neptune", "Pluto", "Saturn", ...]
["Pluto", "Neptune", "Uranus", "Saturn", "Jupiter", "Mars", "Earth", ...]
["Earth", "Jupiter", "Mercury", "Saturn", "Mars", "Venus", "Uranus", ...]
例子的输出结果。
在这章我们学习了Ruby的数组。zetcode原文地址