Enumerable#inject是Ruby核心库中的一个简洁而且强大的API,今天读到一段简洁的代码之后,对这个API产生了浓厚的兴趣,索性搜寻一下资料,总结一下它的用法。
代码如下:
def text_at(*args) args.inject(@feed) { |s, r| s.send(:at, r)}.inner_text end
这段代码完成的功能是:取出XML文件中某子元素的文本内容,它是用nokogiri库来完成这个功能的。关于Nokogiri库API(at(), inner_text())的细节我们不谈,只是用这段代码来引起你对inject的兴趣,现在我们来看inject的用法。
Enumerable#inject是很多Ruby高手喜欢的API,因此将来读Ruby也会经常遇到,即使有的用法不能完全理解,也要先混个脸熟,使用场景见多了就自然理解了。
1. 数字求和
Inject最常见的用法就是这一个了,假定你有一个内容为数字的数组,你可以像下面这样对它求和:
irb(main):001:0> [1, 2, 3, 4, 5].inject(0) { |sum, e| sum + e } => 15
或者像这样:
irb(main):002:0> (1..5).inject(0) { |sum, e| sum + e } => 15
用区间或者数组在这里没有分别,Ruby会自动转换。Inject在这里接受一个方法参数和一个block. 对数组中的每一个元素,block都执行一次。第一次执行block的时候,inject接收的方法参数被作为block的第一个参数,而block的第二个参数则是数组的第一个元素。第二次执行block的时候,情况就有了变化,这也是inject神奇的地方。这时候block的第一个参数则是block上一次执行的返回值(block的最后一个表达式),第二个参数则是数组的第二个元素,后面的三次执行方式与第二次相同,这样我们就计算出了数组元素之和。事实上,上面的代码还可以更简洁一些:
irb(main):006:0> [1, 2, 3, 4, 5].inject { |sum, e| sum + e } => 15
这段代码可以计算出相同结果的原因是:inject的方法参数是可选的,如果不提供的话,Ruby默认将数组的第一个元素作为block第一次执行时候的第一个参数,在这种情况下,block一共需要执行4次,比传入默认参数的形式少执行一次。
2. 转换数据结构
2.1生成Hash:
hash = [[:first_name, 'Shane'], [:last_name, 'Harvie']].inject({}) do |result, element| result[element.first] = element.last result end
当然这种用法也有别的形式,并不一定需要用到inject,比如:
Hash[*[[:first_name, 'Shane'], [:last_name, 'Harvie']].flatten]
可以达到相同目的而代码更简洁。
2.2 过滤数组:
arr = [1, 2, 3, 4, 5, 6].inject([]) do |r, e| r << e.to_s if e % 2 == 0 r end
当然这种用法也有不使用inject的等价方式:
[1, 2, 3, 4, 5, 6].select { |e| e % 2 == 0 }.collect { |e| e.to_s } # => ["2", "4", "6"]
具体用哪一种就是萝卜青菜,各有所爱了。
3. 更高级的用法
先看一段代码:
class Recorder # undefine any instance methods except methods start from __ # and inspect/to_str/object_id instance_methods.each do |meth| undef_method meth unless meth =~ /^(__|inspect|to_str)|object_id/ end # store messages sent to the Recorder object # that's why it's called Recorder def method_missing(sym, *args) messages << [sym, args] self end # getter of instance variable messages def messages @messages ||= [] end end
代码中比较难懂的部分已经加了注释,类Recorder完成的功能是记录所有发送给它对象的消息(Ruby中对象调用通常称作向对象发送消息)。下面我们再打开类Recorder,定义另一方法来展示这些保存的消息:
class Recorder def play_for(obj) messages.inject(obj) do |result, msg| result.send msg.first, *msg.last end end end
在这个方法中,我们用到了inject, 它向传入的对象obj调用保存的消息,并传入之前调用者传入的所有参数。下面的代码展现了它的用法:
recorder = Recorder.new recorder.methods.sort recorder.play_for(String)
它实现了对String对象(你应该可以想起来,Ruby的类也是对象)调用#methods(), 然后对#methods返回结果调用#sort().
其实上面这个Recorder示例和本文开头的那个范例原理相同,前一个调用可以响应第一个消息,返回的结果则分别可以响应接下来的消息,对比这两个示例可以对Enumerable#inject的强大之处有所体会。
参考:http://blog.jayfields.com/2008/03/ruby-inject.html
相关推荐
Enumerable::Statistics 提供了一些方法来计算数组和枚举中的统计汇总。 安装 将此行添加到应用程序的 Gemfile 中: gem 'enumerable-statistics' 然后执行: $ bundle 或者自己安装: $ gem install ...
在Crystal编程语言中,`flatten_as.cr`是一个扩展了Enumerable模块的功能,增加了`#flatten_as`方法。这个方法与已有的`#flatten`方法类似,用于将嵌套的集合(如数组或范围)扁平化为单一层次的结构。但`#flatten_...
Ruby 1.9.0的推出并非完全稳定,Matz,即Ruby的创始人,曾在ruby-core邮件组中提及,此版本包含了所有预期的不兼容性修改,但并未提供直接的移植工具或方法。 Ruby 1.9 的一些主要不兼容性改动包括: 1. **语法...
Ruby中的代理对象可以通过定义委托(delegation)来实现,例如使用`delegate`方法。 13. **职责链模式**:避免将请求的发送者和接收者耦合在一起,让多个对象都有可能处理请求。Ruby中,可以使用对象间的链式调用来...
在Ruby中,一切皆为对象,即使是基本数据类型如整数和字符串。这种特性使得Ruby在处理面向对象编程时非常强大。类和模块是Ruby中的核心概念,它们提供了组织和复用代码的方式。类定义了对象的属性和行为,而模块则...
- Ruby中的块(由`do...end`或`{...}`定义)是代码的可执行部分,可以与方法一起使用。Proc和Lambda是块的两种对象形式,它们可以保存并稍后执行。理解它们的差异,如 Proc的`call`和Lambda的`[]`调用方式,以及对...
基本的可数方法 ...2-通过在终端上使用$ ruby enumerables.rb执行文件。 作者 :laptop:Vanessa青木 GitHub: 推特: Linkedin: :handshake: 贡献 欢迎提供文稿,问题和功能要求! 随时检查。
"node-enumerable" 将这一概念引入JavaScript,使开发者能够使用链式方法对数组和对象进行复杂的查询和操作,提高了代码的可读性和简洁性。 3. **ES6支持** 这个库完全适配ES6标准,这意味着你可以使用箭头函数、...
此外,Enumerable模块是一组用于遍历和操作集合的强大工具,它包含的`inject`和`reduce`方法可以执行聚合操作,`group_by`则可以按条件对元素分组。 Ruby的模块(Module)和类(Class)系统使得代码组织和复用变得...
Ruby中有很多高效的编程习惯和技巧,例如使用块(blocks)、Proc对象和Lambda表达式来替代循环结构。`fast-ruby` 项目提供了这些方面的比较,展示了如何通过改变编程习惯来提高代码执行速度。 2. **方法调用优化**...
特殊方法是指那些在Ruby中具有特殊含义的方法,例如`initialize`、`==`等。 **3.5 类变量与类方法** 类变量是以`@@`开头的变量,可以在类的所有实例之间共享。类方法则是定义在类本身上的方法。 ```ruby class ...
方法是Ruby中定义行为的基本方式,可以使用`def`关键字定义。Ruby的方法支持默认参数、可变参数和关键字参数。`send`方法可以动态调用对象的方法。 5. **异常处理** Ruby使用`begin..rescue..else..ensure`结构...
这部分内容未给出详细章节,但可以从上下文推断,这部分可能涉及Ruby中的序列化技术以及YAML格式的使用。 以上内容覆盖了Ruby程序设计的核心知识点,从语言基础到高级特性均有涉及。通过这些知识点的学习,可以全面...
1. 面向对象:在Ruby中,一切都是对象,包括基本的数据类型如整数、字符串和布尔值。这意味着你可以对任何对象进行方法调用,增强了代码的灵活性。 2. 动态性:Ruby允许在运行时修改类和对象,这使得它非常适合快速...
该项目展示了Ruby Enumerable模块中方法的重建列表。 重写的Enumerable方法是: 每个-> my_each each_with_index-> my_each_with_index 选择-> my_select 全部? -> my_all? 任何? -> my_any? 没有...
- **使用Ruby**:直接在命令行中输入`ruby filename.rb`来运行脚本文件。 - **使用IDE**:推荐使用FreeRIDE或SciTE等集成开发环境,提供更好的编辑体验。 **2.3 Ruby-irb** IRB(Interactive Ruby Shell)是一个...