PPL UNIT-5
PPL UNIT-5
PART-1
5.1.1 FUNCTIONAL PROGRAMMING LANGUAGES (FPL)
INTRODUCTION
Functional programming languages are specially designed to handle
symbolic computation and list processing applications. Functional
programming is based on mathematical functions.
Functional programming is a programming language in which we try to
bind everything in pure mathematical functions style. It is a declarative
type of programming style.
Its main focus is on “what to solve” in contrast to an imperative style
where the main focus is “how to solve”.
Functional Programming is based on Lambda Calculus:
Lambda calculus is framework developed by Alonzo Church to study
computations with functions. It can be called as the smallest programming
language of the world. It gives the definition of what is computable.
Anything that can be computed by lambda calculus is computable. It is
equivalent to Turing machine in its ability to compute. It provides a
theoretical framework for describing functions and their evaluation. It
forms the basis of almost all current functional programming languages.
Fact: Alan Turing was a student of Alonzo Church who created Turing
machine which laid the foundation of imperative programming style.
Programming Languages that support functional
programming: Haskell, JavaScript, Scala, Erlang, Lisp, ML, Clojure,
OCaml, Common Lisp, Racket.
Concepts of functional programming:
Pure functions
Recursion
Referential transparency
Functions are First-Class and can be Higher-Order
Variables are Immutable
Pure functions:
These functions have two main properties.
First, they always produce the same output for same arguments
irrespective of anything else.
Secondly, they have no side-effects i.e. they do modify any
argument or global variables or output something.
Later property is called immutability. The pure functions only result is the
value it returns. They are deterministic. Programs done using functional
programming are easy to debug because pure functions have no side
effect or hidden I/O. Pure functions also make it easier to write
1
parallel/concurrent applications. When the code is written in this style, a
smart compiler can do many things – it can parallelize the instructions,
wait to evaluate results when need them, and memorize the results since
the results never change as long as the input doesn’t change.
example of the pure function:
sum(x, y) // sum is function taking x and y as
arguments
return x + y // sum is returning sum of x and y without
changing them
Recursion:
There are no “for” or “while” loop in functional languages. Iteration in
functional languages is implemented through recursion. Recursive
functions repeatedly call themselves, until it reaches the base case.
example of the recursive function:
fib(n)
if (n <= 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
Referential transparency:
In functional programs variables once defined do not change their value
throughout the program. Functional programs do not have assignment
statements. If we have to store some value, we define new variables
instead. This eliminates any chances of side effects because any variable
can be replaced with its actual value at any point of execution. State of
any variable is constant at any instant.
example:
x = x + 1 // this changes the value assigned to the variable x. So
the expression is not referentially transparent.
Functions are First-Class and can be Higher-Order:
First-class functions are treated as first-class variable. The first class
variables can be passed to functions as parameter, can be returned from
functions or stored in data structures. Higher order functions are the
functions that take other functions as arguments and they can also return
functions.
example:
show_output(f) // fun show_output is declared taking argument f, which
are another fun
f(); // calling passed function
print_gfg() // declaring another function
print("hello gfg");
show_output(print_gfg) // passing function in another function
2
Variables are Immutable:
In functional programming, we can’t modify a variable after it’s been
initialized. We can create new variables – but we can’t modify existing
variables, and this really helps to maintain state throughout the runtime
of a program. Once we create a variable and set its value, we can have
full confidence knowing that the value of that variable will never change.
3
Applications:
1. It is used in mathematical computations.
2. It is needed where concurrency or parallelism is required.
3. The main domain of functional programming language is Artificial
Intelligence. This knowledge is used for building expert systems,
knowledge representation, machine learning, natural language
processing, modelling speech ad vision.
4
Function applications are specified by pairing the function name
with a particular element of the domain set. Every occurrence of a
parameter is bound to a value from the domain set and is a
constant during evaluation. For example, consider the following
evaluation of cube(x): cube (2.0) = 2.0 * 2.0 * 2.0 = 8
The parameter x is bound to 2.0 during the evaluation and there are
no unbound parameters. Furthermore, x is a constant (its value
cannot be changed) during the evaluation.
A lambda expression specifies the parameters and the mapping
of a function. The lambda expression is the function itself, which is
nameless. For example, consider the following lambda expression:
(x)x * x * x
Church defined a formal computation model (a formal system for
function definition, function application, and recursion) using
lambda expressions. This is called lambda calculus. Lambda
calculus can be either typed or untyped.
Application of the example lambda expression is denoted as in the
following example:
((x)x * x * x)(2) which results in the value 8.
Lambda expressions, like other function definitions, can have more
than one parameter.
Functional Forms:
A higher-order function, or functional form, is one that either takes
one or more functions as parameters or yields a function as its result, or
both. Two common mathematical functional forms provided by the
schema are:
1. function composition is a functional form that takes two functions
as parameters and yields a function whose value is the first actual
parameter function applied to the result of the second actual
parameter function. For example -Function composition is written as
an expression, using ° as an operator, as in h K f g.
For example, if
f(x) K x + 2
g(x) K 3 * x
then h is defined as
h(x) K f(g(x)), or h(x) K (3 * x) + 2
2. Apply-to-all is a functional form that takes a single function as a
parameter and yields a list of values obtained by applying the given
function to each element of a list of parameters. As an example of
the use of map, suppose we want all of the elements of a list cubed.
Consider the following example:
Let h(x) K x * x
5
Then (h, (2, 3, 4)) yields (4, 9, 16)
We can accomplish this with the following –
(map (LAMBDA (num) ( num num num)) '(2 3 5))
This call returns (8 27 125).
6
2. It has advanced object-oriented features.
3. It has got automatic garbage collection. Hence the programmer
need not have to explicitly free the dynamically allocated memory.
4. A strong support for recursion in programs. Due to this feature this
language is widely used and accepted in artificial intelligence.
5. All computation is performed by applying functions to arguments.
Variable declarations are rarely used.
Applications of LISP:
Lisp is mainly used in-
1. Artificial intelligence robotics
2. 2. Computer games
3. Pattern recognition
4. Real time embedded systems
5. List handling and processing
6. For creating applications for educational purpose.
7. For List handling and processing.
Data Types and Structures:
There were only two categories of data objects in the original LISP: atoms
and lists. Atoms and lists are not types in the sense that imperative
languages have types.
Atoms: All the expression in LISP are made up of list. Atoms are
either symbols, in the form of identifiers, or numeric literals. Space
separate elements of a list are known as ATOMS. Some examples of
atoms are:
name
123
xyz
Lists: Lists are specified in LISP by delimiting their elements with
parentheses. The elements of simple lists are restricted to atoms,
as in (A B C D). Nested list structures are also specified by
parentheses. For example, the list (A (B C) D (E (F G) ) ) is a list of
four elements. The first is the atom A; the second is the sublist (B C); the
third is the atom D; the fourth is the sublist (E (F G)), which has as its
second element the sublist (F G).
List elements are pairs, where the first part is the data of the
element, which is a pointer to either an atom or a nested list. The
second part of a pair can be a pointer to an atom, a pointer to another
element, or the empty list. Elements are linked together in lists with the
second parts. Internally, a list is usually stored as linked list structure in
which each node has two pointers, one to reference the data of the
node and the other to form the linked list. A list is referenced by a
pointer to its first element.
7
The internal representations of our two example lists are shown in
below
8
Another feature of early LISP systems that was apparently
accidental was the use of dynamic scoping. Functions were
evaluated in the environments of their callers. Dynamic scoping was
used for most dialects of LISP before 1975.
Expression in LISP:
The expressions are represented using the operands and operators.
Note that the prefix expression (that is operator operand! operand2)
is required for the evaluation of expression.
>(+10 >(*(+23) (+56))
20) 55
30
If we do not want to evaluate an expression but want to print the
entire expression then the quote operator or' is used. For example
> (quote (+ 10 20))
(+10 20)
The eval operator is used to evaluate an expression.
For instance
> (eval (+ 10 20))
30
Define Function in LISP:
The operator defun is used to define the procedure/function. The syntax
of procedure writing in LISP is
defun (proc_name(argument1 argument2)
body of procedure)
For example - procedure for addition of two numbers is as follows -
>(defun mysum(a b)
(+ a b))
Now we can use this procedure the way we want.
> (mysum 2 3)
5
Ex1: Procedure for calculate cube of number in LISP:
>(defun cube(a) (*(*a a)* a))
Output
> (cube 5)
125
Ex2: Procedure to display sum of Squares:
> (defun square(x)(xx))
> (defun sum-of-square(start end)
(if(> start end)
0
(+(square start) (sum-of-square(+ 1 start) end))
))
9
Output
> (sum-of-square 1 5)
55
Ex3: Procedure to Display the number and Its value after
incrementing It by 10:
>(defun myfun(n) (print n) (+ n 10))
Output
> (myfun 10)
10 20
DEFPARAMETER AND SETF IN LISP:
defparameter: The variable can be defined using defparameter. For
example
> (defparameter a 10)
A
When we type the variable name on the prompt then we get the value
assigned to it. It is as shown below-
>a
10
Setf: The setf is used to change the value of the variable. The setf will
make global variables.
> (self a 100) // changing the value of a from 10 to 100
100
>a // simply testing whether a=100 or not
100
>(+ a 10) // performing some operation with the changed value
110
10
(10 20 30)
The list containing no item is called the empty list. On entering the empty
li it will return the value NIL.
>()
NIL
Using the word list we can also create a list.
> (list 10 20 30)
(10 20 30)
11
(AGE 30)
LISP Functions:
One of the more common uses of the LISP-based programming languages
is list processing. This subsection introduces the Scheme functions for
dealing with lists. Suppose we have a function that has two parameters,
an atom and a list, and the purpose of the function is to determine
whether the given atom is in the given list. Neither the atom nor the list
should be evaluated; they are literal data to be examined. To avoid
evaluating a parameter, it is first given as a parameter to the primitive
function QUOTE, which simply returns it without change. The following
examples illustrate QUOTE:
(QUOTE A) returns A
(QUOTE (A B C)) returns (A B C)
the common abbreviation of the call to QUOTE is used, which is done
simply by preceding the expression to be quoted with an apostrophe (').
Thus, instead of (QUOTE (A B)), '(A B) will be
used.
CDR: The cdr returns rest of the list after the first element is removed. It
is pronounced as could- (CDR '(1 2 3 4)) returns (234)
(CDR '(A B C)) returns (B C)
(CDR '((A B) C D)) returns (C D)
(CDR 'A) is an error
(CDR '(A)) returns ()
(CDR '()) is an error
CAR: The car returns first element of a non-empty list. For example
(CAR '(1 2 3 4)) returns 1
(CAR '(A B C)) returns A
(CAR '((A B) C D)) returns (A B)
(CAR 'A) is an error because A is not a list
(CAR '(A)) returns A
(CAR '()) is an error
CONS: We can combine two lists using cons. The cons function takes two
arguments and returns a new cons cell containing the two values.
Following are example calls to CONS:
(CONS 10 (20 30 40)) returns (10 20 30 40)
(CONS 'A '()) returns (A)
(CONS 'A '(B C)) returns (A B C)
(CONS '() '(A B)) returns (() A B)
(CONS '(A B) '(C D)) returns ((A B) C D)
CAR and CDR take a list apart, and CONS constructs a new list from
given list parts. The two parameters to CONS become the CAR and CDR
of the new list. Thus, if a_list is a list, then
(CONS (CAR a_list) (CDR a_list))
returns a list with the same structure and same elements as a_list
apply CONS to two atoms, although that is legal. The result of such an
12
application is a dotted pair, so named because of the way it is
displayed by Scheme.
For example, consider the following call:
(CONS 'A 'B)
If the result of this is displayed, it would appear as (A . B)
This dotted pair indicates that instead of an atom and a pointer or a
pointer and a pointer, this cell has two atoms.
LIST is a function that constructs a list from a variable number of
parameters. It is a shorthand version of nested CONS functions, as
illustrated in the following:
(LIST 'apple 'orange 'grape) returns (apple orange
grape)
Using CONS, the call to LIST above is written as follows:
(CONS 'apple (CONS 'orange (CONS 'grape '())))
Output:
“Hello"
"Welcome"
13
on-quoting in LISP:
The quoting is used for evaluation of expression. Quoting is used to treat
expressions as data. The symbol ' is used to represent the variable.
For example-
>pi
3.14159
But the
>(quote pi)
or
>'pi
will return pi
LET:
LET is a function that creates a local scope in which names are
temporarily bound to the values of expressions. These names can then
be used in the evaluation of another expression, but they cannot be
rebound to new values in LET.
The following example illustrates the use of LET. It computes the roots
of a given quadratic equation, assuming the roots are real. The
mathematical definitions of the real (as opposed to complex) roots of
the quadratic equation ax2 + bx + c are as follows:
root1 = (-b + sqrt(b2 - 4ac))/2a and root2 = (-b -
sqrt(b2 - 4ac))/2a
This example uses LIST to create the list of the two values that make up
the result.
LET construct:
The syntax of Let construct is as follows -
(let ((varl vall) (var2 val2).. (var valn)), last_val)
Where varl, var2, ..vara are variable names and vall, val2, .. valn are the
initial values assigned to the respective variables.
When let is executed, each variable is assigned the respective value and
lastly the s-expression is evaluated.
The value of the last expression evaluated is returned.
(let (x=2, y=3, z=x+y) print(x + y + z))
This will return 10
14
Factorial of a given number:
(defun factorial (n)
(let ((f 1)) // initialising f=1
(dotimes (x n) // repeating the following block for n times
(setff(*f (1+x)))) //f=f'(x+1)
f)) // returns the value of f each time.
Output
> (factorial 5)
120
Common LISP:
Common LISP (Steele, 1990) was created in an effort to combine the
features of several early 1980s dialects of LISP, including Scheme, into a
single language. Being something of a union of languages, it is quite large
and complex, similar in these regards to C++ and C#. Its basis, however,
is the original LISP, so its syntax, primitive functions, and fundamental
nature come from that language.
Following is the factorial function written in Common LISP:
(DEFUN factorial (x)
(IF (<= n 1)
1
(* n factorial (− n 1)))
))
Only the first line of this function differs syntactically from the Scheme
version of the same function.
The list of features of Common LISP is long:
a large number of data types and structures, including records,
arrays, complex numbers, and character strings.
powerful input and output operations and a form of packages for
modularizing collections of functions and data, and also for
providing access control.
Common LISP includes several imperative constructs, as well as
some mutable types.
15
construct opens a connection to a file and creates a stream object
to represent the connection.
Indefinite extent : The entity continues to exist as long as the
possibility of reference remains. For example- most Common Lisp
data objects have indefinite extent.
16
from operator import add
add5 = partial (add, 5)
The from declaration here imports the functional version of the
addition operator, which is named add, from the operator module.
After defining add5, it can be used with one parameter, as in the
following: add5 (15), This call returns 20.
17
The values can be assigned to some The values are returned from the
variable in imperative languages. functions depending upon the input
given to that function.
The order of execution is very The order of execution is of low
important in imperative languages. importance in functional languages.
The control flow is manipulated by The control flow is manipulated by
conditional statements, loops, functional calls including recursion.
function or method calls.
Examples of imperative languages Examples of functional languages
are C, Java, Python, Ruby, JavaScript are LISP, Haskell, ML and so on.
and so on.
18
let c = 21 in
let cTimes = \x ->c*x in
let c = 5 in
cTimes 2
The result will be 42(21*2) as it uses static scope rule.
19
=fib(n-1)+fib(n-2)
ML:
ML (Milner et al., 1990) is a static-scoped functional programming
language, like Scheme.
Function declarations in ML appear in the general form:
fun function_name(formal parameters) = expression;
When called, the value of the expression is returned by the function.
Actually, the expression can be a list of expressions, separated by
semicolons and surrounded by parentheses. The return value in this
case is that of the last expression.
Consider the following ML function declaration:
fun circumf(r) = 3.14159 * r * r;
This specifies a function named circumf that takes a floating-point
(real in ML) argument and produces a floating-point result.
The ML selection control flow construct is similar to that of the
imperative languages. It has the following general form:
if expression then then_expression else else_expression
The first expression must evaluate to a Boolean value.
The conditional expressions of Scheme can appear at the function
definition level in ML. In ML, the computation performed by a
function can be defined for different forms of the given parameter.
This feature is meant to mimic the form and meaning of conditional
function definitions in mathematics.
Scoping in ML:
The scoping rule used in ML is called lexical scoping, because names
refer to their nearest preceding lexical (textual) definition.
For example - if f() returns 12, then let val x = f() in x * x end is an
expression that evaluates to 144, using a temporary variable named
x to avoid calling f() twice.
20
style syntax.
For example - The function type For example - The function
can be described as type can be described as
f:: Bool-> Int fun f (b:bool):
fb = if b then 1 else 0 int =if b then 1 else 0
21