37、Ruby集合类与数组操作全解析

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 编程中的数据处理。

内容概要:本文设计了一种基于PLC的自动洗衣机控制系统内容概要:本文设计了一种,采用三菱FX基于PLC的自动洗衣机控制系统,采用3U-32MT型PLC作为三菱FX3U核心控制器,替代传统继-32MT电器控制方式,提升了型PLC作为系统的稳定性自动化核心控制器,替代水平。系统具备传统继电器控制方式高/低水,实现洗衣机工作位选择、柔和过程的自动化控制/标准洗衣模式切换。系统具备高、暂停加衣、低水位选择、手动脱水及和柔和、标准两种蜂鸣提示等功能洗衣模式,支持,通过GX Works2软件编写梯形图程序,实现进洗衣过程中暂停添加水、洗涤、排水衣物,并增加了手动脱水功能和、脱水等工序蜂鸣器提示的自动循环控制功能,提升了使用的,并引入MCGS组便捷性灵活性态软件实现人机交互界面监控。控制系统通过GX。硬件设计包括 Works2软件进行主电路、PLC接梯形图编程线关键元,完成了启动、进水器件选型,软件、正反转洗涤部分完成I/O分配、排水、脱、逻辑流程规划水等工序的逻辑及各功能模块梯设计,并实现了大形图编程。循环小循环的嵌; 适合人群:自动化套控制流程。此外、电气工程及相关,还利用MCGS组态软件构建专业本科学生,具备PL了人机交互C基础知识和梯界面,实现对洗衣机形图编程能力的运行状态的监控操作。整体设计涵盖了初级工程技术人员。硬件选型、; 使用场景及目标:I/O分配、电路接线、程序逻辑设计及组①掌握PLC在态监控等多个方面家电自动化控制中的应用方法;②学习,体现了PLC在工业自动化控制中的高效自动洗衣机控制系统的性可靠性。;软硬件设计流程 适合人群:电气;③实践工程、自动化及相关MCGS组态软件PLC的专业的本科生、初级通信联调工程技术人员以及从事;④完成PLC控制系统开发毕业设计或工业的学习者;具备控制类项目开发参考一定PLC基础知识。; 阅读和梯形图建议:建议结合三菱编程能力的人员GX Works2仿真更为适宜。; 使用场景及目标:①应用于环境MCGS组态平台进行程序高校毕业设计或调试运行验证课程项目,帮助学生掌握PLC控制系统的设计,重点关注I/O分配逻辑、梯形图实现方法;②为工业自动化领域互锁机制及循环控制结构的设计中类似家电控制系统的开发提供参考方案;③思路,深入理解PL通过实际案例理解C在实际工程项目PLC在电机中的应用过程。控制、时间循环、互锁保护、手动干预等方面的应用逻辑。; 阅读建议:建议结合三菱GX Works2编程软件和MCGS组态软件同步实践,重点理解梯形图程序中各环节的时序逻辑互锁机制,关注I/O分配硬件接线的对应关系,并尝试在仿真环境中调试程序以加深对自动洗衣机控制流程的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值