Blocks, Procs and Lambdas
14.1 Blocks
[1, 2, 3].each { |n| puts n } # braces: single line (convention)
[1, 2, 3].each do |n| # do...end: multi-line
puts n * 2
end
def repeat(times)
return enum_for(:repeat, times) unless block_given? # return an enumerator without a block
times.times { |i| yield i } # yield calls the block
end
repeat(3) { |i| puts "Iteration #{i}" }
def with_timing
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
result = yield
puts "Took #{(Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).round(3)}s"
result
end
with_timing { sleep 0.1 }
[1, 2, 3].map { it * 2 } # `it` implicit parameter (Ruby 3.4)
[1, 2, 3].map { _1 * 2 } # numbered parameters (Ruby 2.7+)14.2 Explicit Block Parameters
def run_callback(&callback) # captures the block as a Proc
callback.call(42)
end
def wrap(&block) = other_method(&block) # pass the block along
words.map(&:upcase) # Symbol#to_proc14.3 Procs and Lambdas
Lenient (missing args → nil, extras ignored) Strict (ArgumentError) Returns from the enclosing method Returns from the lambda itself Proc.new, proc, blocks lambda, ->
square = Proc.new { |x| x * x }
square = proc { |x| x * x }
double = lambda { |x| x * 2 }
triple = ->(x) { x * 3 } # stabby lambda (preferred)
add = ->(a, b = 1) { a + b }
square.call(4); square.(4); square[4]; square.yield(4)
triple.curry; add.arity; triple.lambda?14.4 Closures
def make_counter
count = 0
increment = -> { count += 1 }
get = -> { count }
[increment, get]
end
inc, get = make_counter
3.times { inc.call }
get.call # 3
multiplier = ->(factor) { ->(x) { x * factor } }
times5 = multiplier.(5)
times5.(3) # 1514.5 Composition and Currying
add1 = ->(x) { x + 1 }
dbl = ->(x) { x * 2 }
(add1 >> dbl).call(3) # 8 (add1 then dbl)
(add1 << dbl).call(3) # 7 (dbl then add1)
pipeline = method(:Integer) >> add1 >> dbl
pipeline.("4") # 10
volume = ->(l, w, h) { l * w * h }.curry
volume[2][3][4] # 24