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