`
sunwinner
  • 浏览: 202488 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Ruby中Enumerable#inject用法示范

    博客分类:
  • Ruby
阅读更多

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

    Enumerable::Statistics 提供了一些方法来计算数组和枚举中的统计汇总。 安装 将此行添加到应用程序的 Gemfile 中: gem 'enumerable-statistics' 然后执行: $ bundle 或者自己安装: $ gem install ...

    flatten_as.cr:添加类似于枚举的Enumerable#flatten_as,并具有对所扁平化内容的编译时间控制

    在Crystal编程语言中,`flatten_as.cr`是一个扩展了Enumerable模块的功能,增加了`#flatten_as`方法。这个方法与已有的`#flatten`方法类似,用于将嵌套的集合(如数组或范围)扁平化为单一层次的结构。但`#flatten_...

    为何Ruby 1.9的不兼容性会导致原有Ruby代码无法工作

    Ruby 1.9.0的推出并非完全稳定,Matz,即Ruby的创始人,曾在ruby-core邮件组中提及,此版本包含了所有预期的不兼容性修改,但并未提供直接的移植工具或方法。 Ruby 1.9 的一些主要不兼容性改动包括: 1. **语法...

    Ruby-Ruby中的GoF设计模式实现

    Ruby中的代理对象可以通过定义委托(delegation)来实现,例如使用`delegate`方法。 13. **职责链模式**:避免将请求的发送者和接收者耦合在一起,让多个对象都有可能处理请求。Ruby中,可以使用对象间的链式调用来...

    Ruby编程语言算法集

    在Ruby中,一切皆为对象,即使是基本数据类型如整数和字符串。这种特性使得Ruby在处理面向对象编程时非常强大。类和模块是Ruby中的核心概念,它们提供了组织和复用代码的方式。类定义了对象的属性和行为,而模块则...

    ruby trap 初学者使用

    - Ruby中的块(由`do...end`或`{...}`定义)是代码的可执行部分,可以与方法一起使用。Proc和Lambda是块的两种对象形式,它们可以保存并稍后执行。理解它们的差异,如 Proc的`call`和Lambda的`[]`调用方式,以及对...

    Enumerable-Methods:这是Microverse中Ruby模块的项目#2

    基本的可数方法 ...2-通过在终端上使用$ ruby enumerables.rb执行文件。 作者 :laptop:Vanessa青木 GitHub: 推特: Linkedin: :handshake: 贡献 欢迎提供文稿,问题和功能要求! 随时检查。

    前端开源库-node-enumerable

    "node-enumerable" 将这一概念引入JavaScript,使开发者能够使用链式方法对数组和对象进行复杂的查询和操作,提高了代码的可读性和简洁性。 3. **ES6支持** 这个库完全适配ES6标准,这意味着你可以使用箭头函数、...

    ruby实用函数和实例

    此外,Enumerable模块是一组用于遍历和操作集合的强大工具,它包含的`inject`和`reduce`方法可以执行聚合操作,`group_by`则可以按条件对元素分组。 Ruby的模块(Module)和类(Class)系统使得代码组织和复用变得...

    Ruby-fastrubyRuby编写快速收集常见Ruby惯用语

    Ruby中有很多高效的编程习惯和技巧,例如使用块(blocks)、Proc对象和Lambda表达式来替代循环结构。`fast-ruby` 项目提供了这些方面的比较,展示了如何通过改变编程习惯来提高代码执行速度。 2. **方法调用优化**...

    ruby初学者教程(对初学者很有帮助)

    特殊方法是指那些在Ruby中具有特殊含义的方法,例如`initialize`、`==`等。 **3.5 类变量与类方法** 类变量是以`@@`开头的变量,可以在类的所有实例之间共享。类方法则是定义在类本身上的方法。 ```ruby class ...

    Ruby语言中文教程

    方法是Ruby中定义行为的基本方式,可以使用`def`关键字定义。Ruby的方法支持默认参数、可变参数和关键字参数。`send`方法可以动态调用对象的方法。 5. **异常处理** Ruby使用`begin..rescue..else..ensure`结构...

    Ruby程序设计.doc

    这部分内容未给出详细章节,但可以从上下文推断,这部分可能涉及Ruby中的序列化技术以及YAML格式的使用。 以上内容覆盖了Ruby程序设计的核心知识点,从语言基础到高级特性均有涉及。通过这些知识点的学习,可以全面...

    Ruby学习资料chm

    1. 面向对象:在Ruby中,一切都是对象,包括基本的数据类型如整数、字符串和布尔值。这意味着你可以对任何对象进行方法调用,增强了代码的灵活性。 2. 动态性:Ruby允许在运行时修改类和对象,这使得它非常适合快速...

    enumerable-methods

    该项目展示了Ruby Enumerable模块中方法的重建列表。 重写的Enumerable方法是: 每个-&gt; my_each each_with_index-&gt;​​ my_each_with_index 选择-&gt; my_select 全部? -&gt; my_all? 任何? -&gt; my_any? 没有...

    RUBY基础入门指南

    - **使用Ruby**:直接在命令行中输入`ruby filename.rb`来运行脚本文件。 - **使用IDE**:推荐使用FreeRIDE或SciTE等集成开发环境,提供更好的编辑体验。 **2.3 Ruby-irb** IRB(Interactive Ruby Shell)是一个...

Global site tag (gtag.js) - Google Analytics