Как передать блок в метод?ruby-86

Блоки (blocks) — это одна из фундаментальных возможностей Ruby, позволяющая передавать код в методы. Рассмотрим все способы передачи блоков и их особенности.

1. Базовый синтаксис передачи блока

Самый распространённый способ — использование do...end или {...}:

# Способ 1: с do...end (для многострочных блоков)
[1, 2, 3].each do |num|
  puts num * 2
end

# Способ 2: с {...} (для однострочных блоков)
[1, 2, 3].each { |num| puts num * 2 }

2. Явное объявление параметра блока

Можно явно указать параметр блока через &:

def process_items(items, &block)
  items.each(&block)
end

process_items([1, 2, 3]) { |n| puts n * 3 }

Особенности:

  • Блок становится объектом Proc
  • Можно проверять block_given?
  • Можно вызывать через block.call

3. Неявное получение блока через yield

Метод может получать блок неявно и вызывать его через yield:

def print_twice
  yield if block_given?
  yield if block_given?
end

print_twice { puts "Hello!" }
# Выведет "Hello!" два раза

4. Передача существующего Proc/Lambda

Можно передать уже созданный объект Proc или lambda:

doubler = Proc.new { |x| x * 2 }
[1, 2, 3].map(&doubler) # => [2, 4, 6]

tripler = lambda { |x| x * 3 }
[1, 2, 3].map(&tripler) # => [3, 6, 9]

5. Сравнение способов передачи

СпособДоступ черезМожно проверить наличие?Становится Proc?
{...}/do...endyieldblock_given?Нет
Явный &blockblock.callДа (через block)Да
Proc/LambdacallВсегда естьДа

6. Особые случаи передачи блоков

Передача нескольких блоков:

Ruby не поддерживает напрямую, но можно использовать хитрости:

def two_blocks(default1, default2, &main_block)
  block1 = Proc.new { default1 }
  block2 = Proc.new { default2 }
  [main_block, block1, block2]
end

Блоки с аргументами:

def calculate(a, b)
  yield(a, b)
end

result = calculate(5, 3) { |x, y| x * y } # => 15

7. Лучшие практики

  1. Используйте do...end для многострочных блоков, {...} для однострочных
  2. Проверяйте block_given?, если блок опционален
  3. Для сложной логики предпочитайте явное объявление через &block
  4. Избегайте излишнего преобразования блоков в Proc без необходимости

8. Примеры использования

Кастомный метод с блоком:

def repeat(times)
  times.times { yield } if block_given?
end

repeat(3) { puts "Ruby!" }

Блок с возвращаемым значением:

def transform(array)
  array.map { |item| yield(item) }
end

transform([1, 2, 3]) { |x| x**2 } # => [1, 4, 9]

Резюмируем: в Ruby есть несколько способов передачи блоков в методы — от неявного использования yield до явного объявления параметра с &. Выбор конкретного способа зависит от потребностей: для простых случаев достаточно yield, для более сложных сценариев лучше использовать явное объявление блока как Proc. Блоки — мощный инструмент Ruby, делающий код более выразительным и гибким.