Ruby 零基础教程

Ruby 数组

数组(Arrays)是 Ruby 中最基础也是最重要的数据结构之一,专门用于存储有序的项目集合。数组的用途极其广泛,从简单的数字列表到复杂的对象集合,它都能轻松驾驭。

本章将全面覆盖创建数组的各种方法、访问数组元素的核心技巧,以及操作数组索引时必须注意的关键事项。

1. 创建数组

在 Ruby 中,有多种方式可以创建数组,每种方式都适用于不同的场景。掌握这些方法是你高效使用数组的第一步。

1.1 使用字面量 (Literal Notation)

创建数组最常见、最直观的方法是使用“字面量”表示法:用方括号 [] 将逗号分隔的元素列表括起来。

# 一个空数组
empty_array = []

# 一个包含数字的数组
number_array = [1, 2, 3, 4, 5]

# 一个包含字符串的数组
string_array = ["apple", "banana", "cherry"]

# 一个包含混合数据类型的数组
mixed_array = [1, "hello", 3.14, true]

puts empty_array.inspect
puts number_array.inspect
puts string_array.inspect
puts mixed_array.inspect

在上面的例子中:

  • empty_array 被初始化为一个空数组。
  • number_array 存储了一组整数。
  • string_array 包含了一个字符串列表。
  • mixed_array 完美展示了数组可以容纳不同数据类型的元素。这正是 Ruby 动态类型特性的魅力所在。

1.2 使用 Array.new 构造函数

Array.new 构造函数在创建数组时提供了更高的灵活性,特别是当你需要初始化一个特定大小或带有默认值的数组时。

创建指定大小的数组

你可以通过向 Array.new 传递一个整数参数来创建特定大小的数组。默认情况下,数组将用 nil 值填充。

# 创建一个大小为 5 的数组,用 nil 填充
array_of_nils = Array.new(5)
puts array_of_nils.inspect # 输出: [nil, nil, nil, nil, nil]

创建带有默认值的指定大小数组

要使用特定值初始化数组,请向 Array.new 传递两个参数:数组的大小和默认值。

# 创建一个大小为 3 的数组,用值 "default" 填充
default_array = Array.new(3, "default")
puts default_array.inspect # 输出: ["default", "default", "default"]
重要避坑指南: 当你使用可变对象(如字符串或另一个数组)作为默认值时,数组中的所有元素实际上都会指向内存中的同一个对象。修改其中一个元素,会连带影响所有其他元素。这是 Ruby 新手最常踩的坑!
# 演示可变默认值陷阱的示例
mutable_array = Array.new(3, []) # 意图:创建包含 3 个空数组的数组
mutable_array[0] << "added"      # 向第一个元素(数组)中添加 "added"
puts mutable_array.inspect       # 意外输出: [["added"], ["added"], ["added"]]

为了避免这个灾难,请配合代码块 (block) 使用 Array.new,这样就会为每个元素创建一个全新的独立对象:

# 正确做法:创建一个包含独立可变对象的数组
correct_array = Array.new(3) { [] } # 创建包含 3 个相互独立的空数组的数组
correct_array[0] << "added"         # 向第一个元素添加 "added"
puts correct_array.inspect          # 正确输出: [["added"], [], []]

在这里,代码块 { [] } 会为数组的每一个槽位执行一次,从而创建出全新、独立的空数组。

1.3 使用 %w 和 %W 创建字符串数组

Ruby 提供了 %w%W 这两种极其便利的简写符号,专门用于快速创建字符串数组。

  • %w 创建字符串数组时,不会解析任何特殊字符或变量。
  • %W 允许在字符串中进行变量插值和转义字符解析。
# 使用 %w (以空格分隔即可,无需写引号和逗号)
words_array = %w[apple banana cherry]
puts words_array.inspect # 输出: ["apple", "banana", "cherry"]

# 使用 %W 进行变量插值
name = "Alice"
greeting_array = %W[Hello #{name}, welcome!]
puts greeting_array.inspect # 输出: ["Hello Alice,", "welcome!"] (注意空格分隔规则)

1.4 将其他对象转换为数组

许多 Ruby 对象都可以使用 .to_a 方法转换为数组。

# 将范围 (Range) 转换为数组
range = (1..5)
range_array = range.to_a
puts range_array.inspect # 输出: [1, 2, 3, 4, 5]

# 将字符串转换为字符数组
string = "Ruby"
string_array = string.chars # 在这种场景下,使用 .chars 比 string.to_a 更常见也更高效
puts string_array.inspect # 输出: ["R", "u", "b", "y"]

2. 访问数组元素

访问数组中的元素是最基础的操作。Ruby 提供了多种灵活的方法,可以根据索引或其他条件来检索元素。

2.1 通过正数索引访问

Ruby 中的数组是从 0 开始索引 (zero-indexed) 的。这意味着第一个元素在索引 0 处,第二个在索引 1 处,依此类推。你可以将索引放在方括号 [] 内来访问对应的元素。

my_array = ["apple", "banana", "cherry", "date"]

# 访问第一个元素 (索引 0)
first_element = my_array[0]
puts "第一个元素: #{first_element}" # 输出: 第一个元素: apple

# 访问第三个元素 (索引 2)
third_element = my_array[2]
puts "第三个元素: #{third_element}" # 输出: 第三个元素: cherry

2.2 负数索引访问 (从后往前找)

Ruby 原生支持负数索引,这让你能够非常方便地从数组的尾部开始访问元素。-1 代表最后一个元素,-2 代表倒数第二个,依此类推。

my_array = ["apple", "banana", "cherry", "date"]

# 访问最后一个元素 (索引 -1)
last_element = my_array[-1]
puts "最后一个元素: #{last_element}" # 输出: 最后一个元素: date

# 访问倒数第二个元素 (索引 -2)
second_last_element = my_array[-2]
puts "倒数第二个元素: #{second_last_element}" # 输出: 倒数第二个元素: cherry

2.3 数组切片 (Array Slicing)

数组切片允许你提取数组的一部分,并将其作为一个全新的数组返回。你可以指定一个索引范围,或者指定一个“起始索引”加上“长度”。

使用范围 (Range) 进行切片:

my_array = ["apple", "banana", "cherry", "date", "fig"]

# 提取从索引 1 开始,直到(但不包括)索引 4 的元素
slice_1 = my_array[1...4] # 三个点 (...) 表示不包含结束点
puts slice_1.inspect # 输出: ["banana", "cherry", "date"]

# 提取从索引 1 开始,直到(并包括)索引 4 的元素
slice_2 = my_array[1..4] # 两个点 (..) 表示包含结束点
puts slice_2.inspect # 输出: ["banana", "cherry", "date", "fig"]

使用“起始索引”和“长度”进行切片:

my_array = ["apple", "banana", "cherry", "date", "fig"]

# 从索引 1 开始,向后提取 3 个元素
slice_3 = my_array[1, 3]
puts slice_3.inspect # 输出: ["banana", "cherry", "date"]

2.4 越界访问 (Out-of-Bounds Access)

如果你尝试访问数组边界之外的元素,Ruby 会十分温和地返回 nil而绝对不会引发程序崩溃或报错

my_array = ["apple", "banana", "cherry"]

# 访问超出数组大小的索引
out_of_bounds = my_array[5]
puts out_of_bounds.inspect # 输出: nil

# 访问超出数组开头的负数索引
out_of_bounds_negative = my_array[-5]
puts out_of_bounds_negative.inspect # 输出: nil

2.5 使用 first 和 last 方法

为了更语义化地访问首尾元素,Ruby 提供了极其方便的 firstlast 方法。

my_array = ["apple", "banana", "cherry"]

# 访问第一个元素
first_element = my_array.first
puts "第一个元素: #{first_element}" # 输出: 第一个元素: apple

# 访问最后一个元素
last_element = my_array.last
puts "最后一个元素: #{last_element}" # 输出: 最后一个元素: cherry

# 传入数字 n:获取前 n 个元素
first_two = my_array.first(2)
puts "前两个元素: #{first_two}" # 输出: 前两个元素: ["apple", "banana"]

注意:first(n) 方法会返回一个包含前 n 个元素的新数组

3. 修改数组元素

数组是可变 (mutable) 的,这意味着你可以在创建数组后自由地更改它的内容。你可以通过向特定索引赋新值来修改元素。

3.1 通过索引修改元素

my_array = ["apple", "banana", "cherry"]

# 修改索引 1 处的元素
my_array[1] = "grape"
puts my_array.inspect # 输出: ["apple", "grape", "cherry"]

# 修改索引 -1 处(最后一个)的元素
my_array[-1] = "date"
puts my_array.inspect # 输出: ["apple", "grape", "date"]

3.2 在当前大小之外添加元素 (自动扩容)

如果你向一个超出现有数组大小的索引赋值,Ruby 数组会自动增长以容纳这个新元素。中间缺失的空隙索引会自动被填入 nil

my_array = ["apple", "banana", "cherry"]

# 在索引 5 处添加一个元素(当前数组只有索引 0, 1, 2)
my_array[5] = "fig"
puts my_array.inspect # 输出: ["apple", "banana", "cherry", nil, nil, "fig"]

3.3 替换数组切片

你可以直接用新的元素去替换数组中的一个切片(片段)。新元素的数量不需要与被替换切片的大小一致,Ruby 会自动调整数组大小。

my_array = ["apple", "banana", "cherry", "date", "fig"]

# 将索引 1 到 3 的元素替换为新元素
my_array[1..3] = ["grape", "kiwi"]
puts my_array.inspect # 输出: ["apple", "grape", "kiwi", "fig"]

# 将切片替换为空数组,实际上等同于删除了这部分元素
my_array[1..2] = []
puts my_array.inspect # 输出: ["apple", "fig"]