16

Rubyで単一のアイテムを「マッピング」する方法を探しています。

この関数を呼び出してブロックを渡したいのですが、オブジェクトはブロックに渡され、ブロックの結果が呼び出し元に返されます。マップが行うこととまったく同じですが、単一の要素に対してです。

その動機は、何か他のものを構築するためだけに使用されるオブジェクトを生成することがあるということです。これで、元のオブジェクトは不要になります。変換をブロックに入れて、一時的なものを削除するとよいでしょう。

不自然な例として、月と年の組み合わせを表す整数を作成したいとします。今日の日付の場合、コードは次のようになります。

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 inの回答を使用するjondavidjohnのも 1 つの方法ですが、再割り当てのオーバーヘッドがありますself。このような機能はかつてRuby コアで提案されましたが、却下されて撤回されました。そこで Ruby 開発者の 1 人である knu (武者明紀) が提示したソリューションを使用すると、次のように記述できます。

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 に答える