Functional Programming in Ruby
04 Feb 2017Although I’ve been focusing learning functional programming through Elixir and Elm these days, it’s interesting to go back to the first programming language I love to see what it can do.
These are some examples of Ruby’s functional programming abilities taken from HackerRank’s Ruby challenges.
Ruby can support higher-order functions i.e. can take and pass functions as arguments through several mechanisms: Blocks, Procs, and Lambdas. I’m not going to go into super detail as there are plenty good posts already out there on the topic. But a basic primer goes as follows:
Passing a Block to a function as nameless method:
def calculate(a,b)
yield(a, b)
end
puts calculate(15, 10) {|a, b| a - b} # 5
Assigning a Proc to a variable. Proc’s are like a “saved” block:
def foo(a, b, my_proc)
my_proc.call(a, b)
end
add = proc {|x, y| x + y}
puts foo(15, 10, add) # 25
Very similar to Procs but differing how they return (see link mentioned beginning of post) out of a function call is Ruby’s lambda.
# the -> keyword is Ruby >= 1.9's lambda syntax
area = ->(a, b) { a * b }
x = 10.0; y = 20.0
area_rectangle = area.(x, y)
area_triangle = 0.5 * area.call(x, y)
puts area_rectangle #200.0
puts area_triangle #100.0
Let’s dig into an interesting example where we show partial function applications
def factorial(n)
(1..n).inject(:*) || 1
end
combination = -> (n) do
-> (r) do
factorial(n) / (factorial(r) * factorial(n-r))
end
end
#How many combinations in 52 card deck when taking 5 cards?
n = gets.to_i # Let's enter 52
r = gets.to_i # Let's enter 5
nCr = combination.(n) # returns a lambda object
puts nCr.(r) # call our lambda with 2nd argument to be applied, result is 2,598,960
So similar to partial function applications is the concept of currying. Currying is converting a single function of n arguments into n functions with a single argument each.
power_function = -> (x, z) {
(x) ** z
}
base = gets.to_i
raise_to_power = power_function.curry.(base)
power = gets.to_i
puts raise_to_power.(power)