使用 Ruby 的 Enumerable 模块的 4 种鲜为人知的方法
我喜欢 Ruby 的一个重要原因是,它能让我仅用寥寥几个字符或几行代码就完成大量工作,而且还能确保代码易于阅读。这一点在处理数组和哈希表(在 Ruby 中也称为枚举对象)时体现得尤为明显。
任何包含 Ruby 强大的Enumerable模块的对象都可以以各种方式进行迭代、遍历、操作、切片和分解。该模块的灵活性使得复杂任务的代码出乎意料地简洁。
Ruby 2.6于2018 年 12 月发布,它为 Enumerable 引入了一些新方法,并对列表和序列的处理进行了其他改进。在本文中,我将向您展示 Ruby Enumerable 最近新增的一些功能,以及一些常用的旧功能。
无尽的范围
范围是 Ruby 的一项强大功能,它允许你快速创建可迭代的数字或字母范围,例如1..1010 ('A'..'Z')...1..
为什么无限范围有用?一个显而易见的用途是,它可以简洁地生成一个不断增长的整数列表:
(1..).each do |i|
puts i
end
# 1
# 2
# 3
# ...
另一种使用无限范围的独特方法是将其与其他链式调用的方法结合使用。在这种情况下,范围并非真正意义上的无限,而是在满足链式调用方法的条件时结束。让我们来看一个例子:
p (1..).step(5).take(100)
# [1, 6, 11, 16, 21, 26, 31, 36, 41, 46, …. 491, 496]
这里发生了什么?该step(5)方法从无限大的范围内每隔五个数字取一个。然后,该take方法取该序列的前100个元素,此时序列变为有限值。使用无限范围作为表达式的起始点,可以确保后半部分适用于任何输入集。我们可以将5或100替换为更大的数字,仍然能够得到预期的结果。
提示:范围也适用于字母:
p ('A'..'Z').step(2)
# ["A", "C", "E", "G", "I", "K", "M", "O", "Q", "S", "U", "W", "Y"]
关键词lazy
这个功能并非 Ruby 2.6 的新特性,但它功能强大却常常被忽略。枚举类型支持一种惰性迭代机制,可以帮助你避免在不需要的时候将整个文件或数据库记录集读入内存。例如,当你只需要查找文件中包含单词“jane”的前 10 行时。
File.open("names.txt") do |f|
f.each_line.lazy.select { |line|
line.match(/jane/i)
}.first(10)
end
虽然只是多了一种方法,但添加 ` lazy` 关键字改变了底层的执行过程。它不会像不使用 `lazy` 关键字时那样将整个文件读入内存。使用 `lazy` 并将 `lazy`lazy放在表达式末尾,文件会逐行读取,但一旦找到 10 个匹配项,读取就会停止。each_linefirst(10)
范围步进
对范围进行操作step可以生成每隔 2 个、3 个或 n 个元素的子范围。Ruby 2.6 为单步执行操作带来了更强大的功能和更简洁的语法。
首先,步骤新增了一个别名%:
p ((1..10) % 2)
# [1, 3, 5, 7, 9]
接下来,现在有可以在ArithmethicSequence类型的范围的步骤上调用的first方法。last
p ((1..10) % 2).last
# 9
each_cons
当您需要一次性遍历包含多个重叠元素的数组时,` each_consslice` 方法就非常有用。它类似于切片,可以从连续元素生成子数组。但是,下一个数组的第一个元素是前一个数组的第二个元素。通过示例可以更容易地理解这一点。
p (1..10).each_cons(2)
# [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]]
each_cons接受一个整数参数,用于指定每个数组的大小。
因为字符串本质上就是字符数组,所以each_cons可以用来进行一些原本可能很繁琐的字符串处理。这里有一个有趣的例子,可以让你的字符串看起来更加诡异:
str = "arrays and collections are scary"
p str.chars.each_cons(2).map(&:join).join
# "arrrraayyss aanndd ccoolllleeccttiioonnss aarree ssccaarry"
另一种可能更有用的方法each_cons是在遍历集合时保持前一个和后一个元素的上下文关系。
primes = [2, 3, 5, 7, 11, 13]
primes.each_cons(3).each { |previous, current, next_|
p "#{current} is the prime number between #{previous} and #{next_}"
}
# "3 is the prime number between 2 and 5"
# "5 is the prime number between 3 and 7"
# "7 is the prime number between 5 and 11"
# "11 is the prime number between 7 and 13"
这里可以看到 Ruby 的另一个实用特性:参数解构。函数的参数是一个数组each,但我们可以提供三个参数:previous`a` current、`b` 和 `c` next_,让 Ruby 自动将数组的第 0、1 和 2 个元素分别赋值给这三个变量。
结论
我们仅仅触及了 Ruby 枚举功能的冰山一角。希望您已经学到了一些技巧,可以帮助您优化当前或未来的代码库。结合无限范围、单步执行和惰性关键字,可以大大简化一系列循环处理用例。
如果您有兴趣了解更多关于如何提升 Ruby 代码水平的信息,我强烈推荐 Aaron Patterson 的 Code[ish] Podcast,以及这期关于Ruby 正则表达式和开源开发者如何让您的公司更强大的节目。
感谢阅读!
文章来源:https://dev.to/orbit/4-lesser-known-ways-to-use-ruby-s-enumerable-module-3d8b