[翻译]Ruby教程10——数组

数组

这一部分的教程将介绍数组。数组是有序对象的集合。


一个这是在某个时刻只能保存一项数值。不过数组可以保存多项。这些数据项被称为数组的元素。数组可以保存任何类型的数据。每个元素可以使用索引来引用。第一个元素的索引值为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| ee}

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| ee}

创建一个有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

The inspect method prints the string representation of the array to the terminal.


$ ./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

第一种情况我们组合使用了lengthtimes方法。第二种情况我们使用了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 method方法返回一个数组包含选中的元素。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| -2e }

我们展示了fetch方法的一些形式的用法。


puts lts.fetch(0)
puts lts.fetch(-2)

第一行打印了数组的第一个元素。第二行打印了倒数第二个元素。


puts lts.fetch(8, ‘undefined’)

第三种形式是返回指定索引的元素,如果索引超出范围则返回默认值,这里是‘undefined’。没有第二个参数则会抛出IndexError错误。


puts lts.fetch(8) { |e| -2e}

最后一种形式我们定义了一个代码块,传递了索引对应的值,这个方法返回了代码块调用的结果。


$ ./retrieval4.rb

0

5

undefined

-16


脚本的输出结果。




我们将展示taketake_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?方法指出两个数组是否相等。这里是返回truedup方法创建一个浅复制对象。


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中有一些比较相似的方法。reversereverse!方法都是改为数组元素的顺序,将它们反转。不同在于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 language=”-y”][/c]/
end

puts lts.inspect

lts.replace([“x”, “y”, “z”])
puts lts.inspect

我们使用了两个方法,reject!replace


lts.reject! do |e|
e =~ /[c language=”-y”][/c]/
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方法


下面的例子我们展示三个方法:selectcollectmap


#!/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|
e2
end

puts mapped.inspect

所有这些方法都对数组的元素执行许多操作。


selected = nums.select do |e|
e > 10
end

上面的代码使用collect方法创建了一个新数组。我们只选择了满足条件式的元素。这里我们选择了所有大于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的数组。




原文地址: http://zetcode.com/lang/rubytutorial/arrays/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial