не можно без проблем конвертировать процесс в лямбду. Ответ Марка Рушакова не сохраняет значение self
в блоке, потому что self
становится Object.new
. Ответ Павла Томулика не может работать с Ruby 2.1, потому что define_singleton_method
теперь возвращает символ, поэтому to_lambda2
возвращает :_.to_proc
.
Мой ответ также неверен :
def convert_to_lambda &block
obj = block.binding.eval('self')
Module.new.module_exec do
define_method(:_, &block)
instance_method(:_).bind(obj).to_proc
end
end
Сохраняет значение self
в блоке:
p = 42.instance_exec { proc { self }}
puts p.lambda? # false
puts p.call # 42
q = convert_to_lambda &p
puts q.lambda? # true
puts q.call # 42
Но это не так с instance_exec
:
puts 66.instance_exec &p # 66
puts 66.instance_exec &q # 42, should be 66
Я должен использовать block.binding.eval('self')
, чтобы найти правильный объект. Я поместил свой метод в анонимный модуль, чтобы он никогда не загрязнял ни один класс. Затем я привязываю свой метод к правильному объекту. Это работает, хотя объект никогда не включал модуль! Связанный метод создает лямбду.
66.instance_exec &q
терпит неудачу, потому что q
является тайным методом, привязанным к 42
, и instance_exec
не может повторно связать метод. Это можно исправить, расширив q
, чтобы открыть несвязанный метод, и переопределив instance_exec
, чтобы связать несвязанный метод с другим объектом. Даже в этом случае module_exec
и class_exec
все равно потерпят неудачу.
class Array
$p = proc { def greet; puts "Hi!"; end }
end
$q = convert_to_lambda &$p
Hash.class_exec &$q
{}.greet # undefined method `greet' for {}:Hash (NoMethodError)
Проблема в том, что Hash.class_exec &$q
определяет Array#greet
, а не Hash#greet
. (Хотя $q
является тайным методом анонимного модуля, он по-прежнему определяет методы в Array
, а не в анонимном модуле.) В исходном протоколе Hash.class_exec &$p
будет определять Hash#greet
. Я пришел к выводу, что convert_to_lambda
неверно, потому что он не работает с class_exec
.