Title: Functional Languages
1Functional Languages
- Early in AI research, there was a need for
symbolic computing - handling symbols instead of numbers or strings
- parsing input
- recursion
- A functional approach was taken
- programs would comprise functions and function
calls, utilizing recursion as much as possible - rather than a series of imperative statements
with local variables, controlled through loops - The earliest functional language was not Lisp,
but Lisp was the successful language to come out
of this research - Some details on Lisp
- Lisp began as a purely functional language
although it picked up many imperative features
(loops, go to statements, local vars, etc) from
other languages over time - Lisp is interpreted (although compilers later
became available) leading to the ability to
develop programs in a piecemeal fashion - eventually, Lisp compilers for better performance
- Lisp initially was dynamically scoped but later
Lisp was statically scoped - Lisp uses only implicit heap dynamic variables
2Functional Forms
- A function maps from a domain set to a range set
- the range of these two sets is determined by the
function - A functional form is a higher-order function,
that is, a function that applies to functions - this can either be a function that accepts a
function as a parameter, or a function that
returns a function - these are important in Lisp as they allow
functions to generate new functions (that is,
code that can produce code) - Construction
- a functional form in which the result of one
function is applied to a second function (nested
functions) - example f(x) x 2, g(x) 3 x, h(x) f o
g f(g(x)) 3(x2) - Apply to all
- a functional form in which the function is
applied to a list of parameters, returning a list
of values - example (using f from above) apply(f(2, 5, 12))
(4, 10, 14)
3LISP
- A typeless language we do not declare
variables, and variables are merely pointers that
can point to any type of datum - In early Lisp, there were no local variables,
only parameters - Data Structures are either atoms and lists
- atom a literal value (char, string, number)
usually denoted as atom (as in a or buckeye or
red) - list nil or ( x ) where x is an atom, a series
of atoms, or a sub list, or any combination of
these - example of a list (A (B C D) E F)
- In Lisp, functions are stored in lists and
written in prefix notation - form of a Lisp function is (function_name
param1 param2 ) - examples
- ( 5 7) ? returns 12
- (equal? a b) returns T (true) if a b, nil
(false) otherwise - (/ ( a b) (- c d)) returns (a b) / (c d)
- note the nested parens, this will take some
getting used to! - (cube x) ? returns x3
- Functions can also be written as Lambda
expressions - a Lambda expression is an unnamed function in
which the parameter(s) is(are) specified after
the function - e.g. (lambda (x) ( x x)) (5) ?25
4Representing Lists
The CDR of a node can be equal to nil
(equivalent to null or NULL in Java and C) Cs
Fs CDRs are nil
5Lisp and the Eval Function
- Lisps primary function is EVAL which evaluates a
list or atom at run-time - functions are special cases of lists they are
lists whose car is a function name and whose cdr
is a list of parameters - eval will evaluate the function name on the
parameters - that is, (eval lis) does (eval (car lis) (cdr
lis)) - this makes Lisp an interpreted language the
eval function is the interpreted - original Lisp was supposed to be similar to
FORTRAN in its appearance, but this was not
practical - lists are known as s-expressions (symbolic
expressions) and can represent either data or
functions - note that evaluating a list which is not a
function will return an error - for instance if lis1 is (a b c d) then (eval
lis1) would attempt to evaluate a on (b c d) but
a is an atom, not a function! - to define a function, we use the following
notation - (function_name (LAMBDA (arg1 argn) expression))
- where function_name is the name of the function
to be called by other functions and where
expression consists of lists of Lisp function
calls
6Example Math Functions in Lisp
(quote a) or a means to take something
literally, do not evaluate it A without the
would be interpreted as a variable, but if A has
not already been assigned a value, we get
- 42 returns 42
- 42 returns 42
- A returns an error but A returns A
- ( 3 7) returns 21
- ( 5 7 8) returns 20
- (- 5 6) returns -1
- (- 15 7 2) returns 6
- (- 24 ( 4 3)) returns 12
- (gt 5 6) returns nil (false)
- ( 5 ( 2 3)) returns t (true)
- ( ( 5 4) (- 8 (/ 6 2)) returns 45
an error for trying to access it
We will use quote to define atoms and initialize
lists without using a quote, the list is
evaluated as a function call where the first
item in the list is a function name and
the remainder are parameters, but this would
yield an error because the first item would not
be a function name but a datum (probably an atom)
7Variables vs. Symbols and Lists
- Variables point to data items which are either
symbols (similar in ways to strings) or lists - a variable is denoted by its name (e.g., A)
- An atom or list is denoted using a quote mark as
in A or (A B C) - To create a symbol, use the function QUOTE as in
(QUOTE A) which returns A or use the shorthand
A - note that has a different meaning in Lisp to
as the is used in macro definitions, something
we will briefly discuss later - Notice that ( 3 5) does not return 8, it
returns the literal list ( 3 5) because says
do not evaluate this - we could evaluate this list by doing (eval ( 3
5)) which would return 8 - so the will allow us to construct a list
whether the list stores data or code, and the
eval function will allow us to execute a list
that is code
8Scheme
- Lisp was a pioneering programming language in AI
and functional programming, but it had many
problems - the lack of variables led to the programmer
having to be tricky in how to use recursion - dynamic scoping was also a problem
- Scheme was one of many versions of Lisp that
added functionality to the language - Scheme emerged from MIT in the mid 70s
- characterized by a small subset of Lisp features,
static scoping and classes - Scheme continues to be taught today as an example
of a functional language although Scheme itself
has not been used much in AI (AI tends to use
Common Lisp today if it uses any version of Lisp
at all) - We concentrate our examination on Lisp by looking
at Scheme and later Common Lisp rather than Lisp
itself because Lisp is awkward and difficult
9Some Functions in Scheme
- CAR content of address register (returns first
item of a list) - CDR content of decrement register (returns
remainder of list) - EQ? equal (for atoms or lists, returns T or
nil) - MEMBER membership (is a given item in this
list?) - ATOM? is the datum an atom? (T or nil)
- LIST? is the datum a list? (T or nil)
- NULL? is the datum a nil pointer or something?
(T or nil) - EVEN?, ODD?, ZERO? self explanatory (T or nil)
- , ltgt, lt, gt, lt, gt self explanatory (T or nil)
- CONS a list constructor (well go into more
detail later) - LIST an alternative list constructor (returns a
list) - IF statement (IF (condition) (then clause)
(else clause) ) - returns whatever the then clause or else clause
returns - COND conditional statement used like a nested
if-else structure (well see more examples of
this momentarily)
10Assignments in Scheme
- Scheme has 3 different ways of assigning values
- set!, define, let
- (set! a b) is essentially a b, although in this
case, it is a pointer assignment where a points
to b - if b is a literal, then a points to a memory
location storing the value of b, otherwise a
points at the same memory location as what b
points to - (set! a (a b c)) creates the list of a, b and c
and has the variable a point at the list - (set! x ( 6 (- 8 ( 2 1)))) computes the value
(6 (8 (2 1))) or 12, stores it in memory
and has x point at that memory location - (set! x ( 6 5)) points x at the list ( 6 5)
but not 11 - if we do then do (eval x), we get 11
- (define a b) is used to bind the name a to the
item b, this can be used to bind a constant to
its value, or a function name to its code (we do
not use define for variables because the value is
not permitted to change once assigned) - (let ((a 0) b) creates local variables for a
block enclosed in ( ), in this case, a is
assigned 0, and b has no initial value
11Types of Functions
- All functions return a value
- the various mathematical functions return atoms
or numerical values - predicate functions return T or NIL depending on
the outcome of the condition - these include EQ?, ATOM?, NULL?, , lt, gt, lt, gt,
ltgt, EVEN?, ODD?, ZERO? - LIST and CONS are list constructor functions
- they create and return lists
- COND and IF evaluate one or more conditions to
select a function to evaluate and return the
value returned by that function - SET! is a function with a side effect, it
redirects a pointer to point at the second
parameter and then returns the value of the
second parameter - note that if the second parameter is a literal
value (atom or list), there is another side
effect, first storage is allocated to store the
literal value
12Conditional Examples
- Form is
- (COND (test1 result1) (test2 result2) )
- where tests are conditionals and results are
functions - The first test to return true executes the
associated function
- The IF statement has two forms
- (IF (condition) (function1)) if the condition
returns T, the function returns the value
returned by function1 - (IF (condition) (function1) (function2)) if the
condition returns T, the function returns the
value returned by function1 otherwise it returns
the value returned by function2
(cond ((eq? x 0) (set! y 0)) ((gt x 0)
(set! y 1)) (t (set! y 1))) (cond ((lt
hours 40) ( hours wages)) (t ( ( 40 wages)
( ( (- hours 40) wages) 1.5))))
(if (lt hours 40) ( hours wages) ( (
40 wages) ( ( (- hours 40) wages)
1.5))) We might use this as (set! pay (if
))
13CONS list constructor
- CONS has two parameters and returns the list
constructed by taking the first parameter and
making it the CAR of a list and attaching to it
the second parameter as the CDR - Since the CDR must be a list, this only works if
the second parameter was already a list - Examples
- (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)
- (CONS A ((B C))) returns (A (B C))
A
C D
A B
14Defining Functions in Scheme
- Form (DEFINE (funct_name params) body)
- (DEFINE (square num) ( num num))
- so we can now use square as (square x) which
returns x2 - (DEFINE (second alist) (car (cdr alist)))
- notice that for the parameter, I used alist, this
was merely to avoid confusion since list is
defined as a function - if we do (second (a b c d)), it returns b
- (DEFINE (factorial n) (cond ((eq? n 0)
1) (T ( n (factorial (- n 1)))))) - (DEFINE (fib n) (if (gt n 2) ( (fib (- n
1)) (fib (- n 2))) 1)) - (DEFINE (foo alist) (cond ((null? alist)
0) (T ( (foo (cdr alist)) 1))))
If n 0, return 1, otherwise return n
factorial (n-1)
What does this function do?
15More Scheme Functions
(DEFINE (factorial n) (IF ( n 0) 1 ( n
(factorial (- n 1))))) (DEFINE (member atm
lis) (COND ((NULL? lis)
NIL) ((EQ? atm (CAR lis)) T)
(ELSE (member atm (CDR lis)))))
Member will return T if the atm is found in the
lis (list) and nil if it is not found
(member d (a b c d e f)) returns T (member
d (a b e f j k)) returns nil Member in Lisp
and Common Lisp will return the list starting at
atm and going to the end of the list, how can we
modify Schemes member to do this?
(DEFINE (append lis1 lis2) (COND ((NULL?
lis1) lis2) (ELSE (CONS (CAR
lis1)
(append (CDR lis1) lis2))))) If we call
(append (A B) (C D E)) we get back (A B C D E)
If we call (append ((A B) C) (D (E F))) we get
back ((A B) C D (E F))
16Two Similar Scheme Functions
(DEFINE (equalsimp lis1 lis2) (COND ((NULL?
lis1) (NULL? lis2)) ((NULL? lis2) NIL) ((EQ?
(CAR lis1) (Car lis2)) (equalsimp (CDR lis1)
(CDR lis2))) (ELSE NIL)))
(DEFINE (equal lis1 lis2) (COND ((ATOM?
lis1) (EQ? lis1 lis2)) ((ATOM? lis2) NIL)
((equal (CAR lis1) (Car lis2)) (equal (CDR
lis1) (CDR lis2))) (ELSE NIL)))
17More Examples
A function that uses local variables to compute
quadratic roots (DEFINE (quadratic_roots a b c)
(LET ((root_part_over_2a (/ (SQRT (- ( b b)
(4 a c))) ( 2 a)))
(minus_b_over_2a (/ (- 0 b) ( 2 a))))
(DISPLAY ( minus_b_over_2a root_part_over_2a))
(NEWLINE) (DISPLAY (-
minus_b_over_2a root_part_over_2a))))
Two ways to add a list of numbers (DEFINE (adder
lis) (COND ((NULL? lis) 0)
(ELSE ( (CAR lis)
(adder (CDR lis)))))) (DEFINE (create-adder
lis) (COND ((NULL? Lis) 0) (ELSE (EVAL
(CONS lis))))) What does this second one
do?
Example of an apply-to-all function (DEFINE
(mapcar fun lis) (COND ((NULL? Lis) ( ))
(ELSE (CONS (fun (CAR lis))
(mapcar fun (CDR lis)))))) (mapcar (LAMBDA
(X) ( x ( x 1))) (1 5 2 9)) returns
(2 30 6 90)
18Common Lisp
- First developed in 1984
- Incorporates features of many earlier Lisps
including Scheme - Around 1988, CL was combined with CLOS (the
Common Lisp Object System) to be a hybrid
OOfunctional language (in the same way that C
is a combination of C and Smalltalk) - Common Lisp is similar to C in size and
complexity - Common Lisp uses static scoping as its default
but allows for dynamic scoping - Common Lisp includes
- a full range of built-in data types including
arrays, records, complex s, strings, classes - powerful I/O operations including streams and
formatted I/O - imperative features including a wide variety of
control structures - exception handling (several different forms)
19Some Common Lisp Functions
- PROG
- a local block where global variables are
unaffected by any statements within the block - progn is the same except that the block
automatically returns the value of the last
function executed in the local block - New control statements
- DOTIMES counter-controlled loop
- DOLIST iterator loop, executes loop body once
for each list item - DO general purpose loop, can be
counter-controlled or logical, much like Cs
for-loop - Setf for assignment
- variables declared in let statements initialized
to NIL until assigned a value - DEFUN defining functions
- form (DEFUN functionname (params) body)
- body is any single or group of statements, use
(let ( ) body) to have local variables and (progn
body) for a block (for instance as the if or else
clause of an if-else statement) - GO a goto statement (we will see this next)
- Variations of CAR and CDR CADR, CDDR, CAAR,
CDAR, etc are all available
20Common Lisp Functions
Two member functions Iterative version (DEFUN
imember (atm lst) (PROG ( )
loop_1 (COND ((NULL lst) (return nil))
((EQUAL atm (CAR lst))
(RETURN T))) (SETQ lst (CDR lst))
(GO loop_1))) Recursive version (DEFUN
rmember (atm lst) (COND ((NULL lst)
NIL) ((EQUAL atm (CAR
lst)) T) (T (rmember atm
(CDR lst)))))
Two length functions Iterative version (DEFUN
ilength (lst) (PROG (sum) (SETQ
sum 0) again (COND
((NULL lst) (RETURN sum)))
(SETQ sum ( sum 1)) (SETQ lst
(CDR lst)) (GO again))) Recursive version
(DEFUN rlength (lst) (COND ((NULL lst) 0)
(T ( 1 (rlength (CDR lst))))))
21More Recursive Demonstrations
- The main idea behind Lisp was to use recursion
and no local variables - while both iteration and local variables (through
the let function are available, we are generally
discouraged from using either as seen in these
examples
(defun remove-first (a lis) (cond ((null
lis) '( )) ((equal (car lis) a) (cdr
lis)) (t (cons (car lis) (remove-first
a (cdr lis))))))
(defun member-at-all (a lis) (cond ((null
lis) nil) ((listp (car lis)) (or
(member-at-all a (car lis))
(member-at-all a (cdr lis)))) ((equal
(car lis) a) t) (t (member-at-all a
(cdr lis)))))
Remove the first instance of the item a from the
list lis
(defun all-the-same (lis) (cond ((null lis)
t) ((or (atom lis) ( (length lis) 1))
t) ((equal (car lis) (cadr lis))
(all-the-same (cdr lis))) (t
nil)))
The member function looks for a in lis, but what
if a is burried inside of further
lists? member-at-all sees if a is a member at
any level of lis and returns t or nil
Determine if the items in lis are all the same,
such as (a a a) versus (a b a)
22Common Lisp Data Structures
- struct similar to C/C
- (defstruct car year make model mileage)
- (setf mycar (make-car year 2005 model
camry)) - make-car is automatically generated when use
defstruct to define car - defstruct also creates member accessing functions
for each field as car-member - (car-year mycar) returns 2005
- (car-mileage mycar) returns nil since that member
has no value yet - arrays use make-array to generate an array
- (setf anarray (make-array (10 5)) creates a
10x5 array and points anarray at it - access the array using (aref arrayname
arrayindex(es)) - as in (setf (aref anarray 3 2) 12) which sets
anarray32 12 - indices start at 0 as in C/Java
- arrays can be resized/reshaped with the old
values retained - strings treated as arrays of chars
- can be assigned using make-array or directly as
in (setf name Frank Zappa) - accessed through aref and length
- classes added to the language in the late 80s
- define a class through defclass and a method
through defmethod, and an object (instance)
through make-instance - common lisp classes are unlike other OOPLs
because methods are not necessarily tied to
classes, but can be tied to specific objects, and
there is no information hiding mechanisms
23Conclusion
- Functional languages vs. imperative languages
- variables/memory usage less visible making
programming easier - at least in some situations
- simpler syntactic structures to deal with
(everything is a list) - concurrency easier to design and implement
- interpreted nature makes large systems easier to
build - exploits recursion as much as possible, more so
than imperative languages - Uses of functional languages
- mostly used in AI research
- Natural Language Understanding (easy parsing
partially due to recursive nature) - Expert Systems (easy rule format)
- Knowledge Representation (symbolic capabilities)
- Machine Learning (dynamic storage)
- used to teach functional programming
- used to implement EMACS, MACSYMA and some
operating systems