-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy path08_procs_and_lambdas.rb
166 lines (119 loc) · 4.75 KB
/
08_procs_and_lambdas.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# *Blocks recap*
names = ["mariana", "gabriele", "shannon"]
names.each do |name|
# Everything inside here is a *block*!
reversed_name = name.reverse
puts reversed_name.upcase
end
# A new powerful method: `map`
# same as `collect` (less common), it modifies each element of a collection
names.map { |name| name.upcase }
puts names
# Nothing happened, because `map` returns just a copy of the original collection, it doesn't modify it!
# I should assign this copy to a new variable
upcase_names = names.map { |name| name.upcase }
puts upcase_names
# or add `!` to the method to modify the original collection (a method with the `!` is a *destructive method*, because it alters the state of the original object)
names.map! { |name| name.upcase }
puts names # it changed!
#############################
# *Yield*
# Use it when you want your method to accept a block
def print_welcome_message
puts "Welcome to today's lesson!"
yield
end
print_welcome_message do
# Everything inside here will replace the yield above!
puts "Today the teachers are Mariana and Gabriele"
puts "We'll talk about yield, procs, lambdas, and other cool stuff!"
end
# What if I don't give a block?
print_welcome_message # => 'no block given' error!
# But there's a solution! Just use `yield if block_given?`
def print_welcome_message
puts "Welcome to today's lesson!"
yield if block_given?
end
print_welcome_message # no error!
# I can also pass parameters to the yield:
def print_welcome_message(teacher_name)
puts "Welcome to today's lesson!"
yield(teacher_name) if block_given?
end
print_welcome_message("gabriele") do |name| # here I have the same variable ("gabriele") available as a parameter (called `name`, but it's arbitrary)!
puts "Today's teacher is #{name.upcase}"
end
#############################
# *Procs*
# Use them to assign a block to a variable
def print_welcome_message
puts "Welcome to today's lesson!"
yield if block_given?
end
today_lecture = Proc.new do
puts "Today's teachers are Mariana and Gabriele"
puts "We'll talk about procs and lambdas!"
end
# Pass the proc (so, the block!) with this syntax:
print_welcome_message(&today_lecture)
# A proc can also be called without relating it to a method, just use the `call` method on the same proc!
today_lecture.call
# Other example: print even numbers
numbers = (1..100).to_a
numbers.each { |n| puts n if n.even? }
# The block above could be reused, let's make it a proc!
print_if_even = Proc.new { |n| puts n if n.even? }
numbers.each(&print_if_even)
#############################
# *Procs and symbols*
# In Ruby, we can pass a method name (*name*, not the whole method!) with a symbol
# This symbol can be easily turned into a proc!
names = ["mariana", "gabriele"]
names.map! { |name| name.upcase }
# We can refer to the `upcase` method with a symbol (`:upcase`), and convert it to a proc by adding a `&` before
names.map!(&:upcase) # note the colon, we are passing a symbol here!
puts names
#############################
# *Lambdas*
# Same as procs, except for (mainly 2) small differences
def print_welcome_message
puts "Welcome to today's lesson!"
yield if block_given?
end
today_lecture = lambda do # just a different keyword, it does the same!
puts "Today's teachers are Mariana and Gabriele"
puts "We'll talk about procs and lambdas!"
end
print_welcome_message(&today_lecture)
# Differences with procs:
# 1. Lambdas check the number of arguments, procs don't
my_proc = Proc.new { |x, y| puts "I don't care about arguments!" }
my_proc.call
my_lambda = lambda { |x, y| puts "I will return an error if you don't give me the right arguments! 🤬" }
my_lambda.call # => 'wrong number of arguments' error! I should pass 2 arguments, x and y
# 2. When they return inside a method, lambas don't interrupt the execution of the function after the `call`, procs do
def lambda_method
my_lambda = lambda { return "Call me, but if you do other stuff after that, I won't be returned :(" }
my_lambda.call # it doesn't stop here!
"You just called the lambda, but I'm not a lambda!"
end
def proc_method
my_proc = Proc.new { return "Call me, and I'll stop the method here!" }
my_proc.call # it stops here!
"This text will never be printed :("
end
puts lambda_method
puts proc_method
#############################
# Some other examples:
# Proc returning student's grades below 10
students_grades = [10, 15, 5, 4, 19, 14, 2, 7, 13]
under_10 = Proc.new { |grade| grade < 10 } # I could use a lambda here, it would be the same!
low_grades = students_grades.select(&under_10)
puts low_grades
# Lambda returning only the symbols inside an array
names = ["mariana", :gabriele, "shannon"]
filter_symbols = lambda { |x| x.is_a? Symbol } # I could use a proc here, it would be the same!
symbols = names.select(&symbol_filter)
puts symbols