Blocks and Iterators in Ruby
Blocks are closures; they pair naturally with iterators for expressive collection code.
Blocks 101
Blocks use {} or do..end. They capture surrounding variables.
2.times { puts "hi" }
[1,2,3].each do |n|
puts n * 2
end
Precedence: {} vs do..end
{} binds tighter than do..end. Use {} for single‑line function arguments, do..end for multi‑line control flow.
puts [1,2,3].map { |x| x + 1 } # block to map
# vs
[1,2,3].each do |x|
puts x + 1 # multi‑line
end
yield and block_given?
Methods can yield to an optional block; block_given? checks for presence.
def around
puts "before"
value = yield("data") if block_given?
puts "after"
value
end
around { |x| x.upcase } # prints before/after, returns "DATA"
External iterators (Enumerator)
Obtain an enumerator to step manually or chain later.
enum = [1,2,3].each # => #<Enumerator: ...>
enum.next # => 1
enum.next # => 2
enum.rewind
Lazy pipelines
Process large or infinite sequences lazily.
squares = (1..).lazy.select(&:odd?).map { |n| n*n }
squares.take(5).to_a # => [1, 9, 25, 49, 81]
Symbol‑to‑proc shorthand
["a","b"].map(&:upcase) # => ["A","B"]
Summary
- Blocks are closures; pass them implicitly or as Procs
- Prefer iterator methods (
each,map,select,reduce) over manual loops - Use enumerators (
to_enum) andlazyfor advanced control