Blocks in Ruby are anonymous chunks of code that can be passed to methods as arguments.
They are a powerful feature of Ruby, allowing for concise and flexible code execution.
What You’ll Learn
1. What Are Blocks?
A block in Ruby is a piece of code enclosed between do…end or curly braces {}. Blocks are often used to specify behaviors for methods or to execute code in a specific context.
Example
[1, 2, 3].each do |number| puts number end # Output: # 1 # 2 # 3
2. Using Blocks with Methods
Many Ruby methods, like each, map, and select, are designed to take blocks to define how they should behave.
Example: Iterating with a Block
[1, 2, 3].each { |num| puts num }
# Output:
# 1
# 2
# 3
3. Block Syntax
3.1 do…end for Multiline Blocks
[1, 2, 3].each do |num| puts num * 2 end # Output: # 2 # 4 # 6
3.2 Curly Braces {} for Single-Line Blocks
[1, 2, 3].each { |num| puts num * 2 }
# Output:
# 2
# 4
# 6
4. Passing Blocks to Methods
Ruby allows you to write methods that take a block. Use the yield keyword to execute the block within the method.
5. Yielding to a Block
The yield keyword is used within a method to execute the block passed to it.
Example: Basic yield
def greet
puts "Hello"
yield
puts "Goodbye"
end
greet { puts "Ruby!" }
# Output:
# Hello
# Ruby!
# Goodbye
Example: yield with Parameters
def calculate(a, b)
yield(a, b)
end
result = calculate(5, 3) { |x, y| x + y }
puts result # Output: 8
6. Block Parameters
Blocks can accept parameters, which are defined between vertical bars (| |).
Example
def iterate_over(numbers)
numbers.each { |num| yield num }
end
iterate_over([1, 2, 3]) { |n| puts n * 2 }
# Output:
# 2
# 4
# 6
7. Using Proc and lambda
7.1 Proc
A Proc is an object that encapsulates a block.
Example: Creating a Proc
my_proc = Proc.new { puts "Hello from Proc!" }
my_proc.call
# Output: Hello from Proc!
Example: Passing a Proc to a Method
def execute_proc(proc)
proc.call
end
my_proc = Proc.new { puts "Hello again!" }
execute_proc(my_proc)
# Output: Hello again!
7.2 lambda
A lambda is a special type of Proc that is more strict about arguments.
Example: Creating a lambda
my_lambda = lambda { |x| puts x * 2 }
my_lambda.call(5) # Output: 10
Differences Between Proc and lambda
- A lambda checks the number of arguments passed, while a Proc does not.
- A lambda returns control to the caller after execution, but a Proc can return from the method containing it.
def test_proc
p = Proc.new { return "Proc Exited" }
p.call
"After Proc"
end
def test_lambda
l = lambda { return "Lambda Executed" }
l.call
"After Lambda"
end
puts test_proc # Output: Proc Exited
puts test_lambda # Output: After Lambda
8. Practical Examples
8.1 Using a Block to Customize Behavior
def repeat(n)
n.times { yield }
end
repeat(3) { puts "Ruby is fun!" }
# Output:
# Ruby is fun!
# Ruby is fun!
# Ruby is fun!
8.2 Filtering Elements with Blocks
numbers = [1, 2, 3, 4, 5]
even_numbers = numbers.select { |num| num.even? }
puts even_numbers.inspect # Output: [2, 4]
8.3 Using Blocks with Hashes
scores = { Alice: 90, Bob: 85, Carol: 95 }
scores.each do |name, score|
puts "#{name}: #{score}"
end
# Output:
# Alice: 90
# Bob: 85
# Carol: 95
8.4 Block for Repeated Tasks
def measure_time
start_time = Time.now
yield
end_time = Time.now
puts "Elapsed time: #{end_time - start_time} seconds"
end
measure_time do
sum = 0
(1..1_000_000).each { |i| sum += i }
end
# Output: Elapsed time: X seconds (depends on your machine)
8.5 Passing Multiple Blocks Using Proc
def execute_blocks(block1, block2)
block1.call
block2.call
end
block1 = Proc.new { puts "Block 1 executed!" }
block2 = Proc.new { puts "Block 2 executed!" }
execute_blocks(block1, block2)
# Output:
# Block 1 executed!
# Block 2 executed!
9. Best Practices for Using Blocks
- Use Blocks for Customizable Behavior:
- Blocks are ideal for methods like each, map, or custom iterators.
- Leverage Proc and lambda for Reusability:
- Use Proc and lambda when you need to reuse blocks.
- Prefer yield for Simplicity:
- If a block is required, yield is simple and clear.
- Handle Argument Mismatches:
- Ensure block arguments match the method’s expectations, especially for lambda.
10. Summary
Key Concepts
- Blocks are anonymous code chunks passed to methods.
- Use yield to execute blocks.
- Proc and lambda encapsulate reusable blocks.
- Blocks are commonly used for iterating and customizing behavior.
Examples in Context
Ruby blocks empower developers to write concise, reusable, and dynamic code, making them essential for Ruby programming.
