【问题标题】:Can someone explain a real-world, plain-language use for inject in Ruby?有人可以解释一下在 Ruby 中注入的真实、简单的语言用法吗?
【发布时间】:2012-08-16 07:34:00
【问题描述】:

我正在学习 Ruby,遇到了注入。我正处于理解它的风口浪尖,但是当我是那种需要现实世界的例子来学习一些东西的人时。我遇到的最常见的例子是人们使用注入来将 (1..10) 范围的总和相加,我不太关心。这是一个随意的例子。

在实际程序中我会用它做什么?我正在学习,所以我可以继续使用 Rails,但我不必有一个以 Web 为中心的示例。我只需要一些有目的的东西,我可以把我的头包裹起来。

谢谢大家。

【问题讨论】:

标签: ruby inject


【解决方案1】:

inject 有时可以通过其“其他”名称reduce 更好地理解。这是一个对Enumerable 进行操作(迭代一次)并返回单个值的函数。

它有许多有趣的使用方式,尤其是与其他Enumerable 方法链接时,例如map。很多时候,它可以是一种更简洁、更有表现力的做事方式,即使有另一种方式。

这样的例子一开始可能看起来没什么用:

range.inject {|sum, x| sum += x}

然而,变量range 不必是一个简单的显式范围。它可以是(例如)从数据库返回的值列表。如果您运行的数据库查询返回了购物车中的价格列表,则可以使用 .inject 将所有价格相加并得出总和。

在简单的情况下,您可以在 SQL 查询本身中执行此操作。在更困难的情况下,例如有些物品加税而有些不加税,inject 之类的东西可能更有用:

cart_total = prices.inject {|sum, x| sum += price_with_tax(x)}

Enumerable 中的对象是需要比简单数值更详细的处理的复杂类时,或者当 Enumerable 包含需要转换为的不同类型的对象时,这种事情也特别有用处理前的常见类型。由于inject 需要一个块,因此您可以将此处的功能设置为您需要的复杂程度。

【讨论】:

  • 这可能是个人喜好问题,但我通常会使用prices.map(&method(:price_with_tax)).inject(:+)(只要维护者知道&method)。
  • 你的例子需要prices.inject(0),否则它不会计算第一项的税。
  • 这让我对注入本身有了一些更好的理解,但我认为我对 Ruby 太陌生了。一个大问题,也是我必须解决的一个问题,就是我只学到了我需要知道的东西。我已经使用 PHP 多年 - 在其中构建了数十个站点 - 但所有站点都具有相同的十几个功能。我需要专注于真正学习 Ruby,而不仅仅是基础知识。但是,当我转而学习基础知识时,所有内容都是用技术语言编写的,并带有针对初学者的极端示例。我不想查找方法并从定义和示例中摸不着头脑。
  • @user1612935- 当我第一次学习 Ruby 时,我遇到了同样的问题,因为我是从类似的背景开始学习的。如果您学习语言本身(完全独立于它在 Web 框架中的使用),您将对 Ruby 的功能和风格有更好的感觉。我建议尝试ruby-doc.org 列出的一些书籍。我发现“Programming Ruby”这本书特别有用,即使对初学者也是如此。
【解决方案2】:

这里有几个inject() 的实际应用示例:

[1, 2, 3, 4].inject(0) {|memo, num| memo += num; memo} # sums all elements in array

该示例遍历 [1, 2, 3, 4] 数组的每个元素,并将元素添加到 memo 变量(memo 通常用作块变量名称)。此示例在每次迭代后显式返回 memo,但返回也可以是隐式的。

[1, 2, 3, 4].inject(0) {|memo, num| memo += num} # also works

inject() 在概念上类似于以下显式代码:

result = 0
[1, 2, 3, 4].each {|num| result += num}
result # result is now 10

inject() 对于创建数组和散列也很有用。下面是如何使用inject() 将[['dogs', 4], ['cats', 3], ['dogs', 7]] 转换为{'dogs' => 11, 'cats' => 3}

[['dogs', 4], ['cats', 3], ['dogs', 7]].inject({'dogs' => 0, 'cats' => 0}) do |memo, (animal, num)|
  memo[animal] = num
  memo
end

这是一个更通用、更优雅的解决方案:

[['dogs', 4], ['cats', 3], ['dogs', 7]].inject(Hash.new(0)) do |memo, (animal, num)|
  memo[animal] = num
  memo
end

同样,inject() 在概念上类似于以下代码:

result = Hash.new(0)
[['dogs', 4], ['cats', 3], ['dogs', 7]].each do |animal, num|
  result[animal] = num
end
result # now equals {'dogs' => 11, 'cats' => 3}

【讨论】:

    【解决方案3】:

    假设您有一个 eBay 上某件商品的销售价格列表,而不是一个范围,并且您想知道平均价格。您可以通过注入 + 然后除以长度来做到这一点。

    【讨论】:

      【解决方案4】:

      ActiveRecord 范围是一个典型案例。如果我们在模型上调用scoped,我们会得到一个对象,我们可以在该对象上链接其他作用域。这让我们可以使用注入来建立一个搜索范围,比如说,一个参数哈希:

      search_params = params.slice("first_name","last_name","city","zip").
        reject {|k,v| v.blank?}
      
      search_scope = search_params.inject(User.scoped) do |memo, (k,v)|
        case k
        when "first_name"
          memo.first_name(v)
        when "last_name"
          memo.last_name(v)
        when "city"
          memo.city(v)
        when "zip"
          memo.zip(v)
        else
          memo
        end
      

      (注意:如果没有提供参数,这会带回整个表格,这可能不是您想要的。)

      【讨论】:

      • 我希望我真的明白这一点,但它现在已经过去了。我不是白痴,我发誓我不是,我只是在编程上苦苦挣扎,但我努力去理解,一旦有什么东西最终触动了我,它就会终生存在。
      • 没关系。在这个例子中发生了很多事情。我试图说明的一点是inject 在实际项目中很有用。将其视为遍历集合并生成单个结果的通用函数——可以是数字、字符串或任何你想要的对象。
      • 这更有意义。我仍然没有完全理解它,但我可以看到我很可能有一天会需要它并且会理解它。
      【解决方案5】:

      我最喜欢对 inject 或它的同义词 reduce 的解释是:

      reduce 接收一个数组并将其缩减为单个值。它通过遍历一个列表、沿途保持和转换一个运行总计来做到这一点。

      我在一篇精彩的文章中找到了它 http://railspikes.com/2008/8/11/understanding-map-and-reduce

      【讨论】:

        猜你喜欢
        • 2011-10-14
        • 2017-08-28
        • 2020-01-17
        • 2011-06-19
        • 2019-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-15
        相关资源
        最近更新 更多