`
sunwinner
  • 浏览: 197944 次
  • 性别: 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,并具有对所扁平化内容的编译时间控制

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

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

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

    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? 没有...

    前端开源库-node-enumerable

    前端开源库-node-enumerable节点可枚举,ES6就绪LINQ功能以typescript编写

    enumerable-challenges

    无数挑战这个存储库在 WidgetCo 的一个典型工作日中跟随我们的英雄。 在一天的过程中,我们的英雄将面临同事...学习目标展示对 Ruby 的 Enumerable 必须提供的方法的理解。 通过从头开始创建测试和示例数据来实现 TDD。

    learn_ruby_kansai_67

    Ruby 初学者的 53 节课数组和哈希练习 1 让我们创建一个新的 Array,它不使用Enumerable#map将 Array 的每个元素加倍。 a = [ 1 , 2 , 3 , 5 ]result = [ ]a . each do | i | ...a # =&gt; [1, 2, 3, 5]result # =&gt; [2,...

    active_enumerable:类似于ActiveRecord的Ruby可枚举集合的查询方法

    提供类似ActiveRecord的查询方法,以用于Ruby Enumerable集合。 使用哈希或自定义Ruby对象表示记录。 安装 将此行添加到您的应用程序的Gemfile中: gem 'active_enumerable' 然后执行: $ bundle 或自己安装为:...

    Enumerable.js:适用于所有类型集合的实用函数

    这个库很大程度上受到 Ruby 的 Enumerable 模块的启发,并借用了它的大量功能。 注意:这并不意味着是一个独立的库,而是要与具有.each方法的另一种数据类型结合使用。安装作为 NPM 模块npm install enumerable-js ...

    enumerable4j:Ruby的惊人“ Enumerable”移植到Java

    概述enumerable4j是Ruby著名的移植到java 。如何使用在获取最新版本: &lt; dependency&gt; &lt; groupId&gt;io.dgroup&lt;/ groupId&gt; &lt; artifactId&gt;enumerable4j&lt;/ artifactId&gt; ${version}&lt;/ version&gt;&lt;/ dependency&gt; 所需的Java...

    C#使用LINQ中Enumerable类方法的延迟与立即执行的控制

    主要介绍了C#的LINQ查询中Enumerable类方法的延迟与立即执行,LINQ语言集成查询可以让C#和VB以查询数据库相同的方式操作内存数据,需要的朋友可以参考下

    ruby-enum:在Ruby中定义枚举的便捷方法

    目录散列检索键和值将键映射到值将值映射到键重复的枚举键或重复的值遗产贡献版权和许可相关项目 用法枚举可以定义为常量或类方法来访问,这是优先考虑的问题。 常数定义枚举,并将其作为常量引用。 class ...

    ruby语法基础教程

    §13.3.7 Ruby中YAML的使用 99 第十四章 安全控制 100 §14.1 0级 101 §14.1 1级 101 §14.2 2级 101 §14.3 3级 101 §14.4 4级 101 第十五章 单元测试 101 §15.1 什么是单元测试 101 §15.2 Ruby单元测试框架 ...

    Prototype使用指南之enumerable.js

    Enumerable是一个抽象对象(需要说明的是,javascript中并没有类的概念,所指的类也就是一个函数,继承一般指的是一个对象(父)将它的方法属性copy(通过Object.extend, copy的是引用)到子类(函数)的prototype属性(一...

    Recursively Enumerable Sets and Degrees(Soare)

    Recursively Enumerable Sets and Degrees by Rovert I. Soare。递归论的书籍,Soare的,从djvu转过来的,有可能有误。经典的教材

    enumerable.lua:一个lua集合库

    一个受 Ruby 启发的收藏库。 安装 将 (或 )添加到您的项目中。 用法 local Enumerable = require ( ' enumerable ' ) collectionInstance = Enumerable. create ({ 1 , 2 , 3 , 4 , 5 }) secondElement = ...

    EnumeratorKit, 在 Objective C 中,ruby 样式枚举.zip

    EnumeratorKit, 在 Objective C 中,ruby 样式枚举 EnumeratorKit - objective-c 中的ruby样式枚举 EnumeratorKit是一个集合枚举库,在 ruby 模块和 Enumerator 类的Enumerable 之后建模。它允许你用一个非常 comp

    crispy:在 Ruby 中测试任何对象的间谍

    通过使用 Array 和 Enumerable 的方法,您再也不必记住 RSpec 中复杂的 API 和大量的参数匹配器了! 使模拟过时,因此您不必担心将期望放在哪里(即在主题方法之前或之后)。 安装 将此行添加到应用程序的 Gemfile...

Global site tag (gtag.js) - Google Analytics