Skip to main content

Ruby Blocks and Procs

Table of Contents

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

Related