Higher Order Procedures
Using Higher Order Procedures in Ruby
Trotter Cashion
What is a Higher Order Procedure?
- It's a procedure. (really?)
- Takes a procedure as an argument. Or...
- Returns a procedure as its return value.
Procedures as Data
- Need a method of treating a procedure as data
- Ruby provides Proc objects and blocks.
Procs
- Create using Proc.new or lambda
- Are anonymous procedures that can be called using var.call
- For example:
- my_proc = lambda {|x| x + 10 }
- my_proc.call(5) => 15
A Simple Higher Order Procedure
Beyond the Basics
- Higher order procedures are good for things other than collections, such as Arrays.
- They allow us to remove duplication and respect the DRY principle.
Sum Odd Numbers
class Range
def sum_odd_numbers
result = 0
self.each do |i|
if i % 2 == 1
result += i
end
end
return result
end
end
Respect DRY
class Range
def sum_if(&predicate)
result = 0
self.each do |i|
result += i if yield(i)
end
return result
end
def sum_even_numbers
sum_if {|i| i % 2 == 0}
end
def sum_odd_numbers
sum_if {|i| i % 2 == 1}
end
end
Advantages
- Using higher order procedures facilitates the expression of behavior within our code.
- Adding a new paradigm, such as sum_multiples_of_three, is as simple as making a new rule for sum_if.
Procs and Blocks Are Closures
- They execute within the scope in which they were defined, regardless of the scope from which they are called.
- i.e. - They have access to all the variables that were in scope at the time they were defined.
Extras: Can Be Used To Make Data
- Using higher order procedures and the closure property, we can make pair data objects like those in lisp.
def make_pair(x, y)
lambda do |m|
return x if m == 0
return y if m == 1
raise "Argument isn't 0 or 1"
end
end
def first(pair); pair.call(0); end
def second(pair); pair.call(1); end
my_pair = make_pair(4, 5) => <#Proc>
first(my_pair) => 4
Can Even Be Used To Create Objects
- We can use a higher order procedure to create a number object.
def make_number(x)
lambda do |op, *y|
if op == '+'
make_number(x + y[0])
elsif op == '-'
make_number(x - y[0])
elsif op == 'val'
x
end
end
end
my_num = make_number(5) => <#Proc>
my_num.call('+', 7).call('val') => 12
Gotchas
- Procedures created as blocks or using Proc.new do not treat the keyword return like normal.
- Using return will cause a LocalJumpError.
- Must use 'break' anywhere you would normally use a return.
- Procedures defined using lambda behave as expected in regards to return.
- A return exits the anonymous function only.
Thank You
- I hope that you've learned one or two new things.
- If you want to practice using higher order procedures, go to my website lifecoding.com where I have some exercises that you can try.