Ruby Blocks and Procs #
Blocks, Procs, and Lambdas are powerful features in Ruby that allow you to group code into reusable chunks and pass them around your program.
Blocks #
Blocks are chunks of code enclosed between do...end or curly braces:
# Block with do...end
[1, 2, 3].each do |num|
puts num * 2
end
# Block with curly braces
[1, 2, 3].each { |num| puts num * 2 }
# Using yield to call a block
def greet
puts "Before yield"
yield
puts "After yield"
end
greet { puts "Hello from the block!" }
Block Parameters #
Pass parameters to blocks:
def repeat(n)
n.times { |i| yield(i) }
end
repeat(3) { |num| puts "Iteration #{num}" }
# Multiple parameters
def test_block
yield(1, 2)
end
test_block { |a, b| puts "a: #{a}, b: #{b}" }
Procs #
Procs are objects that contain blocks of code:
# Creating a Proc
square = Proc.new { |x| x ** 2 }
puts square.call(5) # 25
# Procs as method parameters
def run_proc(p)
p.call
end
my_proc = Proc.new { puts "Hello from Proc!" }
run_proc(my_proc)
Lambdas #
Lambdas are similar to Procs but with subtle differences:
# Creating a lambda
multiply = lambda { |x, y| x * y }
# or using stabby lambda syntax
multiply = ->(x, y) { x * y }
puts multiply.call(3, 4) # 12
# Lambdas check argument count
add = lambda { |a, b| a + b }
# add.call(1) # Error: wrong number of arguments
Proc vs Lambda Differences #
Key differences between Procs and Lambdas:
# 1. Argument checking
my_proc = Proc.new { |x, y| puts "x: #{x}, y: #{y}" }
my_proc.call(1) # Works: x: 1, y: (nil)
my_lambda = lambda { |x, y| puts "x: #{x}, y: #{y}" }
# my_lambda.call(1) # Error!
# 2. Return behavior
def proc_return
my_proc = Proc.new { return "from proc" }
my_proc.call
"from method" # Never reached
end
def lambda_return
my_lambda = lambda { return "from lambda" }
my_lambda.call
"from method" # This is returned
end
puts proc_return # "from proc"
puts lambda_return # "from method"
Common Block Methods #
Ruby provides many useful methods that accept blocks:
# map/collect - transform elements
squares = [1, 2, 3].map { |n| n ** 2 }
# select/filter - filter elements
evens = [1, 2, 3, 4].select { |n| n.even? }
# reduce/inject - accumulate values
sum = [1, 2, 3, 4].reduce(0) { |acc, n| acc + n }
# each_with_index
%w[a b c].each_with_index do |letter, index|
puts "#{index}: #{letter}"
end
Block Methods #
Check if a block was given and handle it:
def optional_block
if block_given?
yield
else
puts "No block provided"
end
end
optional_block { puts "Block!" }
optional_block