Ruby map() для одного объекта

Я ищу способ "отобразить" один элемент в 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" (или даже существует).

Если есть более рубиновый способ обработки чего-то подобного, я все уши. Я знаю классы патчей обезьян, но я ищу, чтобы обрабатывать те случаи, которые немного более преходящи.

Ответ 1

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 }

Ответ 2

Использование instance_eval как в ответе jondavidjohn - один из способов, но у него есть накладные расходы для переназначения self. Такая функция была когда-то в Ruby core, но была отклонена и была отозвана. Используя решение, представленное там одним из разработчиков 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)

Ответ 3

Ruby builtin 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