0% found this document useful (0 votes)
0 views114 pages

Unit - V PPL

The document provides an overview of functional and logic programming languages, focusing on lambda calculus, functional programming fundamentals, and programming with Scheme and LISP. It explains key concepts such as nameless functions, the evaluation of lambda expressions, and the characteristics of purely functional languages. Additionally, it covers the structure of Scheme, including function definitions, primitive numeric functions, and control flow constructs.

Uploaded by

abesalok123
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
0 views114 pages

Unit - V PPL

The document provides an overview of functional and logic programming languages, focusing on lambda calculus, functional programming fundamentals, and programming with Scheme and LISP. It explains key concepts such as nameless functions, the evaluation of lambda expressions, and the characteristics of purely functional languages. Additionally, it covers the structure of Scheme, including function definitions, primitive numeric functions, and control flow constructs.

Uploaded by

abesalok123
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 114

UNIT - V

FUNCTIONAL AND LOGIC PROGRAMMING LANGUAGES


FUNCTIONAL AND LOGIC PROGRAMMING LANGUAGES

Introduction to lambda calculus – fundamentals of


functional programming languages – Programming
with Scheme – Programming with ML –
Introduction to logic and logic programming –
Programming with Prolog – multi-paradigm
languages
Introduction to lambda calculus
Introduction to lambda calculus
● A mathematical function is a mapping of members of one set, called the domain
set, to another set, called the range set.
● A function definition specifies the domain and range sets, either explicitly or
implicitly, along with the mapping.
● Function definitions are often written as a function name, followed by a list of
parameters in parentheses, followed by the mapping expression. For example,
cube(x) ≡ x * x * x, where x is a real number.
● the domain and range sets are the real numbers. The symbol ≡ is used to mean
“is defined as.”
Introduction to lambda calculus
● Lambda notation, as devised by Alonzo Church (1941), provides a method for
defining nameless functions.
● 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
Introduction to lambda calculus
● 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
Introduction to lambda calculus
● 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.
Introduction to lambda calculus
● When a lambda expression is evaluated for a given parameter, the expression is
said to be applied to that parameter.
● The mechanics of such an application are the same as for any function
evaluation.
● 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.
Fundamentals of functional programming languages
Fundamentals of functional programming languages
● In an imperative language, an expression is evaluated and the result is stored
in a memory location, which is represented as a variable in a program. This is
the purpose of assignment statements.
● A program in an assembly language often must also store the results of partial
evaluations of expressions. For example, to evaluate

(x + y)/(a - b)

the value of (x + y) is computed first. That value must then be stored while (a - b) is
evaluated.

● The compiler handles the storage of intermediate results of expression


evaluations in high-level languages.
Fundamentals of functional programming languages
● A purely functional programming language does not use variables or
assignment statements, thus freeing the programmer from concerns related to
the memory cells, or state, of the program.
● Repetition must be specified with recursion rather than with iteration.
● Programs are function definitions and function application specifications, and
executions consist of evaluating function applications.
● Without variables, the execution of a purely functional program has no state in
the sense of operational and denotational semantics.
● The execution of a function always produces the same result when given the
same parameters. This feature is called referential transparency.
Fundamentals of functional programming languages
● A functional language provides a set of primitive functions, a set of functional
forms to construct complex functions from those primitive functions, a function
application operation, and some structure or structures for representing data.
● These structures are used to represent the parameters and values computed
by functions. If a functional language is well defined, it requires only a relatively
small number of primitive functions..
Fundamentals of functional programming languages
● A functional language provides a set of primitive functions, a set of functional
forms to construct complex functions from those primitive functions, a function
application operation, and some structure or structures for representing data.
● These structures are used to represent the parameters and values computed
by functions. If a functional language is well defined, it requires only a relatively
small number of primitive functions.
● the first functional programming language, LISP, uses a syntactic form, for both
data and code, that is very different from that of the imperative languages
Fundamentals of functional programming languages
Data Types and Structures (LISP)

● 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)
Fundamentals of functional programming languages
Data Types and Structures (LISP)

● 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)

Fundamentals of functional programming languages
The First LISP Interpreter

● The original intent of LISP’s design was to have a notation for programs that
would be as close to Fortran’s as possible, with additions when necessary
● This notation was called M-notation, for meta-notation. There was to be a
compiler that would translate programs written in M-notation into semantically
equivalent machine code programs for the IBM 704.
● The first requirement for the universal LISP function was a notation that allowed
functions to be expressed in the same way data was expressed.
Fundamentals of functional programming languages
The First LISP Interpreter

● Function calls were specified in a prefix list form originally called Cambridge
Polish,2 as in the following:

(function_name argument1 … argumentn)

● For example, if + is a function that takes two or more numeric parameters, the
following two expressions evaluate to 12 and 20, respectively:

(+ 5 7)

(+ 3 4 7 6)
Fundamentals of functional programming languages
The First LISP Interpreter

● Function calls were specified in a prefix list form originally called Cambridge
Polish,2 as in the following:

(function_name argument1 … argumentn)

● To allow the binding of functions to names so that functions could be referenced


by other functions and by themselves.
● This name binding was specified by a list consisting of the function name and a
list containing the lambda expression, as in

(function_name (LAMBDA (arg1 … argn) expression))


Programming with Scheme
Programming with Scheme
Origins of Scheme
● The Scheme language, which is a dialect of LISP, was developed at MIT in the
mid-1970s (Sussman and Steele, 1975).
● It is characterized by its small size, its exclusive use of static scoping, and its
treatment of functions as first-class entities.
● As first-class entities, Scheme functions can be the values of expressions,
elements of lists, passed as parameters, and returned from functions.
● Early versions of LISP did not provide all of these capabilities. As an essentially
typeless small language with simple syntax and semantics,
● Scheme is well suited to educational applications, such as courses in
functional programming, and also to general introductions to programming.
● Most of the Scheme code in the following sections would require only minor
modifications to be converted to LISP code.
Programming with Scheme
The Scheme Interpreter
● A Scheme interpreter in interactive mode is an infinite read-evaluate-print loop (often
abbreviated as REPL). It repeatedly reads an expression typed by the user (in the form
of a list), interprets the expression, and displays the resulting value.
● This form of interpreter is also used by Ruby and Python.
● Expressions are interpreted by the function EVAL.
● Literals evaluate to themselves. So, if you type a number to the interpreter, it simply
displays the number.
● Expressions that are calls to primitive functions are evaluated in the following way:
First, each of the parameter expressions is evaluated, in no particular order. Then, the
primitive function is applied to the parameter values, and the resulting value is
displayed.
● Of course, Scheme programs that are stored in files can be loaded and interpreted.
● Comments in Scheme are any text following a semicolon on any line.
Programming with Scheme
Primitive Numeric Functions

● Scheme includes primitive functions for the basic arithmetic operations.


● These are +, −, *, and /, for add, subtract, multiply, and divide. * and + can have
zero or more parameters.
● If * is given no parameters, it returns 1; if + is given no parameters, it returns 0.
● + adds all of its parameters together.
● * multiplies all its parameters together.
● / and − can have two or more parameters.
● In the case of subtraction, all but the first parameter are subtracted from the first.
● Division is similar to subtraction
Programming with Scheme
Primitive Numeric Functions

Expression Value

42 42

(* 3 7) 21

(+ 5 7 8) 20

(− 5 6) −1

(− 15 7 2) 6

(− 24 (* 4 3)) 12
Programming with Scheme
Primitive Numeric Functions

● There are a large number of other numeric functions in Scheme, among them
MODULO, ROUND, MAX, MIN, LOG, SIN, and SQRT.
● SQRT returns the square root of its numeric parameter, if the parameter’s value
is not negative. If the parameter is negative, SQRT yields a complex number.
● In Scheme, note that we use uppercase letters for all reserved words and
predefined functions.
● If a function has a fixed number of parameters, such as SQRT, the number of
parameters in the call must match that number. If not, the interpreter will produce
an error message.
Programming with Scheme
Defining Functions

● A Scheme program is a collection of function definitions. Consequently,


knowing how to define these functions is a prerequisite to writing the simplest
program.
● In Scheme, a nameless function actually includes the word LAMBDA, and is
called a lambda expression. For example,

(LAMBDA (x) (* x x))

is a nameless function that returns the square of its given numeric parameter.
Programming with Scheme
Defining Functions

● This function can be applied in the same way that named functions are: by
placing it in the beginning of a list that contains the actual parameters. For
example, the following expression yields 49:

((LAMBDA (x) (* x x)) 7)

● In this expression, x is called a bound variable within the lambda expression.


During the evaluation of this expression, x is bound to 7.
Programming with Scheme
Defining Functions

● A bound variable never changes in the expression after being bound to an


actual parameter value at the time evaluation of the lambda expression begins.
● Lambda expressions can have any number of parameters. For example, we
could have the following:

(LAMBDA (a b c x) (+ (* a x x) (* b x) c))
Programming with Scheme
Defining Functions

The Scheme special form function DEFINE serves two fundamental needs of
Scheme programming:

● to bind a name to a value and


● to bind a name to a lambda expression.

The form of DEFINE that binds a name to a value may make it appear that DEFINE
can be used to create imperative language–style variables.
Programming with Scheme
Defining Functions

● The simplest form of DEFINE is one used to bind a name to the value of an
expression. This form is

(DEFINE symbol expression)


Programming with Scheme
Defining Functions

● The simplest form of DEFINE is one used to bind a name to the value of an
expression. This form is

(DEFINE symbol expression)

For example,
(DEFINE pi 3.14159)
(DEFINE two_pi (* 2 pi))
Programming with Scheme
Defining Functions

● The simplest form of DEFINE is one used to bind a name to the value of an
expression. This form is

(DEFINE symbol expression)

● This form of DEFINE is analogous to a declaration of a named constant in an


imperative language.
● In Java, the equivalents to the above defined names are as follows:

final float PI = 3.14159;

final float TWO_PI = 2.0 * PI;


Programming with Scheme
Defining Functions

● The simplest form of DEFINE is one used to bind a name to the value of an
expression. This form is

(DEFINE symbol expression)

● Names in Scheme can consist of letters, digits, and special characters except
parentheses; they are case insensitive and must not begin with a digit
Programming with Scheme
Defining Functions
● The second use of the DEFINE function is to bind a lambda expression to a
name
● In this case, the lambda expression is abbreviated by removing the word
LAMBDA.
● To bind a name to a lambda expression, DEFINE takes two lists as parameters.
● The first parameter is the prototype of a function call, with the function name
followed by the formal parameters, together in a list. The second list contains an
expr.
(DEFINE (function_name parameters)
(expression)
)
Programming with Scheme
Defining Functions
● (DEFINE (function_name parameters)
(expression)
)
● (DEFINE (square number) (* number number))
(square 5)
● (DEFINE (hypotenuse side1 side2)
(SQRT(+(square side1)(square side2)))
)
Programming with Scheme
Output Functions

● Scheme includes a few simple output functions, but when used with the
interactive interpreter, most output from Scheme programs is the normal output
from the interpreter, displaying the results of applying EVAL to top-level
functions.
● Scheme includes a formatted output function, PRINTF, which is similar to the
printf function of C.
● Note that explicit input and output are not part of the pure functional
programming model
Programming with Scheme
Numeric Predicate Functions
● A predicate function is one that returns a Boolean value (some representation of either
true or false). Scheme includes a collection of predicate functions for numeric data.
● Among them are the following:
Function Meaning
= Equal
<> Not equal
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
EVEN? Is it an even number?
ODD? Is it an odd number?
ZERO? Is it zero?
● Notice that the names for all predefined predicate functions that have words for names
end with question marks
Programming with Scheme
Numeric Predicate Functions

● A predicate function is one that returns a Boolean value (some representation of


either true or false). Scheme includes a collection of predicate functions for
numeric data.
● In Scheme, the two Boolean values are #T and #F (or #t and #f), although
some implementations use the empty list for false.The Scheme predefined
predicate functions return the empty list, (), for false.
● The NOT function is used to invert the logic of a Boolean expression.
Programming with Scheme
Control Flow
Scheme uses three different constructs for control flow:
● one similar to the selection construct of the imperative languages and two based
on the evaluation control used in mathematical functions.
Programming with Scheme
Control Flow
Scheme uses three different constructs for control flow:
● The Scheme two-way selector function, named IF, has three parameters: a
predicate expression, a then expression, and an else expression. A call to IF has
the form
(IF predicate then_expression else_expression)
(DEFINE (factorial n) (IF (<= n 1) 1 (* n (factorial (− n 1)))))
Programming with Scheme
Control Flow
Scheme uses three different constructs for control flow:
● The Scheme multiple selector, which is based on mathematical conditional
expressions, is a special form function named COND.
● COND is a slightly generalized version of the mathematical conditional
expression; it allows more than one predicate to be true at the same time.
● Because different mathematical conditional expressions have different numbers
of parameters, COND does not require a fixed number of actual parameters.
● Each parameter to COND is a pair of expressions in which the first is a predicate
(it evaluates to either #T or #F).
The general form of COND is
(COND
(predicate1 expression1)
(predicate2 expression2)
...
(predicaten expressionn)
[(ELSE expressionn+1)]
)
Programming with Scheme
Control Flow
Scheme uses three different constructs for control flow:
● Following is an example of a simple function that uses COND:
(DEFINE (leap? year)
(COND
((ZERO? (MODULO year 400)) #T)
((ZERO? (MODULO year 100)) #F)
(ELSE (ZERO? (MODULO year 4)))
))
Programming with Scheme
Control Flow
Scheme uses three different constructs for control flow:
● The third Scheme control mechanism is recursion, which is used, as in
mathematics, to specify repetition
Programming with Scheme
List Functions
● One of the more common uses of the LISP-based programming languages is list
processing
● Scheme programs are interpreted by the function application function, EVAL.
When applied to a primitive function, EVAL first evaluates the parameters of the
given function.
● 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.
● 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)
Programming with Scheme
List Functions
The fundamental list operations in Scheme are two functions that take lists apart and
two that build lists.
● The CAR function returns the first element of its list parameter. For example,
consider the following example:
(CAR '(A B C)) This call to CAR returns A.
● The CDR function returns its parameter list minus its first element. For
example, consider the following example:
(CDR '(A B C)) This function call returns the list (B C).
Programming with Scheme
List Functions
The fundamental list operations in Scheme are two functions that take lists apart and
two that build lists.
(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
(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
Programming with Scheme
List Functions
● The 704’s memory words had two fields, named decrement and address, that
were used in various operand addressing strategies.
● Each of these fields could store a machine memory address. The 704 also
included two machine instructions, also named CAR (contents of the address
part of a register) and CDR (contents of the decrement part of a register), that
extracted the associated fields.
Programming with Scheme
List Functions
● Some of the most commonly used functional compositions in Scheme are built in
as single functions.
For example, (CAAR x) is equivalent to (CAR(CARx)),
(CADR x) is equivalent to (CAR (CDR x)), and
(CADDAR x) is equivalent to (CAR (CDR (CDR (CAR x)))).
Programming with Scheme
List Functions
● Some of the most commonly used functional compositions in Scheme are built in
as single functions.
For example, (CAAR x) is equivalent to (CAR(CARx)),
(CADR x) is equivalent to (CAR (CDR x)), and
(CADDAR x) is equivalent to (CAR (CDR (CDR (CAR x)))).
As an example, consider the following evaluation of CADDAR:
(CADDAR '((A B (C) D) E)) = (CAR (CDR (CDR (CAR '((A B (C) D) E)))))
= (CAR (CDR (CDR '(A B (C) D))))
= (CAR (CDR '(B (C) D)))
= (CAR '((C) D))
= (C)
Programming with Scheme
List Functions
● The function CONS takes two parameters and returns a new list with its first
parameter as the first element and its second parameter as the remainder of that
list.
● For example, consider the following:
(CONS 'A '(B C))
This call returns the new list (A B C).
Programming with Scheme
List Functions
● The function CONS takes two parameters and returns a new list with its first
parameter as the first element and its second parameter as the remainder of that
list.
● (CONS 'A '()) returns (A)
Programming with Scheme
List Functions
● The function CONS takes two parameters and returns a new list with its first
parameter as the first element and its second parameter as the remainder of that
list.
● (CONS 'A '(B C)) returns (A B C)
Programming with Scheme
List Functions
● The function CONS takes two parameters and returns a new list with its first
parameter as the first element and its second parameter as the remainder of that
list.
● (CONS '() '(A B)) returns (() A B)
Programming with Scheme
List Functions
● The function CONS takes two parameters and returns a new list with its first
parameter as the first element and its second parameter as the remainder of that
list.
● (CONS '(A B) '(C D)) returns ((A B) C D)
Programming with Scheme
List Functions
● The LIST function takes any number of parameters and returns a new list
with the parameters as its elements. For example, consider the following call
to LIST:
(LIST 'A 'B '(C D))
This call returns the new list (A B (C D)).
Programming with Scheme
Predicate Functions for Symbolic Atoms and Lists
Scheme has three fundamental predicate functions, EQ?, NULL?, and LIST?, for
symbolic atoms and lists.
● The EQ? function takes two expressions as parameters, although it is usually
used with two symbolic atom parameters. It returns #T if both parameters have
the same pointer value—that is, they point to the same atom or list; otherwise, it
returns #F.
● If the two parameters are symbolic atoms, EQ? returns #T if they are the same
symbols (because Scheme does not make duplicates of symbols); otherwise #F
Programming with Scheme
Predicate Functions for Symbolic Atoms and Lists
Scheme has three fundamental predicate functions, EQ?, NULL?, and LIST?, for
symbolic atoms and lists.
● The LIST? predicate function returns #T if its single argument is a list and
#F otherwise, as in the following examples:
(LIST? '(X Y)) returns #T
(LIST? 'X) returns #F
(LIST? '()) returns #T
Programming with Scheme
Predicate Functions for Symbolic Atoms and Lists
Scheme has three fundamental predicate functions, EQ?, NULL?, and LIST?, for
symbolic atoms and lists.
● The NULL? function tests its parameter to determine whether it is the empty
list and returns #T if it is.
Consider the following examples:
(NULL? '(A B)) returns #F
(NULL? '()) returns #T
(NULL? 'A) returns #F
(NULL? '(())) returns #F
The last call yields #F because the parameter is not the empty list
Programming with Scheme
Example Scheme Functions
● Consider the problem of membership of a given atom in a given list that
does not include sublists. Such a list is called a simple list.
● If the function is named member, it could be used as follows:
(member 'B '(A B C)) returns #T
(member 'B '(A C D E)) returns #F
Programming with ML
Programming with ML
● ML is a static-scoped functional programming language, like Scheme.
● However, it differs from LISP and its dialects, including Scheme, in a number of
significant ways.
● One important difference is that ML is a strongly typed language, whereas
Scheme is essentially typeless.
● ML has type declarations for function parameters and the return types of
functions, although because of its type inferencing they are often not used.
Programming with ML
● It does have identifiers, which have the appearance of names of variables in
imperative languages. However, these identifiers are best thought of as names
for values.
● Once set, they cannot be changed. They are like the named constants of
imperative languages like final declarations in Java.
● ML identifiers do not have fixed types—any identifier can be the name of a
value of any type
Programming with ML
● A table called the evaluation environment stores the names of all implicitly and
explicitly declared identifiers in a program, along with their types. This is like a
run-time symbol table.
● When an identifier is declared, either implicitly or explicitly, it is placed in the
evaluation environment
● Another important difference between Scheme and ML is that ML uses a syntax
that is more closely related to that of an imperative language than that of LISP
Programming with ML
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.


Programming with ML
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.

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 types are inferred from the type of the literal in the expression.
Programming with ML
Function declarations in ML appear in the general form

fun function_name(formal parameters) = expression;

The function

fun times10(x) = 10 * x;

the argument and functional value are inferred to be of type int.


Programming with ML
Function declarations in ML appear in the general form

fun function_name(formal parameters) = expression;

Consider the following ML function:

fun square(x) = x * x;

ML determines the type of both the parameter and the return value from the *
operator in the function definition. Because this is an arithmetic operator, the type of
the parameter and the function are assumed to be numeric. In ML, the default
numeric type is int. So, it is inferred that the type of the parameter and the return
value of square is int.
Programming with ML
Function declarations in ML appear in the general form

fun function_name(formal parameters) = expression;

Consider the following ML function:

fun square(x) = x * x;

square(2.75); it would cause an error, because ML does not coerce real values to int
type
Programming with ML
Function declarations in ML appear in the general form

fun function_name(formal parameters) = expression;

Consider the following ML function:

fun square(x) = x * x;

fun square(x) : real = x * x;

Because ML does not allow overloaded functions, this version could not coexist
with the earlier int version. The last version defined would be the only one.
Programming with ML
Function declarations in ML appear in the general form

fun function_name(formal parameters) = expression;

Consider the following ML function:

fun square(x) = x * x;

fun square(x) : real = x * x;

Each of the following definitions is also legal:

fun square(x : real) = x * x;

fun square(x) = (x : real) * x;

fun square(x) = x * (x : real);


Programming with ML
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.


Programming with ML
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.

fun fact(n : int): int = if n <= 1 then 1 else n * fact(n − 1);


Programming with ML
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

In ML, the particular expression that defines the return value of a function is chosen
by pattern matching against the given parameter.

Multiple definitions of a function can be written using parameter pattern matching.

fun fact(0) = 1

| fact(1) = 1

| fact(n : int): int = n * fact(n − 1);


Programming with ML
ML supports lists and list operations

● Lists are specified in square brackets, with the elements separated by commas,
as in the following list of integers: [5, 7, 9]
● [] is the empty list, which could also be specified with nil
● The Scheme CONS function is implemented as a binary infix operator in ML,
represented as ::. For example, 3 :: [5, 7, 9] returns the following new list: [3, 5,
7, 9].
● ML has functions that correspond to Scheme’s CAR and CDR, named hd (head)
and tl (tail). For example, hd [5, 7, 9] is 5 tl [5, 7, 9] is [7,9]
Programming with ML
ML supports lists and list operations

● For example, in a formal parameter, the expression


(h :: t)
is actually two formal parameters, the head and tail of given list parameter, while the
single corresponding actual parameter is a list.
● For example, the number of elements in a given list can be computed with the
following function:

fun length([]) = 0
| length(h :: t) = 1 + length(t);
Programming with ML
● In ML, names are bound to values with value declaration statements of the form
val new_name = expression;
For example, val distance = time * speed;
Programming with ML
● In ML, names are bound to values with value declaration statements of the form
val new_name = expression;
For example, val distance = time * speed;
● The val statement binds a name to a value, but the name cannot be later
rebound to a new value.
● Actually, if you do rebind a name with a second val statement, it causes a new
entry in the evaluation environment that is not related to the previous version of
the name.
● the old evaluation environment entry (for the previous binding) is no longer
visible
Programming with ML
The normal use of val is in a let expression.

Consider the following example:

let
val radius = 2.7
val pi = 3.14159
in
pi * radius * radius
end;
Programming with ML
ML supports lists and list operations

● ML includes several higher-order functions that are commonly used in functional


programming.
● Among these are a filtering function for lists, filter, which takes a predicate
function as its parameter.
● The predicate function is often given as a lambda expression, which in ML is
defined exactly like a function, except with the fn reserved word, instead of fun,
and of course the lambda expression is nameless.

Programming with ML
ML supports lists and list operations

● filter returns a function that takes a list as a parameter. It tests each element of
the list with the predicate. Each element on which the predicate returns true is
added to a new list, which is the return value of the function.
filter(fn(x) => x < 100, [25, 1, 50, 711, 100, 150, 27, 161, 3]);

[25, 1, 50, 27, 3]


Programming with ML
ML supports lists and list operations

● The map function takes a single parameter, which is a function.


● The resulting function takes a list as a parameter. It applies its function to each
element of the list and returns a list of the results of those applications.
● Consider the following code:
fun cube x = x * x * x;
val cubeList = map cube;
val newList = cubeList [1, 3, 5];
After execution, the value of newList is [1, 27, 125]
Programming with ML
ML supports lists and list operations

● The map function takes a single parameter, which is a function.


● The resulting function takes a list as a parameter. It applies its function to each
element of the list and returns a list of the results of those applications.
● Consider the following code:
fun cube x = x * x * x;
val cubeList = map cube;
val newList = cubeList [1, 3, 5];
After execution, the value of newList is [1, 27, 125]

val newList = map (fn x => x * x * x, [1, 3, 5]);


Programming with ML
ML supports lists and list operations

● ML has a binary operator for composing two functions, o (a lowercase “oh”).


● To build a function h that first applies function f and then applies function g to the
returned value from f, we could use the following: val h = g o f;
Programming with ML
ML supports lists and list operations

● ML functions take a single parameter.


● When a function is defined with more than one parameter, ML considers the
parameters to be a tuple, even though the parentheses that normally delimit a
tuple value are optional.
● The commas that separate the parameters (tuple elements) are required.
Programming with ML
ML supports lists and list operations

● The process of currying replaces a function with more than one parameter
with a function with one parameter that returns a function that takes the other
parameters of the initial function
● ML functions that take more than one parameter can be defined in curried form
by leaving out the commas between the parameters (and the delimiting
parentheses).For example, we could have the following:
fun add a b = a + b;
add 3 5;
This call to add returns 8,
Programming with ML
ML supports lists and list operations

● Curried functions are interesting and useful because new functions can be
constructed from them by partial evaluation.
● Partial evaluation means that the function is evaluated with actual parameters for
one or more of the leftmost formal parameters.
● For example, we could define a new function as follows:
fun add5 x = add 5 x;
val num = add5 10;
Programming with ML
ML supports lists and list operations

● Curried functions also can be written in Scheme, Haskell, and F#. Consider the
following Scheme function:
(DEFINE (add x y) (+ x y))
● A curried version of this would be as follows:
(DEFINE (add y) (LAMBDA (x) (+ y x)))
● This can be called as follows:
((add 3) 4)
Programming with ML
ML supports lists and list operations

● ML has enumerated types, arrays, and tuples.


● ML also has exception handling and a module facility for implementing abstract
data types.
● ML has had a significant impact on the evolution of programming languages.
● For language researchers, it has become one of the most studied languages.
● Furthermore, it has spawned several subsequent languages, among them
Haskell, Caml, OCaml, and F#.
Introduction to logic and logic programming –
Introduction to logic and logic programming –
● The approach is to express programs in a form of symbolic logic and use a
logical inference process to produce results.
● Programs in logic programming languages are collections of facts and rules.
● Programming that uses a form of symbolic logic as a programming language
is often called logic programming, and languages based on symbolic logic are
called logic programming languages, or declarative languages.
● Languages used for logic programming are called declarative languages,
because programs written in them consist of declarations rather than
assignments and control flow statements
Programming with Prolog
● Alain Colmerauer and Phillippe Roussel at the University of Aix-Marseille, with
some assistance from Robert Kowalski at the University of Edinburgh, developed
the fundamental design of Prolog.
● Prolog programs consist of collections of statements. There are only a few
kinds of statements in Prolog, but they can be complex.
Programming with Prolog
Terms
● A Prolog term is a constant, a variable, or a structure.
● A constant is either an atom or an integer.
● Atoms are the symbolic values of Prolog and are similar to their counterparts in
LISP.
● In particular, an atom is either a string of letters, digits, and underscores that
begins with a lowercase letter or a string of any printable ASCII characters
delimited by apostrophes
● A variable is any string of letters, digits, and underscores that begins with an
uppercase letter or an underscore ( _ ). Variables are not bound to types by
declarations. The binding of a value, and thus a type, to a variable is called an
instantiation. Instantiation occurs only in the resolution process.
● A variable that has not been assigned a value is called uninstantiated.
Programming with Prolog
Terms

● The last kind of term is called a structure. Structures represent the atomic
propositions of predicate calculus, and their general form is the same:

functor(parameter list)

The functor is any atom and is used to identify the structure. The parameter list can
be any list of atoms, variables, or other structures..
Programming with Prolog
Fact Statements

● Prolog has two basic statement forms; these correspond to


● the headless and
● headed Horn clauses of predicate calculus.
Programming with Prolog
Fact Statements

● The simplest form of headless Horn clause in Prolog is a single structure, which
is interpreted as an unconditional assertion, or fact. Logically, facts are simply
propositions that are assumed to be true.

female(shelley).
male(bill).
female(mary).
male(jake).
father(bill, jake).
father(bill, shelley).
mother(mary, jake).
mother(mary, shelley).
Programming with Prolog
Rule Statements

● This form can be related to a known theorem in mathematics from which a


conclusion can be drawn if the set of given conditions is satisfied. The right side
is the antecedent, or if part, and the left side is the consequent, or then part.
● If the antecedent of a Prolog statement is true, then the consequent of the
statement must also be true.
● Because they are Horn clauses, the consequent of a Prolog statement is a single
term, while the antecedent can be either a single term or a conjunction.
Programming with Prolog
Rule Statements

● Conjunctions contain multiple terms that are separated by logical AND


operations.
● In Prolog, the AND operation is implied. The structures that specify atomic
propositions in a conjunction are separated by commas, so one could consider
the commas to be AND operators
The general form of the Prolog headed Horn clause statement is
consequence :- antecedent_expression.
ancestor(mary, shelley) :- mother(mary, shelley).
states that if mary is the mother of shelley, then mary is an ancestor of shelley.
Headed Horn clauses are called rules, because they state rules of implication
between propositions.
Programming with Prolog
Rule Statements

The following demonstrates the use of variables in Prolog statements:


parent(X, Y) :- mother(X, Y).
parent(X, Y) :- father(X, Y).
grandparent(X, Z) :- parent(X, Y) , parent(Y, Z).
These statements give rules of implication among some variables, or universal
objects. In this case, the universal objects are X, Y, and Z. The first rule states that if
there are instantiations of X and Y such that mother(X, Y) is true, then for those same
instantiations of X and Y, parent(X, Y) is true.
Programming with Prolog
Rule Statements

● The = operator, which is an infix operator, succeeds if its two term operands are
the same. For example, X = Y.
● The not operator, which is a unary operator, reverses its operand, in the sense
that it succeeds if its operand fails.
For example, not(X = Y) succeeds if X is not equal to Y
Programming with Prolog
Goal Statements

● The Prolog statements for logical propositions, which are used to describe both
known facts and rules that describe logical relationships among facts
● These statements are the basis for the theorem-proving model.
● The theorem is in the form of a proposition that we want the system to either
prove or disprove.
● In Prolog, these propositions are called goals, or queries.
● The syntactic form of Prolog goal statements is identical to that of headless Horn
clauses. For example, we could have
man(fred).
to which the system will respond either yes or no.
Programming with Prolog
The Inferencing Process of Prolog
● Queries are called goals.
● When a goal is a compound proposition, each of the facts (structures) is called a
subgoal.
● To prove that a goal is true, the inferencing process must find a chain of
inference rules and/or facts in the database that connect the goal to one or more
facts in the database
● For example, if Q is the goal, then either Q must be found as a fact in the
database or the inferencing process must find a fact P1 and a sequence of
propositions P2, P3, c, Pn such that P2 :- P1
P 3 :- P2
...
Q :- P n
Programming with Prolog
The Inferencing Process of Prolog

● Because the process of proving a subgoal is done through a proposition


matching process, it is sometimes called matching. In some cases, proving a
subgoal is called satisfying that subgoal
● Consider the following query:
man(bob).
If, however, the database contains the following fact and inference rule,
father(bob).
man(X) :- father(X)
Programming with Prolog
The Inferencing Process of Prolog

● There are two opposite approaches to attempting to match a given goal to a fact
in the database. The system can begin with the facts and rules of the database
and attempt to find a sequence of matches that lead to the goal. This approach
is called bottom-up resolution, or forward chaining.
● The alternative is to begin with the goal and attempt to find a sequence of
matching propositions that lead to some set of original facts in the database. This
approach is called top-down resolution, or backward chaining
Programming with Prolog
The Inferencing Process of Prolog

● The last feature of Prolog’s resolution mechanism that must be discussed is


backtracking.
● When a goal with multiple subgoals is being processed and the system fails to
show the truth of one of the subgoals, the system abandons the subgoal it
cannot prove. It then reconsiders the previous subgoal, if there is one, and
attempts to find an alternative solution to it. This backing up in the goal to the
reconsideration of a previously proven subgoal is called backtracking.
Programming with Prolog
Simple Arithmetic
● Prolog supports integer variables and integer arithmetic.
● Originally, the arithmetic operators were functors, so that the sum of 7 and the
variable X was formed with
+(7, X)
● Prolog now allows a more abbreviated syntax for arithmetic with the is operator.
● This operator takes an arithmetic expression as its right operand and a variable
as its left operand
● A is B / 17 + C.
Programming with Prolog
Simple Arithmetic
● Prolog does not have assignment statements in the same sense as imperative languages.
● Suppose we know the average speeds of several automobiles on a particular racetrack and the amount
of time they are on the track. This basic information can be coded as facts, and the relationship between
speed, time, and distance can be written as a rule, as in the following:
speed(ford, 100).
speed(chevy, 105).
speed(dodge, 95).
speed(volvo, 80).
time(ford, 20).
time(chevy, 21).
time(dodge, 24).
time(volvo, 24).
distance(X, Y) :- speed(X, Speed),
time(X, Time),
Y is Speed * Time.
Now, queries can request the distance traveled by a particular car. For example, the query
distance(chevy, Chevy_Distance).
instantiates Chevy_Distance with the value 2205.
Programming with Prolog
Simple Arithmetic
● At this point it is instructive to take an operational look at how a Prolog system
produces results.
● Prolog has a built-in structure named trace that displays the instantiations of
values to variables at each step during the attempt to satisfy a given goal.
● trace is used to understand and debug Prolog programs.
● To understand trace, it is best to introduce a different model of the execution of
Prolog programs, called the tracing model.
Programming with Prolog
Simple Arithmetic
The tracing model describes Prolog execution in terms of four events:
(1) call, which occurs at the beginning of an attempt to satisfy a goal,
(2) exit, which occurs when a goal has been satisfied,
(3) redo, which occurs when backtrack causes an attempt to resatisfy a goal, and
(4) fail, which occurs when a goal fails.
Programming with Prolog
Simple Arithmetic
trace.
distance(chevy, Chevy_Distance).
(1) 1 Call: distance(chevy, _0)?
(2) 2 Call: speed(chevy, _5)?
(2) 2 Exit: speed(chevy, 105)
(3) 2 Call: time(chevy, _6)?
(3) 2 Exit: time(chevy, 21)
(4) 2 Call: _0 is 105*21?
(4) 2 Exit: 2205 is 105*21
(1) 1 Exit: distance(chevy, 2205)

Chevy_Distance = 2205
Programming with Prolog
List Structures
● Prolog uses the syntax of ML and Haskell to specify lists. The list elements are
separated by commas, and the entire list is delimited by square brackets, as in
[apple, prune, grape, kumquat]
● The notation [] is used to denote the empty list
● Prolog simply uses a special notation.
● [X | Y] denotes a list with head X and tail Y, where head and tail correspond to
CAR and CDR in LISP.
● A list can be created with a simple structure, as in
new_list([apple, prune, grape, kumquat]).

Programming with Prolog
List Structures
● The | operator used to dismantle lists can also be used to create lists from given
instantiated head and tail components, as in
[Element_1 | List_2]
● If Element_1 has been instantiated with pickle and List_2 has been instantiated
with [peanut, prune, popcorn], the sample notation will create, for this one
reference, the list [pickle, peanut, prune, popcorn].
Programming with Prolog
List Structures
● The first two parameters to the append operation in the following code are the
two lists to be appended, and the third parameter is the resulting list:
append([], List, List).
append([Head | List_1], List_2, [Head | List_3]) :-
append(List_1, List_2, List_3).
● Suppose we need to be able to determine whether a given symbol is in a given
list. A straightforward Prolog description of this is
member(Element, [Element | _]).
member(Element, [_ | List]) :- member(Element, List).
Programming with Prolog
Deficiencies of Prolog
● Resolution Order Control
● The Closed-World Assumption
● The Negation Problem
● Intrinsic Limitations
– multi-paradigm languages

You might also like