Блок Ruby, процедуры и instance_eval

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

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

Что дает ошибку:

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

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

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

b{ upcase }

Дальнейший взгляд с этим методом:

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

Выдает ту же ошибку Proc to String.

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

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

редактировать

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


person brad    schedule 16.06.2011    source источник


Ответы (5)


Все, что вам нужно сделать, чтобы ваш первый пример заработал, это:

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

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

person Michael Kohl    schedule 16.06.2011
comment
Забавно, как я начал разбираться в этой проблеме, когда писал этот вопрос и задавал другим... и в конце концов понял, что мне просто нужно знать разницу между процедурой и блоком. Большое спасибо!! - person brad; 16.06.2011
comment
предоставлена ​​мертвая ссылка - person yohanes; 09.05.2020

Разница в том, что 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 вместо слота block. Не имеет значения, что аргумент является экземпляром Proc. Синтаксис определяет, как он передается и обрабатывается методом.

person Mark Reed    schedule 17.06.2011
comment
хороший лаконичный ответ, спасибо! (извините, я уже принял @Michaels, поскольку он указал мне разницу между блоком и Proc) - person brad; 17.06.2011

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

person Samuel    schedule 16.06.2011
comment
но какая разница между block и proc? - person brad; 16.06.2011

Принципиальное отличие состоит в том, что экземпляр Proc является объектом, тогда как блок не является объектом. & – это оператор, который меняет местами блок и экземпляр Proc.

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

person sawa    schedule 16.06.2011

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

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

Метод instance_eval может получать аргументы блока. b это Proc.

person Justin_lu    schedule 11.08.2013