Computer Science‎ > ‎

Functional Programming with Ruby



Programming With Ruby

While Ruby is a regular programming language, here's how we can use a few good ideas from Functional Programming to write code in a style which is similar to the Functional Paradigm.

Filters


Ruby brings to us find_all and select  keywords to help us use filters.
In the example below we apply a filter ( which selects only even numbers, i.e. those divisible by 2) and applies it to the list of first ten natural numbers (which are given by (1..10) in Ruby )

(1..10).select{|x| x%2==0}
=> [2, 4, 6, 8, 10]

(1..10).find_all{|x| x%2==0}
=> [2, 4, 6, 8, 10]


Loops and Iterators

The each keyword is a more "functional" way of doing things as opposed to regular loops.

Imperative style for loop in Ruby :

for counter in 1..10
    puts counter
end

Output:
1
2
..
..
10


Way to do it using "each" :

(1..10).each{|x| puts x}
1
2
3
4
5
6
7
8
9
10
=> 1..10

Iterating Through a Hash Map :
{"robin"=>"red","hood"=>"green"}.each{|key,value| puts key + ":" + value}

Output:
{"robin"=>"red","hood"=>"green"}.each{|key,value| puts key + ":" + value}


Some Examples of Recursive Functions


Computing the Factorial of a number :

def factorial(n)
    n if n<=1
    n * factorial(n-1)
end


Using an accumulator to compute the factorial:

def fact(n,acc)
    0 if n == 0
    acc if n == 1
    fact(n-1,acc*n)
end

def factorial(n)
    fact(n,1)
end


Computing the n-th Fibonacci Term

def fib(n)
    n if n<= 1
    fib(n-1) + fib(n-2)
end

Lambda in Ruby

Ruby1.9 onward has "lambda" which is support for anonymous functions.
Here's how we created a "lambda".

lm = ->(x,y,z){ x * y * z}
lm.call(1,2,3)
=> 6

def callAFunction(x,y,z,lambda)
    lambda.call(x,y,z)
end

callAFunction(2,3,4,lm)
=> 24

So, we sent in lambda like a function pointer to callAFunction
Lambda was then invoked with the parameters (2,3,4) and it returned the product 2*3*4 = 24.

Why exactly is this useful ?

Consider the following tasks :
We need to evaluate :
square of sin(x)
square of cos(x)
square of 10*x

Method #1:
Here we declare three different functions for somewhat similar purposes.

def squareOfSin(x)
    Math.sin(x)*Math.sin(x)
end

def squareOfCos(x)
    Math.cos(x) * Math.cos(x)
end

def squareOf10Times(x)
    (10*x) * (10*x)
end

Method #2:

def squareOfFunction(x,lambda)
    lambda.call(x) * lambda.call(x)
end

squareOfFunction(x, (x){Math.sin(x)} )
squareOfFunction(x, (x){Math.cos(x)} )
squareOfFunction(x, (x){10*x} )


So, we use the idea of a higher order function in Method #2. Instead of creating three different functions for squaring sin(x), cos(x), etc.  we pass in the lower order function( sin, cos, 10x ) as an anonymous function to squareOfFunction.

Closures in Ruby

Closures in Ruby are supported via the proc and lambda keywords. This is a concept which is a fairly important part of the functional programming paradigm. Broadly speaking, they are related to lambdas and function pointers.

I'll explain Closures with the help of an example.


class ClosureTestClass

    def initialize(n)
        @varA = n
        @varB = 10
   end

    # This is the closure code
    def print_values(var_new)
         lambda{puts "varA=" + @varA.to_s+ ";varB=" + @varB.to_s+";"+"New Value="+var_new.to_s} 
    end

end

def caller(test_closure)
        test_closure.call
end

test = ClosureTestClass.new(5)
printer = test.print_values(15)
caller(printer)

Output:
varA=5;varB=10;New Value=15

If you look at the output, you might wonder how it is possible for varA and varB to be present in the output, even though they don't seem to be present as parameters passed to the print_values code block. At the point when the closure was defined, it bound itself to values and variables in the environment which is why it picked up the values of varA and varB, which are attributes of class ClosureTestClass which were defined in initialize. This is the special feature of a closure. varA and varB are "free variables" in the context or the environment, and the closure binds to them.



Currying 

From a function which takes in n parameters, generate one or more functions which have one or more parameter values already filled in. Ruby, from version 1.9, provides us with support to "curry" procs and lambdas.

Say, we have a regular function multiply(a,b)
multiply(a) will now give us a new function, which will multiply whatever is passed to it, by "a".

multiply = lambda {|a,b| a * b}

puts multiply[5,8]

=> This will give us 5*8 = 40
  
curried_multiply = multiply.curry
multiply_by_2 = curried_multiply[2]
multiply_by_10 = curried_multiply[10]

So, we now have 2 new functions, which will multiply a given parameter by 2 and 10 respectively.

puts multiply_by_2(10)
=> This gives us 20

puts multiply_by_10(5)
=> This gives us 50



The Inject keyword.


A Simple example - sum of numbers in an array ..

[
1
, 
2
, 
3
, 
4
].
inject
(
0
) { 
|
result
, 
element
| 
result 
+ 
element 
}


Another example. To find the modal string in a list of strings - the string which occurs most frequently




list_of_strings.inject(Hash.new(0)) { |result, str|
result[str]+=1
result
}.inject(Struct.new(:str, :freq).new("", 0)) { |result, (k,v)|
if v > result.freq
    result.str = k
    result.freq = v
end
result
}.str





If we started off with [code ruby] list_of_strings=["aa","aa","aa","b","c","d"]
The statement would return 
"aa"
 because it occurs more frequently than any other string in the array. The above statement is pretty obfuscated and definitely not elegant, but it was more to convey the idea that inject can be used as an important building block while trying to switch away from imperative styles.

(Some of this is from an answer I gave on Quora).






Check out some of our other Ruby Tutorials :

Introduction to Ruby

 Introduction to Ruby and some playing around with the Interactive Ruby Shell (irb) Introduction to Ruby - Conditional statements and Modifiers: If-then, Unless, Case Introduction to Ruby Comments - Single and Multi-Line comments Introduction to Ruby Loops - Using While, Until, For, Break, Next , Redo, Retry
 Introduction to Ruby - Arrays - Sorting, Filtering (Select), Transforming, Multi-Dimensional Arrays Introduction to Ruby - Strings Introduction to Ruby - Making a Script Executable Introduction to Ruby - Regular Expressions, Match, Scan
 Introduction to Ruby - Computing Factorials Recursively : An Example of Recursion Introduction to Ruby - Binomial Coefficients (nCr) : An Example of Recursion Introduction to Ruby - Computing a Power Set : An Example of Recursion Introduction to Ruby - Towers of Hanoi : An Example of Recursion
 Introduction to Ruby - Strings: Substitution, Encoding, Built-In Methods 



Basic Data Structures With Ruby








Programming With Ruby

 Introduction to Ruby and some playing around with the Interactive Ruby Shell (irb)

Introduction to Ruby - Conditional statements and Modifiers: If-then, Unless, Case

Introduction to Ruby Comments - Single and Multi-Line comments

Introduction to Ruby Loops - Using While, Until, For, Break, Next , Redo, Retry

Introduction to Ruby - Arrays - Sorting, Filtering (Select), Transforming, Multi-Dimensional Arrays 

Introduction to Ruby - Strings

Introduction to Ruby - Making a Script Executable

Introduction to Ruby - Regular Expressions, Match, Scan

Introduction to Ruby - Computing Factorials Recursively : An Example of Recursion

Introduction to Ruby - Binomial Coefficients (nCr) : An Example of Recursion

Introduction to Ruby - Computing a Power Set : An Example of Recursion

Introduction to Ruby - Towers of Hanoi : An Example of Recursion

 Introduction to Ruby - Strings: Substitution, Encoding, Built-In Methods

Basic Data Structures in Ruby - Insertion Sort

Basic Data Structures in Ruby - Selection Sort

Basic Data Structures in Ruby - Merge Sort

Basic Data Structures in Ruby - Quick Sort

Functional Programming with Ruby

Basic Data Structures in Ruby - Stack

Basic Data Structures in Ruby - The Queue

Basic Data Structures in Ruby - Linked List - ( A Simple, Singly Linked List)

Basic Data Structures in Ruby - Binary Search Tree