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
Table of Contents
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.