Блок Ruby, procs и instance_eval

Недавно я попытался сделать что-то похожее на это:

a = "some string"
b = Proc.new{ upcase }
a.instance_eval b

Который дает ошибку:

TypeError: невозможно преобразовать Proc в строку

но это работает:

def b(&block)
  "some string".instance_eval &block
end

b{ upcase }

Дальше посмотрите с помощью этого метода:

def b(&block)
  "some string".instance_eval block
end

Устанавливает ту же ошибку Proc to String.

Итак... мое понимание блоков состоит в том, что они просто проки. Но, очевидно, что-то особенное в том, что этот амперсанд &...

Может кто-нибудь объяснить это мне? Возможно ли преобразовать обычный proc в то, что является особенным в этом объекте &block?

изменить

Только что выяснил мой второй вопрос, добавьте & в proc... это было легко, но ЧТО это действительно делает?

Ответ 1

Все, что вам нужно сделать для первого примера работы:

>> a.instance_eval &b #=> "SOME STRING"

Причина в том, что instance_eval требуется либо строка, либо блок, и амперсанд предоставляет последнюю.

Для понимания различий между блоками и procs, возможно, следующее сообщение в блоге помогает:

Понимание блоков Ruby, Procs и Lambdas

Ответ 2

Отличие состоит в том, что a.instance_eval b передает b как обычный аргумент instance_eval, тогда как a.instance_eval & b передавая его как блок. Это две разные вещи.

Рассмотрим этот вызов метода:

obj.foo(bar) do | x | stuff (x) end

Это вызывает метод foo с одним регулярным аргументом (bar) и одним блочным аргументом ( do | x | stuff (x) end). В определении метода они отличаются префиксом & к параметру блока:

def foo (arg, & block)
...
конец

И если вы хотите передать переменное выражение вместо литерального блока, то это также выполняется путем префикса и выражения (которое должно давать Proc).

Если вы передаете аргумент без &, он переходит в слот arg вместо слота блока. Не имеет значения, что аргумент оказывается экземпляром Proc. Синтаксис определяет, как он передается и обрабатывается методом.

Ответ 3

Это потому, что instance_eval принимает строку для eval или block. instance_eval(&block) передает ваш block в качестве блока instance_eval.

Ответ 4

Важнейшим отличием является то, что экземпляр Proc является объектом, тогда как блок не является объектом. & - это оператор, который взаимно заменяет блок и экземпляр Proc.

Все аргументы метода должны быть объектом. В дополнение к аргументам, метод может принимать блок. instance_eval - это метод, который принимает либо аргумент String, либо блок. Передача объекта Proc не будет удовлетворять ни одному случаю. Если вы присоедините & к объекту Proc, это будет обработано как блок.

Ответ 5

Это будет работать:

a = "some string"
b = Proc.new{ upcase }
a.instance_eval &b

Метод instance_eval может принимать блок-аргументы. b является Proc.