Ruby集合类与数组操作全解析
1. Ruby集合类概述
在Ruby编程中,集合类是用于表示一组值的数据结构。关键的集合类包括数组(Array)、哈希(Hash),标准库还提供了集合(Set)类。这些集合类都混入了
Enumerable
模块,这意味着
Enumerable
模块中的方法是通用的集合操作方法。
1.1 Enumerable对象
Enumerable
模块是一个混合模块,它在
each
迭代器的基础上实现了许多有用的方法。数组、哈希和集合类都包含了
Enumerable
模块,因此实现了该模块中的所有方法。此外,范围(Range)和输入输出对象(IO)也是值得注意的可枚举类。
部分可枚举类有自然的枚举顺序,例如:
- 数组按数组索引递增的顺序枚举元素。
- 范围按升序枚举。
- IO对象按从底层文件或套接字读取的顺序枚举文本行。
- 在Ruby 1.9中,哈希和基于哈希的集合按元素插入的顺序枚举;而在Ruby 1.9之前,这些类的元素枚举顺序基本是任意的。
许多
Enumerable
方法会返回可枚举集合的处理版本或其元素的选定子集合。通常,如果
Enumerable
方法返回一个集合(而不是从集合中选择的单个值),该集合通常是一个数组,但也有例外,例如哈希类重写了
reject
方法,使其返回一个哈希对象而不是数组。无论返回值具体是什么,
Enumerable
方法返回的集合本身一定是可枚举的。
1.2 集合的迭代与转换
1.2.1 基本迭代方法
任何
Enumerable
对象都必须有
each
迭代器。
Enumerable
还提供了
each_with_index
方法,它会产生集合的一个元素和一个整数。对于数组,这个整数是数组索引;对于IO对象,这个整数是行号(从0开始);对于其他对象,这个整数是如果集合转换为数组时的数组索引。示例如下:
(5..7).each {|x| print x } # 输出 "567"
(5..7).each_with_index {|x,i| print x,i } # 输出 "506172"
1.2.2 循环迭代方法
在Ruby 1.9中,
Enumerable
定义了
cycle
方法,它会反复迭代集合的元素,直到你提供的块通过
break
、
return
或抛出异常显式终止迭代。在第一次遍历
Enumerable
对象时,
cycle
会将元素保存到一个数组中,然后从该数组中进行后续迭代。这意味着在第一次遍历集合后,对该集合的修改不会影响
cycle
的行为。
1.2.3 子数组迭代方法
each_slice
和
each_cons
是产生集合子数组的迭代器。在Ruby 1.8中,需要
require 'enumerator'
才能使用,在Ruby 1.9中它们是核心库的一部分。
each_slice(n)
以大小为
n
的“切片”迭代可枚举值,示例如下:
(1..10).each_slice(4) {|x| print x } # 输出 "[1,2,3,4][5,6,7,8][9,10]"
each_cons
与
each_slice
类似,但它在可枚举集合上使用“滑动窗口”,示例如下:
(1..5).each_cons(3) {|x| print x } # 输出 "[1,2,3][2,3,4][3,4,5]"
1.2.4 元素处理与映射方法
collect
方法将一个块应用于集合的每个元素,并将块的返回值收集到一个新数组中。
map
是
collect
的同义词,它通过将块应用于每个元素,将集合的元素映射到数组的元素。示例如下:
data = [1,2,3,4] # 一个可枚举集合
roots = data.collect {|x| Math.sqrt(x)} # 收集数据的平方根
words = %w[hello world] # 另一个集合
upper = words.map {|x| x.upcase } # 映射为大写
1.2.5 元素交错方法
zip
方法将一个可枚举集合的元素与零个或多个其他集合的元素交错,并将一个元素数组(每个集合一个元素)传递给关联的块。如果没有提供块,在Ruby 1.8中返回一个数组的数组,在Ruby 1.9中返回一个枚举器对象(对枚举器对象调用
to_a
会生成在1.8中会返回的数组的数组)。示例如下:
(1..3).zip([4,5,6]) {|x| print x.inspect } # 输出 "[1,4][2,5][3,6]"
(1..3).zip([4,5,6],[7,8]) {|x| print x} # 输出 "14725836"
(1..3).zip('a'..'c') {|x,y| print x,y } # 输出 "1a2b3c"
1.2.6 转换为数组和集合方法
Enumerable
定义了
to_a
方法(同义词为
entries
),用于将任何可枚举集合转换为数组。如果需要使用
to_set
方法将可枚举对象转换为集合,需要
require 'set'
。示例如下:
(1..3).to_a # => [1,2,3]
(1..3).entries # => [1,2,3]
require 'set'
(1..3).to_set # => #<Set: {1, 2, 3}>
1.3 枚举器与外部迭代器
枚举器是
Enumerable::Enumerator
类的实例,尽管它是一个强大的迭代构造,但方法数量却出奇地少。枚举器主要是Ruby 1.9的特性,但在Ruby 1.8中通过引入
enumerator
库也可以使用部分枚举器功能。可以使用
to_enum
或其别名
enum_for
创建枚举器,也可以直接调用迭代器方法而不提供预期的块来创建。示例如下:
e = [1..10].to_enum # 使用 Range.each
e = "test".enum_for(:each_byte) # 使用 String.each_byte
e = "test".each_byte # 使用 String.each_byte
枚举器对象是具有
each
方法的
Enumerable
对象,该方法基于其他对象的某个迭代器方法。除了作为
Enumerable
代理对象外,枚举器还可以作为外部迭代器使用。要使用外部迭代器获取集合的元素,只需反复调用
next
方法,直到它抛出
StopIteration
异常。
Kernel.loop
迭代器会为你捕获
StopIteration
异常。如果底层迭代器方法允许重复迭代,在
StopIteration
异常抛出后,后续调用通常会开始新的迭代;如果可能进行重复迭代,也可以在
StopIteration
异常抛出前调用
rewind
方法强制重启外部迭代器。示例如下:
"Ruby".each_char.max # => "y"; Enumerable方法在枚举器上的使用
iter = "Ruby".each_char # 创建一个枚举器
loop { print iter.next } # 输出 "Ruby"; 将其作为外部迭代器使用
print iter.next # 输出 "R": 迭代器自动重启
iter.rewind # 强制现在重启
print iter.next # 再次输出 "R"
可以通过
with_index
方法从任何枚举器获取一个新的枚举器,新枚举器会产生一个索引(或迭代编号)以及原始迭代器会产生的值。示例如下:
# 输出 "0:R\n1:u\n2:b\n3:y\n"
"Ruby".each_char.with_index.each {|c,i| puts "#{i}:#{c}" }
Enumerable::Enumerator
类定义了
to_splat
方法,这意味着可以在枚举器前加上星号将其“展开”为单个值,用于方法调用或并行赋值。
1.4 集合的排序
Enumerable
中最重要的方法之一是
sort
,它将可枚举集合转换为数组并对该数组的元素进行排序。默认情况下,排序是根据元素的
<=>
方法进行的。如果提供了一个块,则会将元素对传递给该块,块应返回 -1、0 或 +1 来表示它们的相对顺序。示例如下:
w = Set['apple','Beet','carrot'] # 要排序的单词集合
w.sort # ['Beet','apple','carrot']: 按字母顺序
w.sort {|a,b| b<=>a } # ['carrot','apple','Beet']: 逆序
w.sort {|a,b| a.casecmp(b) } # ['apple','Beet','carrot']: 忽略大小写
w.sort {|a,b| b.size<=>a.size} # ['carrot','apple','Beet']: 按长度逆序
如果与
sort
关联的块在进行比较时需要进行大量计算,使用
sort_by
会更高效。与
sort_by
关联的块会为集合中的每个元素调用一次,并应为该元素返回一个数字“排序键”,然后集合将按排序键的升序进行排序。这样,每个元素的排序键只计算一次,而不是每次比较计算两次。示例如下:
# 不区分大小写的排序
words = ['carrot', 'Beet', 'apple']
words.sort_by {|x| x.downcase} # => ['apple', 'Beet', 'carrot']
1.5 集合的搜索
Enumerable
定义了几种在集合中搜索单个元素的方法。
-
include?
及其同义词
member?
用于搜索与参数相等(使用
==
)的元素。示例如下:
primes = Set[2, 3, 5, 7]
primes.include? 2 # => true
primes.member? 1 # => false
-
find方法及其同义词detect会依次将关联的块应用于集合的每个元素。如果块返回除false或nil以外的任何值,find会返回该元素并停止迭代;如果块始终返回nil或false,则find返回nil。示例如下:
# 查找包含数字 1 的第一个子数组
data = [[1,2], [0,1], [7,8]]
data.find {|x| x.include? 1} # => [1,2]
data.detect {|x| x.include? 3} # => nil: 没有这样的元素
-
在Ruby 1.9中,
find_index方法的工作方式与find类似,但返回匹配元素的索引而不是元素本身。如果没有找到匹配项,它也返回nil。需要注意的是,find_index的返回值对于不使用数字索引的集合(如哈希和集合)不太有用。示例如下:
data.find_index {|x| x.include? 1} # => 0: 第一个元素匹配
data.find_index {|x| x.include? 3} # => nil: 没有这样的元素
1.6 子集合的选择
1.6.1 基本选择方法
-
select方法选择并返回集合中块返回非nil、非false值的元素。其同义词find_all的工作方式与find方法类似,但返回所有匹配元素的数组。示例如下:
(1..8).select {|x| x%2==0} # => [2,4,6,8]: 选择偶数元素
(1..8).find_all {|x| x%2==1} # => [1,3,5,7]: 查找所有奇数元素
-
reject方法与select相反,返回的数组中的元素是块返回false或nil的元素。示例如下:
primes = [2,3,5,7]
primes.reject {|x| x%2==0} # => [3,5,7]: 排除偶数元素
-
partition方法用于同时选择和排除集合中的元素,它返回一个包含两个数组的数组。第一个子数组包含块为true的元素,第二个子数组包含块为false的元素。示例如下:
(1..8).partition {|x| x%2==0} # => [[2, 4, 6, 8], [1, 3, 5, 7]]
1.6.2 分组方法
Ruby 1.9中的
group_by
方法是
partition
方法的泛化。它不将块视为谓词并返回两个组,而是将块的返回值用作哈希键,将该键映射到块返回该值的所有集合元素的数组。示例如下:
# 按编程语言的首字母分组
langs = %w[ java perl python ruby ]
groups = langs.group_by {|lang| lang[0] }
groups # => {"j"=>["java"], "p"=>["perl", "python"], "r"=>["ruby"]}
1.6.3 匹配筛选方法
grep
方法返回与参数值匹配的元素数组,使用参数的
===
运算符确定匹配。当与正则表达式参数一起使用时,该方法的工作方式类似于Unix命令行实用程序
grep
。如果调用时关联了一个块,该块将用于处理匹配的元素,就像对
grep
的结果调用
collect
或
map
一样。示例如下:
langs = %w[ java perl python ruby ]
langs.grep(/^p/) # => ["perl", "python"]: 以 'p' 开头
langs.grep(/^p/) {|x| x.capitalize} # => ["Perl", "Python"]: 修正大小写
data = [1, 17, 3.0, 4]
ints = data.grep(Integer) # => [1, 17, 4]: 仅整数
small = ints.grep(0..9) # [1,4]: 仅在范围内
1.6.4 Ruby 1.9新增的选择方法
在Ruby 1.9中,之前描述的选择方法得到了扩展,新增了
first
、
take
、
drop
、
take_while
和
drop_while
方法。
-
first
方法返回
Enumerable
对象的第一个元素,如果提供一个整数参数
n
,则返回包含前
n
个元素的数组。
-
take
和
drop
方法期望一个整数参数。
take
的行为与
first
类似,返回
Enumerable
接收对象的前
n
个元素的数组;
drop
则相反,返回
Enumerable
中除前
n
个元素之外的所有元素的数组。示例如下:
p (1..5).first(2) # => [1,2]
p (1..5).take(3) # => [1,2,3]
p (1..5).drop(3) # => [4,5]
-
take_while和drop_while方法期望一个块而不是整数参数。take_while会依次将Enumerable对象的元素传递给块,直到块第一次返回false或nil,然后返回块返回true的前一个元素的数组;drop_while也会依次将元素传递给块,直到块第一次返回false或nil,然后返回块返回false的元素以及所有后续元素的数组。示例如下:
[1,2,3,nil,4].take_while {|x| x } # => [1,2,3]: 取到 nil 之前
[nil, 1, 2].drop_while {|x| !x } # => [1,2]: 丢弃前导 nil
1.7 集合的缩减
有时候我们希望将可枚举集合缩减为一个捕获集合某些属性的单个值。
1.7.1 最值方法
min
和
max
方法用于执行缩减操作,返回集合中最小或最大的元素(假设元素可以使用
<=>
方法相互比较)。示例如下:
[10, 100, 1].min # => 1
['a','c','b'].max # => 'c'
[10, 'a', []].min # => ArgumentError: 元素不可比较
min
和
max
方法可以像
sort
方法一样接受一个块来比较两个元素。在Ruby 1.9中,使用
min_by
和
max_by
方法会更方便。示例如下:
langs = %w[java perl python ruby] # 哪个名称最长?
langs.max {|a,b| a.size <=> b.size } # => "python": 使用块比较
langs.max_by {|word| word.length } # => "python": 仅 Ruby 1.9 可用
Ruby 1.9还定义了
minmax
和
minmax_by
方法,它们计算集合的最小值和最大值,并将它们作为一个包含两个元素的数组
[min,max]
返回。示例如下:
(1..100).minmax # => [1,100] 以数字形式表示的最小值和最大值
(1..100).minmax_by {|n| n.to_s } # => [1,99] 以字符串形式表示的最小值和最大值
1.7.2 谓词方法
any?
和
all?
是也执行缩减操作的谓词方法。它们将谓词块应用于集合的元素。
all?
方法在谓词对于集合的所有元素都为
true
(即非
nil
且非
false
)时返回
true
;
any?
方法在谓词对于集合中的任何一个元素为
true
时返回
true
。在Ruby 1.9中,
none?
方法仅在谓词从未返回
true
值时返回
true
;
one?
方法仅在谓词对于集合中的一个且仅一个元素返回
true
值时返回
true
。如果不提供块,这些方法将直接测试集合的元素本身。示例如下:
c = -2..2
c.all? {|x| x>0} # => false: 并非所有值都大于 0
c.any? {|x| x>0} # => true: 有些值大于 0
c.none? {|x| x>2} # => true: 没有值大于 2
c.one? {|x| x>0} # => false: 不止一个值大于 0
c.one? {|x| x>2} # => false: 没有值大于 2
c.one? {|x| x==2} # => true: 有一个值等于 2
[1, 2, 3].all? # => true: 没有值为 nil 或 false
[nil, false].any? # => false: 没有 true 值
[].none? # => true: 没有非 false、非 nil 值
1.7.3 计数方法
Ruby 1.9新增的
count
方法返回集合中等于指定值的元素数量,或关联块返回
true
的元素数量。示例如下:
a = [1,1,2,3,5,8]
a.count(1) # => 2: 两个元素等于 1
a.count {|x| x % 2 == 1} # => 4: 四个元素是奇数
1.7.4 通用缩减方法
inject
是一个通用的集合缩减方法,Ruby 1.9将
reduce
定义为
inject
的别名。与
inject
调用关联的块期望两个参数,第一个是累加值,第二个是集合中的一个元素。第一次迭代的累加值是传递给
inject
的参数,一次迭代的块返回值成为下一次迭代的累加值,最后一次迭代后的返回值成为
inject
的返回值。示例如下:
# 有多少个负数?
(-2..10).inject(0) {|num, x| x<0 ? num+1 : num } # => 2
# 单词长度之和
%w[pea queue are].inject(0) {|total, word| total + word.length } # => 11
如果不向
inject
传递参数,块第一次被调用时会传递集合的前两个元素(或者,如果集合中只有一个元素,
inject
直接返回该元素)。这种形式的
inject
对于许多常见操作很有用。示例如下:
sum = (1..5).inject {|total,x| total + x} # => 15
prod = (1..5).inject {|total,x| total * x} # => 120
max = [1,3,2].inject {|m,x| m>x ? m : x} # => 3
[1].inject {|total,x| total + x} # => 1: 块从未被调用
在Ruby 1.9中,可以向
inject
传递一个表示方法(或运算符)的符号,而不是指定一个块。集合中的每个元素将被传递给累加值的指定方法,其结果将成为新的累加值。通常在以这种方式使用符号调用该方法时,会使用
reduce
别名。示例如下:
sum = (1..5).reduce(:+) # => 15
prod = (1..5).reduce(:*) # => 120
letters = ('a'..'e').reduce("-", :concat) # => "-abcde"
1.8 数组操作
1.8.1 数组的创建
数组可以使用数组字面量、类方法
Array.new
或类运算符
Array.[]
创建。示例如下:
[1,2,3] # 基本数组字面量
[] # 空数组
[] # 数组是可变的:这个空数组与其他空数组不同
%w[a b c] # => ['a', 'b', 'c']: 单词数组
Array[1,2,3] # => [1,2,3]: 与数组字面量相同
使用
new()
方法创建数组的示例如下:
empty = Array.new # []: 返回一个新的空数组
nils = Array.new(3) # [nil, nil, nil]: 三个 nil 元素
copy = Array.new(nils) # 复制现有数组
zeros = Array.new(4, 0) # [0, 0, 0, 0]: 四个 0 元素
count = Array.new(3){|i| i+1} # [1,2,3]: 由块计算的三个元素
需要注意的是,在使用重复对象创建数组时要小心。示例如下:
a=Array.new(3,'a') # => ['a','a','a']: 三个对同一个字符串的引用
a[0].upcase! # 将数组的第一个元素大写
a # => ['A','A','A']: 它们都是同一个字符串!
a=Array.new(3){'b'} # => ['b','b','b']: 三个不同的字符串对象
a[0].upcase!; # 将第一个元素大写
a # => ['B','b','b']: 其他元素仍然是小写
除了数组工厂方法外,许多其他类也定义了
to_a
方法来返回数组。特别是,任何
Enumerable
对象(如范围或哈希)都可以使用
to_a
方法转换为数组。此外,数组运算符(如
+
)和许多数组方法(如
slice
)会创建并返回新数组,而不是直接修改接收数组。
1.8.2 数组的大小和元素访问
以下代码展示了如何确定数组的长度,以及从数组中提取元素和子数组的各种方法:
# 数组长度
[1,2,3].length # => 3
[].size # => 0: length 的同义词
[].empty? # => true
[nil].empty? # => false
[1,2,nil].nitems # => 2: 非 nil 元素的数量
[1,2,3].nitems {|x| x>2} # => 1: 匹配块的元素数量 (Ruby 1.9)
# 索引单个元素
a = %w[a b c d] # => ['a', 'b', 'c', 'd']
a[0] # => 'a': 第一个元素
a[-1] # => 'd': 最后一个元素
a[a.size-1] # => 'd': 最后一个元素
a[-a.size-1] # => 'a': 第一个元素
a[5] # => nil: 没有这样的元素
a[-5] # => nil: 没有这样的元素
a.at(2) # => 'c': 与 [] 对于单个整数参数的作用相同
a.fetch(1) # => 'b': 也与 [] 和 at 类似
a.fetch(-1) # => 'd': 支持负参数
a.fetch(5) # => IndexError!: 不允许越界
a.fetch(-5) # => IndexError!: 不允许越界
a.fetch(5, 0) # => 0: 越界时返回第二个参数
a.fetch(5){|x|x*x} # => 25: 越界时计算值
a.first # => 'a': 第一个元素
a.last # => 'd': 最后一个元素
a.choice # Ruby 1.9: 随机返回一个元素
总结
本文详细介绍了Ruby中的集合类,包括
Enumerable
对象的各种操作方法,如集合的迭代与转换、排序、搜索、子集合选择和缩减等,还介绍了数组的创建、大小和元素访问方法。掌握这些知识可以帮助开发者更高效地处理和操作数据集合,提高Ruby编程的效率和质量。
参考表格
| 方法 | 描述 | 示例 |
|---|---|---|
each
| 基本迭代器 |
(5..7).each {|x| print x }
|
each_with_index
| 带索引的迭代器 |
(5..7).each_with_index {|x,i| print x,i }
|
cycle
| 重复迭代 |
(1..3).cycle {|x| print x; break if x == 3 }
|
each_slice
| 按指定大小切片迭代 |
(1..10).each_slice(4) {|x| print x }
|
each_cons
| 滑动窗口迭代 |
(1..5).each_cons(3) {|x| print x }
|
collect/map
| 元素处理与映射 |
data.collect {|x| Math.sqrt(x)}
|
zip
| 元素交错 |
(1..3).zip([4,5,6]) {|x| print x.inspect }
|
to_a/entries
| 转换为数组 |
(1..3).to_a
|
to_set
| 转换为集合 |
require 'set'; (1..3).to_set
|
sort
| 排序 |
w.sort {|a,b| b<=>a }
|
sort_by
| 按排序键排序 |
words.sort_by {|x| x.downcase}
|
include?/member?
| 搜索元素 |
primes.include? 2
|
find/detect
| 查找元素 |
data.find {|x| x.include? 1}
|
find_index
| 查找元素索引 |
data.find_index {|x| x.include? 1}
|
select/find_all
| 选择元素 |
(1..8).select {|x| x%2==0}
|
reject
| 排除元素 |
primes.reject {|x| x%2==0}
|
partition
| 分区选择 |
(1..8).partition {|x| x%2==0}
|
group_by
| 分组 |
langs.group_by {|lang| lang[0] }
|
grep
| 匹配筛选 |
langs.grep(/^p/)
|
min/max
| 最值 |
[10, 100, 1].min
|
min_by/max_by
| 按键求最值 |
langs.max_by {|word| word.length }
|
minmax/minmax_by
| 同时求最值 |
(1..100).minmax
|
any?/all?/none?/one?
| 谓词判断 |
c.all? {|x| x>0}
|
count
| 计数 |
a.count(1)
|
inject/reduce
| 通用缩减 |
(1..5).reduce(:+)
|
流程图
graph TD;
A[创建数组] --> B[使用数组字面量];
A --> C[使用 Array.new];
A --> D[使用 Array.[]];
B --> E[基本数组];
B --> F[空数组];
C --> G[空数组];
C --> H[指定长度和初始值];
C --> I[由块计算元素];
D --> J[类似数组字面量];
以上就是关于Ruby集合类和数组操作的详细介绍,希望对你有所帮助。在实际编程中,可以根据具体需求选择合适的方法来处理数据集合。
1.8.3 数组元素的修改
数组是可变的数据结构,可以对其元素进行修改。以下是一些常见的修改数组元素的方法:
# 修改单个元素
a = %w[a b c d]
a[0] = 'A' # 将第一个元素修改为 'A'
a # => ['A', 'b', 'c', 'd']
# 修改多个元素
a[1, 2] = ['B', 'C'] # 从索引 1 开始,替换 2 个元素
a # => ['A', 'B', 'C', 'd']
# 插入元素
a.insert(2, 'X') # 在索引 2 处插入元素 'X'
a # => ['A', 'B', 'X', 'C', 'd']
# 删除元素
a.delete_at(3) # 删除索引 3 处的元素
a # => ['A', 'B', 'X', 'd']
a.delete('X') # 删除值为 'X' 的元素
a # => ['A', 'B', 'd']
1.8.4 数组的拼接与合并
可以使用
+
运算符和
concat
方法来拼接和合并数组。
# 使用 + 运算符拼接数组
a = [1, 2, 3]
b = [4, 5, 6]
c = a + b
c # => [1, 2, 3, 4, 5, 6]
# 使用 concat 方法合并数组
a.concat(b)
a # => [1, 2, 3, 4, 5, 6]
1.8.5 数组的反转与排序
数组可以进行反转和排序操作。
# 反转数组
a = [1, 2, 3]
a.reverse # => [3, 2, 1]
a # 原数组不变
a.reverse! # 原地反转数组
a # => [3, 2, 1]
# 数组排序
a = [3, 1, 2]
a.sort # => [1, 2, 3]
a # 原数组不变
a.sort! # 原地排序数组
a # => [1, 2, 3]
1.9 哈希操作
哈希(Hash)是 Ruby 中另一种重要的集合类,它使用键值对来存储数据。
1.9.1 哈希的创建
可以使用哈希字面量或
Hash.new
方法来创建哈希。
# 哈希字面量
h = { 'apple' => 1, 'banana' => 2, 'cherry' => 3 }
h # => {"apple"=>1, "banana"=>2, "cherry"=>3}
# 使用 Hash.new
h = Hash.new
h['apple'] = 1
h['banana'] = 2
h # => {"apple"=>1, "banana"=>2}
# 创建带有默认值的哈希
h = Hash.new(0)
h['apple'] # => 0
1.9.2 哈希元素的访问与修改
可以通过键来访问和修改哈希中的元素。
h = { 'apple' => 1, 'banana' => 2, 'cherry' => 3 }
# 访问元素
h['apple'] # => 1
# 修改元素
h['apple'] = 5
h # => {"apple"=>5, "banana"=>2, "cherry"=>3}
# 检查键是否存在
h.key?('apple') # => true
h.key?('grape') # => false
1.9.3 哈希的遍历
可以使用
each
方法来遍历哈希。
h = { 'apple' => 1, 'banana' => 2, 'cherry' => 3 }
h.each do |key, value|
puts "#{key}: #{value}"
end
# 输出:
# apple: 1
# banana: 2
# cherry: 3
1.9.4 哈希的合并
可以使用
merge
和
merge!
方法来合并哈希。
h1 = { 'apple' => 1, 'banana' => 2 }
h2 = { 'banana' => 3, 'cherry' => 4 }
# 使用 merge 方法,返回新的哈希
h3 = h1.merge(h2)
h3 # => {"apple"=>1, "banana"=>3, "cherry"=>4}
h1 # 原哈希不变
# 使用 merge! 方法,原地合并
h1.merge!(h2)
h1 # => {"apple"=>1, "banana"=>3, "cherry"=>4}
1.10 集合操作
集合(Set)是 Ruby 标准库中的一个类,它存储唯一的元素。
1.10.1 集合的创建
要使用集合,需要先引入
set
库,然后可以使用
Set.new
方法创建集合。
require 'set'
s = Set.new([1, 2, 3])
s # => #<Set: {1, 2, 3}>
1.10.2 集合元素的添加与删除
可以使用
add
和
delete
方法来添加和删除集合中的元素。
s = Set.new([1, 2, 3])
s.add(4) # 添加元素 4
s # => #<Set: {1, 2, 3, 4}>
s.delete(2) # 删除元素 2
s # => #<Set: {1, 3, 4}>
1.10.3 集合的运算
集合支持并集、交集和差集等运算。
s1 = Set.new([1, 2, 3])
s2 = Set.new([3, 4, 5])
# 并集
s3 = s1 | s2
s3 # => #<Set: {1, 2, 3, 4, 5}>
# 交集
s4 = s1 & s2
s4 # => #<Set: {3}>
# 差集
s5 = s1 - s2
s5 # => #<Set: {1, 2}>
总结
本文全面介绍了 Ruby 中集合类的相关知识,包括数组、哈希和集合的创建、操作和使用方法。具体总结如下:
1.
数组
:是最常用的数据结构,可通过多种方式创建,能方便地访问、修改和操作元素,支持排序、反转等操作。
2.
哈希
:使用键值对存储数据,可高效地通过键访问值,支持合并、遍历等操作。
3.
集合
:存储唯一元素,支持并集、交集、差集等集合运算。
掌握这些集合类的使用,能让开发者在 Ruby 编程中更高效地处理和管理数据。
参考表格
| 操作对象 | 操作类型 | 方法 | 示例 |
|---|---|---|---|
| 数组 | 创建 | 数组字面量 |
[1, 2, 3]
|
| 数组 | 创建 |
Array.new
|
Array.new(3, 0)
|
| 数组 | 创建 |
Array.[]
|
Array[1, 2, 3]
|
| 数组 | 访问 |
[]
|
a[0]
|
| 数组 | 访问 |
at
|
a.at(2)
|
| 数组 | 访问 |
fetch
|
a.fetch(1)
|
| 数组 | 修改 |
[]=
|
a[0] = 'A'
|
| 数组 | 修改 |
insert
|
a.insert(2, 'X')
|
| 数组 | 修改 |
delete_at
|
a.delete_at(3)
|
| 数组 | 拼接 |
+
|
a + b
|
| 数组 | 拼接 |
concat
|
a.concat(b)
|
| 数组 | 反转 |
reverse
|
a.reverse
|
| 数组 | 反转 |
reverse!
|
a.reverse!
|
| 数组 | 排序 |
sort
|
a.sort
|
| 数组 | 排序 |
sort!
|
a.sort!
|
| 哈希 | 创建 | 哈希字面量 |
{ 'apple' => 1 }
|
| 哈希 | 创建 |
Hash.new
|
Hash.new(0)
|
| 哈希 | 访问 |
[]
|
h['apple']
|
| 哈希 | 修改 |
[]=
|
h['apple'] = 5
|
| 哈希 | 遍历 |
each
|
h.each {|k, v| puts "#{k}: #{v}" }
|
| 哈希 | 合并 |
merge
|
h1.merge(h2)
|
| 哈希 | 合并 |
merge!
|
h1.merge!(h2)
|
| 集合 | 创建 |
Set.new
|
Set.new([1, 2, 3])
|
| 集合 | 添加 |
add
|
s.add(4)
|
| 集合 | 删除 |
delete
|
s.delete(2)
|
| 集合 | 运算 | 并集 |
s1 | s2
|
| 集合 | 运算 | 交集 |
s1 & s2
|
| 集合 | 运算 | 差集 |
s1 - s2
|
流程图
graph TD;
A[集合操作] --> B[数组操作];
A --> C[哈希操作];
A --> D[集合操作];
B --> B1[创建数组];
B --> B2[访问元素];
B --> B3[修改元素];
B --> B4[拼接数组];
B --> B5[反转排序];
C --> C1[创建哈希];
C --> C2[访问元素];
C --> C3[修改元素];
C --> C4[遍历哈希];
C --> C5[合并哈希];
D --> D1[创建集合];
D --> D2[添加元素];
D --> D3[删除元素];
D --> D4[集合运算];
在实际的 Ruby 编程中,开发者可以根据具体的需求,灵活运用这些集合类和操作方法,以实现高效、简洁的代码。希望这些知识能帮助大家更好地掌握 Ruby 编程中的数据处理。
超级会员免费看
7

被折叠的 条评论
为什么被折叠?



