В Ruby работа с блоками кода — фундаментальный навык для любого разработчика. Понимание разницы между lambda, proc и yield критически важно для написания чистого, эффективного и поддерживаемого кода. В этой статье мы детально разберём ключевые отличия, особенности поведения и практические сценарии применения каждого подхода.
🧠 Три способа вызвать блок кода
def with_yield
yield
end
def with_proc(p)
p.call
end
with_yield { puts "через yield" } # => через yield
with_proc(Proc.new { puts "через proc" }) # => через proc
🤹♂️ Различия между lambda и Proc.new
1. Поведение return
def l
f = lambda { return "lambda" }
f.call
"после lambda"
end
def p
f = Proc.new { return "proc" }
f.call
"после proc"
end
puts l # => "после lambda"
puts p # => LocalJumpError (или nil, если rescue)
lambda: return выходит из lambda
proc: return выходит из окружающего метода!
2. Проверка аргументов
l = ->(x, y) { "#{x}, #{y}" }
p = Proc.new { |x, y| "#{x}, #{y}" }
puts l.call(1) # ArgumentError!
puts p.call(1) # => "1, "
lambda — строгая: проверяет кол-во аргументов.
proc — снисходительная: недостающие аргументы = nil.
🚀 Синтаксис
lambda { } # обычная форма
->(x, y) { x + y } # сокращённая
Proc.new { } # старый стиль
proc { } # alias
Современные проекты предпочитают ->.
🧷 А yield?
- Вызывает переданный блок, без явного параметра.
- Работает только если был передан блок.
def do_something
yield if block_given?
end
do_something { puts "Окей!" }
💥 Попробуй вызвать
yield, если блока нет — будетLocalJumpError.
📌 Практика VS собеседование
| Характеристика | lambda | Proc.new | yield |
|---|---|---|---|
return внутри |
не влияет на метод | выходит из метода | выходит из метода |
| Аргументы | строго | мягко | зависит от сигнатуры |
| Доступен как объект? | да | да | нет |
| Можно передать в переменную | да | да | нет |
💡 Совет: всегда называй блоки, когда нужен контроль
def wrap(&block)
block.call if block
end
Доступен как объект?
🔹 lambda и Proc.new:
Они создают объекты, которые можно присвоить в переменную:
f = lambda { puts "Hi" }
f.call
p = Proc.new { puts "Hello" }
p.call
→ Это именованные объекты (f, p) — переменные, которые хранят блок и дают к нему доступ.
🔸 yield:
yield — это безымянный вызов блока, переданного в метод. Блок не получает имени по умолчанию:
def greet
yield if block_given?
end
greet { puts "Yo" }
→ У нас нет переменной, ссылающейся на этот блок.
Если хочешь дать имя — надо явно использовать &block:
def greet(&block)
block.call
end
💡 Резюме:
- lambda и proc: объекты, которые ты можешь назвать и передавать
- yield: “встроенный” вызов анонимного блока, без доступа к объекту этого блока
🔚 Вывод:
lambda и proc — братья с разным воспитанием. А yield — это просто безымянный гость. Разница кажется мелкой, пока return не устроит саботаж твоему методу.