2016-04-20 7 views
1

Мне нужно передать блок из одного метода в другой (я хочу позвонить Rails.cache.fetch с блоком, переданным моему методу).Передача блока от одного метода к другому

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

require "benchmark" 

def with_block(&block) 
    do_something 'Test', &block 
end 

def with_yield 
    do_something('Test') { yield } 
end 

def do_something(string) 
    "#{yield} #{string}" 
end 

n = 5_000_000 
Benchmark.bmbm do |x| 
    x.report("&block") do 
    n.times { with_block { "Yo" } } 
    end 
    x.report("yield") do 
    n.times { with_yield { "Yo" } } 
    end 
end 


&block 3.320000 0.010000 3.330000 ( 3.340438) 
yield 1.670000 0.000000 1.670000 ( 1.669504) 
--------------------------------- total: 5.000000sec 

      user  system  total  real 
&block 3.270000 0.010000 3.280000 ( 3.275914) 
yield 1.680000 0.000000 1.680000 ( 1.682768) 

Выглядит как { yield } подход гораздо быстрее. Правильно ли это? Есть ли какие-либо ошибки, о которых я не знаю, из-за вызова yield внутри вновь созданного блока?

+0

У вас есть 'Proc' и специализированный' Proc', который является 'lambda', который вы также можете использовать. Метод также является блоком, но не изначально является объектом. Вы можете преобразовать метод, чтобы стать объектом 'Method'. У вас есть выбор, отличный от двух способов передачи блока. Поэтому я думаю, что ваш вопрос несколько неполный. Нет никаких особых соображений, связанных с передачей анонимного блока, за исключением доступа к нему по имени (вне метода, то есть), хотя Procs и lambdas считаются «анонимными», я думаю. – vgoff

+0

'yield' быстрее, действительно. Именно потому, что он менее функциональный. Вы можете только вызывать переданный блок. Вы не можете, например, передать его следующему методу. Ваш тест не имеет смысла, кстати. 'do_something' не использует блок. –

+0

@SergioTulentsev Вы можете передать его, позвонив ему. 'class Array; def my_map; отображение {| е | yield (e)} end end'. – sawa

ответ

3

Короткий ответ: Всегда используйте yield, если у вас нет веской причины явно ссылаться на &block.

См: Why blocks make ruby methods 439% slower

С &block, вы получите овеществленную Proc, на котором вы можете сделать все виды stuff и которые вы можете передвигаться. Однако с yield и неявным блоком вы ограничены только вызовом блока.

Используя yield, интерпретатор может обойти все верификации Proc, поскольку он знает, что разработчик не сможет его использовать; следовательно, он может сохранить только структуру уровня С вместо того, чтобы настраивать объект уровня Ruby.

+0

Я понимаю, почему выход быстрее, чем явная ссылка на '& block'. Есть ли лучший способ передать блок от одного метода к другому, ожидать от вызова 'yield' из нового блока? – xx77aBs

+0

Если у вас есть хорошее обоснование для получения удара по производительности, вы можете явно ссылаться на '& block'. (Затем вы просто используете 'block.call' вместо' yield'.) Или, еще один трюк, который вы можете использовать, - 'Kernel # block_given?': Http://apidock.com/ruby/Kernel/block_given%3F - - которые могут использоваться без явного объявления '& block'. –

 Смежные вопросы

  • Нет связанных вопросов^_^