Advanced Techniques with Ruby Blocks
Ruby blocks are a powerful feature of the language. They allow you to write concise and expressive code, and are used extensively in the standard library.
Here are couple of advanced techniques that you can use to get the most out of them:
Lambda vs. Proc
In Ruby, there are two types of blocks: lambdas
and Procs
. While they are similar in many ways, there are some important differences. For example, a lambda is more strict about the number of arguments it receives, while a Proc is more flexible. Here's an example:
def test_lambda
lambda { |x| puts x }
end
def test_proc
Proc.new { |x| puts x }
end
l = test_lambda
p = test_proc
l.call(1) # prints 1
p.call(1) # prints 1
l.call(1, 2) # ArgumentError: wrong number of arguments (2 for 1)
p.call(1, 2) # prints 1
In this example, test_lambda
and test_proc
are methods that return a lambda and a Proc, respectively. The lambda is more strict about the number of arguments it receives, while the Proc is more flexible.
Closures
A closure is a block of code that captures the environment in which it was defined. This means that it has access to variables and methods that were defined in the same scope. Here's an example:
def make_counter(start)
lambda { start += 1 }
end
c1 = make_counter(0)
c2 = make_counter(10)
puts c1.call # prints 1
puts c1.call # prints 2
puts c2.call # prints 11
puts c2.call # prints 12
In this example, make_counter
is a method that returns a lambda. The lambda captures the value of start
when it was defined, and increments it each time it is called. This allows you to create multiple counters that operate independently.
Lazy Evaluation
Lazy evaluation is a technique that allows you to defer the evaluation of a block until it is needed. This can be useful for improving performance or conserving resources. Here's an example:
def lazy(&block)
Enumerator.new do |yielder|
loop { yielder.yield(block.call) }
end.lazy
end
numbers = lazy { rand(100) }
puts numbers.take(10).to_a.join(", ")
In this example, lazy
is a method that takes a block and returns a lazy Enumerator. The block is evaluated each time an element is requested from the Enumerator using the take
method. This allows you to generate a potentially infinite sequence of values without actually generating them all at once.
Ruby blocks are a powerful feature of the language and allow you to write concise and expressive code, and are used extensively in the standard library. Understanding how to use blocks is essential for any Ruby developer. With blocks, you can write code that is more flexible and reusable, and that can be used in a variety of contexts.