单个对象的Ruby map()

And*_*vis 13 ruby map

我正在寻找一种在Ruby中"映射"单个项目的方法.

我想调用这个函数并传递一个块,该对象将被生成块,然后块的结果将返回给调用者.究竟是什么地图,但对于单个元素.

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

作为一个人为的例子,让我们假设我想创建一个表示月/年组合的整数.对于今天的日期,代码看起来像:

day = Date.today
month_number = day.year * 100 + day.month
Run Code Online (Sandbox Code Playgroud)

如果我可以这样做,我真的很喜欢它:

month_number = Date.today.some_function { |d| d.year * 100 + d.month }
Run Code Online (Sandbox Code Playgroud)

但我不知道'some_function'是什么(或者它是否存在).

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

jon*_*ohn 18

instance_eval 是你在找什么.

month_number = Date.today.instance_eval { |d| d.year * 100 + d.month }
Run Code Online (Sandbox Code Playgroud)

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

这可以以更紧凑的方式满足您的需求.

month_number = Date.today.instance_eval { year * 100 + month }
Run Code Online (Sandbox Code Playgroud)


saw*_*awa 15

使用instance_evalas jondavidjohn的答案是一种方法,但它有重新分配的开销self.这个功能曾经在Ruby核心中提出过,但被拒绝并被撤回.使用Ruby开发人员之一(Akinori MUSHA)提供的解决方案,您可以这样写:

month_number = Date.today.tap{|d| break d.year * 100 + d.month}
Run Code Online (Sandbox Code Playgroud)

使用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)
Run Code Online (Sandbox Code Playgroud)


ein*_*ohn 3

自 Ruby 2.5(2017 年底)和 Ruby 2.6 起,这个问题有了新的答案,分别是yield_selfthen

\n

根据文档,这些方法将 self 交给块并返回块的结果

\n

以你的问题为例

\n
month_number = Date.today.then { |d| d.year * 100 + d.month }\n
Run Code Online (Sandbox Code Playgroud)\n

month_number截至撰写本文之日,将为202207 \xe2\x80\x93。

\n

在较新的 Ruby 版本中,这些方法Object 已从Kernel. 它们看起来是一样的;不过,我不知道为什么他们没有别名或类似的东西。

\n