class Timer
def self.measure
start_time = Time.now
yield if block_given?
end_time = Time.now
end_time - start_time
end
end
# Usage:
duration = Timer.measure do
sleep(1)
puts "Heavy operation"
end
# => 1.002 (seconds)
# Blocks with parameters
class Array
def my_each
return enum_for(:my_each) unless block_given?
i = 0
while i < length
yield self[i]
i += 1
end
self
end
def my_map
return enum_for(:my_map) unless block_given?
result = []
my_each { |item| result << yield(item) }
result
end
end
[1, 2, 3].my_map { |n| n * 2 } # => [2, 4, 6]
# Proc examples
multiply = Proc.new { |a, b| a * b }
multiply.call(3, 4) # => 12
multiply[3, 4] # => 12
multiply.(3, 4) # => 12
# Proc with flexible arguments
greet = Proc.new { |name| "Hello, #{name || 'Guest'}!" }
greet.call('Alice') # => "Hello, Alice!"
greet.call # => "Hello, Guest!" (no error)
# Lambda examples
multiply_lambda = ->(a, b) { a * b }
multiply_lambda.call(3, 4) # => 12
# Lambda with strict arguments
strict_greet = ->(name) { "Hello, #{name}!" }
# strict_greet.call # => ArgumentError (wrong number of arguments)
# Returning from lambda vs proc
def test_lambda
lambda_test = -> { return "from lambda" }
lambda_test.call
"from method"
end
def test_proc
proc_test = Proc.new { return "from proc" }
proc_test.call
"from method"
end
test_lambda # => "from method" (lambda returns to lambda)
test_proc # => "from proc" (proc returns from method)
# Converting blocks to procs
def execute_twice(&block)
block.call
block.call
end
execute_twice { puts "Hello" }
# Hello
# Hello
# Closure capturing variables
def counter
count = 0
-> { count += 1 }
end
increment = counter
increment.call # => 1
increment.call # => 2
increment.call # => 3
# Callback pattern
class EventEmitter
def initialize
@listeners = Hash.new { |h, k| h[k] = [] }
end
def on(event, &callback)
@listeners[event] << callback
end
def emit(event, *args)
@listeners[event].each { |callback| callback.call(*args) }
end
end
emitter = EventEmitter.new
emitter.on(:user_created) { |user| puts "Welcome #{user.name}!" }
emitter.on(:user_created) { |user| Analytics.track(user.id) }
emitter.emit(:user_created, user)
# Strategy pattern with lambdas
class Calculator
OPERATIONS = {
add: ->(a, b) { a + b },
subtract: ->(a, b) { a - b },
multiply: ->(a, b) { a * b },
divide: ->(a, b) { b.zero? ? nil : a / b }
}.freeze
def calculate(operation, a, b)
OPERATIONS[operation]&.call(a, b)
end
end
calc = Calculator.new
calc.calculate(:add, 5, 3) # => 8
calc.calculate(:multiply, 4, 7) # => 28
# Lazy evaluation with procs
class LazyValue
def initialize(&computation)
@computation = computation
@cached = false
end
def value
return @value if @cached
@value = @computation.call
@cached = true
@value
end
end
expensive = LazyValue.new do
puts "Computing..."
sleep(1)
42
end
expensive.value # Computes and prints "Computing..."
expensive.value # Returns cached value immediately
Ruby's closures—blocks, procs, lambdas—enable functional programming patterns. Blocks are anonymous code chunks passed to methods. Procs are objects wrapping blocks, callable with call. Lambdas are stricter procs—check argument count and return differently. I use yield for simple block invocation, block_given? to check presence. &block converts blocks to procs. Lambdas with -> syntax are concise. Closures capture surrounding scope, enabling powerful abstractions. Return behavior differs—lambdas return to caller, procs return from enclosing method. I prefer lambdas for predictable behavior. Understanding closures unlocks Ruby's expressiveness—iterator methods, callbacks, DSLs. They're fundamental to idiomatic Ruby code.