16

我正在寻找一种在 Ruby 中“映射”单个项目的方法。

我想调用这个函数并传递一个块,对象将被让给块,然后块的结果将返回给调用者。正是 map 的作用,但对于单个元素。

动机是有时你生成的对象只是用来构造其他东西。然后不再需要原始对象。将转换放入一个块中并消除临时性会很好。

作为一个人为的例子,假设我想创建一个表示月/年组合的整数。对于今天的日期,代码如下所示:

day = Date.today
month_number = day.year * 100 + day.month

如果我能做这样的事情,我真的很喜欢:

month_number = Date.today.some_function { |d| d.year * 100 + d.month }

但我不知道“some_function”是什么(或者它是否存在)。

如果有更多的 Ruby 方式来处理这样的事情,我会全力以赴。我知道猴子修补类,但我希望处理那些更短暂的情况。

4

3 回答 3

21

instance_eval就是你要找的。

month_number = Date.today.instance_eval { |d| d.year * 100 + d.month }

|d|也是可选的,默认self为对象上下文。

这可能会以更紧凑的方式满足您的需求。

month_number = Date.today.instance_eval { year * 100 + month }
于 2013-02-08T20:32:10.710 回答
19

使用instance_evalas injondavidjohn的答案是一种方法,但它有重新分配的开销self。这样的特性曾经在 Ruby 核心中被提出,但被拒绝并被撤回。使用其中一位 Ruby 开发人员 knu (Akinori MUSHA) 提供的解决方案,您可以这样编写:

month_number = Date.today.tap{|d| break d.year * 100 + d.month}

使用tap,您唯一需要做的额外事情就是将其放在break块的开头。


require 'benchmark'

n = 500000
Benchmark.bm do |x|
  x.report{n.times{Date.today.instance_eval{year * 100 + month}}}
  x.report{n.times{Date.today.instance_eval{|d| d.year * 100 + d.month}}}
  x.report{n.times{Date.today.tap{|d| break d.year * 100 + d.month}}}
end

       user     system      total        real
   2.130000   0.400000   2.530000 (  2.524296)
   2.120000   0.400000   2.520000 (  2.527134)
   1.410000   0.390000   1.800000 (  1.799213)
于 2013-02-09T02:30:12.383 回答
4

Ruby 的内置Object#tap函数很接近,但它不返回块的值。

这是一个想法:

class Object
  def sap
    yield self
  end
end

eleven = 10.sap { |x| x + 1 } # => 11
month_number = Date.today.sap { |d| d.year * 100 + d.month } # => 201202
于 2013-02-08T20:33:58.490 回答