byGink▻ Sun, 14 Mar 2021
A block in Ruby is an anonymous, inline piece of code that can be passed as an argument to a method. Blocks are defined using the do
and end
keywords or curly braces {}
. They can be invoked by the method they are passed to using the yield keyword.
Here's an example that demonstrates the use of a block in Ruby:
3.times do
puts "Hello inside the block"
end
# Hello inside the block
# Hello inside the block
# Hello inside the block
In this example, times
method takes a block and execute it with specified number of times. When this code runs, it outputs "Hello inside block" three times.
yield
keywordBut what really happened inside that times
method in the example above? It turns out that was done by a yield
keyword inside. Let's implement our own method to understand how it work.
def do_something
puts "Before yield"
yield
puts "After yield"
end
do_something do
puts "This part is inside block"
end
# Before yield
# This part is inside block
# After yield
Every time we call yield, the block will run, so this is like calling the same method but via block. And we can even pass arguments to yield
def do_another_thing
yield 1
yield 2
yield "halo"
end
do_another_thing do |i|
puts "Something to print out:", i
end
# Something to print out:
# 1
# Something to print out:
# 2
# Something to print out:
# halo
&
operator followed by the block. For example:def repeat(times, &block)
times.times { block.call }
end
repeat(3) { puts "Hello inside the block" }
def repeat(times)
times.times { yield }
end
repeat(3) { puts "Hello inside the block" }
Any method will take a block as the last argument if we don't declare it explicitly. And when declaring it explicitly, we use block.call
instead of yield
to execute block code. That's it.
A proc in Ruby is an object that encapsulates a block of code. Unlike blocks, procs can be stored in variables, passed as arguments to methods, and even returned as the value of other methods.
def repeat(times, proc)
times.times { proc.call }
end
my_proc = Proc.new { puts "Hello inside the proc" }
repeat(3, my_proc)
# Hello inside the proc
# Hello inside the proc
# Hello inside the proc
The proc argument is an instance of the Proc
class that encapsulates the block of code { puts "Hello inside the proc" }
.
One of the main differences between Procs and blocks is that Procs are objects, and blocks are not. This means that Procs can be assigned to variables, passed as arguments to methods, and stored in data structures, while blocks cannot.
proc
keywordWe can also use proc
keyword to declare a proc. But keep in mind about the difference between surrounding scope. The proc
keyword automatically sets the self value of the Proc to the value of self
in the surrounding scope, while Proc.new
does not.
class MyClass
def create_proc
proc { puts self }
end
def create_proc_with_new
Proc.new { puts self }
end
end
my_obj = MyClass.new
my_proc = my_obj.create_proc
my_proc.call # => #<MyClass:0x00007f8c8e1a7b98>
my_proc_with_new = my_obj.create_proc_with_new
my_proc_with_new.call # => main
A lambda in Ruby is a type of closure that is similar to a proc, but with some important differences. Like procs, lambdas are objects that encapsulate a block of code and can be stored in variables, passed as arguments to methods, and returned as the value of other methods.
We can use lambda
keyword or the right arrow ->
to declare a lambda. They're the same, just the difference in syntax. ->(x) { ... }
is equal to lambda { |x| ... }
Here's an example that demonstrates the use of a lambda in Ruby:
def repeat(times, ld)
times.times { ld.call }
end
my_lambda = lambda { puts "Hello inside lambda" }
repeat(3, my_lambda)
# Hello inside lambda
# Hello inside lambda
# Hello inside lambda
my_lambda = lambda { |a, b| a + b }
my_lambda.call(1, 2) # => 3
my_lambda.call(1) # => raises an ArgumentError
return
keyword. While a return
statement inside a proc will cause the containing method to return, a return
statement inside a lambda will cause the lambda itself to return.def returns_lambda
lambda { return "Hello, World!" }
end
def returns_proc
Proc.new { return "Hello, World!" }
end
puts returns_lambda.call # => "Hello, World!"
puts returns_proc.call # => raises a LocalJumpError
If the proc was inside a method, then calling return would be equivalent to returning from that method.
def call_proc
puts "Before the proc"
my_proc = Proc.new { return "Inside the proc" }
my_proc.call
puts "After the proc"
end
p call_proc
# Prints "Before the proc" and returned value is "Inside the proc"
Different to blocks that must be declared and passed to methods immediately, Ruby procs & lambdas also have another special attribute. When you create a Ruby proc or lambda, it captures the current execution scope with it.
Means that a proc will carry with it values like local variables and methods from the context where it was defined. And it always have the latest version from that context.
def return_proc
count = 500
block = Proc.new { puts count }
count = 501
block
end
def call_proc(pass_proc)
pass_proc.call
end
count = 1
my_proc = return_proc
call_proc(my_proc) # => 501
In conclusion, blocks, procs, and lambdas are all types of closures in Ruby that allow you to pass blocks of code as objects and execute them later. While blocks are anonymous and cannot be stored or returned, procs and lambdas can be stored, passed, and returned.
The key difference between procs and lambdas is the way they handle arguments and the return keyword. Understanding these differences is essential for writing clean, maintainable Ruby code.
© 2016-2024 GinkCode.com