Document 13211391

advertisement
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Lambda-expression
TDDC65 Artificial Intelligence and Lisp
Lecture 4
Functions and higher order functions
- Functions - lambda-expressions (sec 7.1 Programmering
i Lisp)
- funcall and function (sec 7.2 Programmering i Lisp)
- higher order functions (sec 7.3 Programmering i Lisp)
At the definition
(defun square (x) (* x x))
the following function is defined:
(lambda (x) (* x x))
The name of the function is:
square
- pattern matching
- Lab 2 - Higher order functions and pattern matching
An expression (form) in LISP is:
(function argument ... argument)
How to write Lisp-programs
* Common Lisp built in functions
* Abstractions
* Style (functional / imperative)
* Program structure - small functions
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
A function is a first order data object:
3
Σf(i)
i=1
(defun sum3 (f)
(+ (funcall f 1) (funcall f 2) (funcall f 3)))
(sum3 (function sin))
(sum3 #’sin)
3
i=1
Example on a function returning another function as value:
(defun choose-function (n)
(cond
((= n 1)
(function 1+))
((= n 2)
(function (lambda (n) (+ n 2))))
(t (function (lambda (n) (+ n 10))))))
(funcall (choose-function 2) 5) => 7
3
Σi2+3
((lambda (x y) (* x y)) 10 20)
=> 200
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
We can now define a function
- taking functions as arguments
- returning a function as value
Σsin(i)
i=1
where function is a lambda-expression or
a name . Example:
(sum3 (function
(lambda (i)
(+ (* i i) 3))))
(setq my-fn (choose-function 3))
(funcall my-fn 2) => 12
(my-fn 2) => ?
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Patterns as higher order functions:
Define a function which increases every element in a
list with 5.
(defun increase-5 (l)
(cond
((endp l) ’())
(t (cons
(+ 5 (first l))
(increase-5 (rest l))) )))
A pattern for traversing a list and applying a function on every element is:
(defun fn-mall (l)
(cond
((endp l) ’())
(t (cons
(”operation” (first l))
(fn-mall (rest l)))))))
(increase-5 ’(1 10 50)) => (6 15 55)
Define a function creating a new list with all first-elements on its sublists.
(defun first-element (l)
(cond
((endp l) ’())
(t (cons
(first (first l))
(first-element (rest l))) )))
(first-element
’((one ett) (two två) (three tre)))
=> (one two three)
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
It is now simple to define this pattern.
(defun my-mapcar (fn l)
(cond
((endp l) ’())
(t (cons
(funcall fn (first l))
(my-mapcar fn (rest l))) )))
fn = ”a function taking one argument”
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Map-functions
Map-functions in Common Lisp
Common Lisp has a large number of higher order
functions. Some of them start its name with map.
(mapcar #’first ’((a b c) (x y z)))
=> (a x)
(mapcar #'square '(1 2 3 4))
=> (12 22 32 42) = (1 4 9 16)
Can be used with several list arguments:
(mapcar
#'(lambda (x) (+ x 10))
'(1 2 3 4))
=> (”1+10” ”2+10” ”3+10” ”4+10”)
= (11 12 13 14)
(mapcar #’(lambda (x y z) (+ x y z))
’(1 2 3)
’(10 20 30)
’(100 200 300))
=> (111 222 333)
We can now define the specialized function by
using mapcar instead.
Some functions are used for its side-effect:
(defun increase-5 (l)
(mapcar #’(lambda (n) (+ n 5)) l))
(mapc #’print ’(anna kalle stina))
anna
kalle
stina
(defun first-element (l)
(mapcar #’first l))
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
A function is applied to a list and its tails:
Double recurison
(maplist #’length ’(a b c d))
=> (4 3 2 1)
lists in lists
binary tree recursion
Conditional functions:
”does it hold for every element ..?”
”does it hold for some element ...?”
(some #’numberp ’(a 1 b c)) => t
(every #’numberp ’(1 2 3 4)) => t
(notany #’numberp ’(a b c d)) => t
(notevery #’numberp ’(a b c 1 d)) => t
Some functions are in if and if-not-variants:
(member-if #’numberp ’(a b 1 c d)) => t
(remove-if #’numberp ’(a b 1 c d)) => (a b c d)
(remove-if-not #’numberp ’(a b 1 c d)) => (1)
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
We make a higher order function which captures
this structures. It needs an initial value, a function to apply when the first element is an atom
(non-list) and a function to apply when the first
element is a list.
(defun map-all-elements (l init fn1 fn2)
(cond
((endp l) init)
((atom (first l))
(funcall fn1
(first l)
(map-all-elements (rest l) init fn1 fn2)))
(t (funcall fn2
(map-all-elements (first l) init fn1 fn2)
(map-all-elements (rest l) init fn1 fn2)) )))
(map-all-elements '(a (b c (d e)) f)
0
#’(lambda (v1 v2) (+ v2 1))
#’+)
=> 6
(map-all-elements '(a (b c (d e)) f)
’()
#’cons
#’append)
=> ?
Example: Total number of elements in a list
with arbitrary elements
(defun symbols-in-seq (l)
(cond
((endp l) 0)
((atom (first l))
(+ 1 (symbols-in-seq (rest l))))
(t (+
(symbols-in-seq (first l))
(symbols-in-seq (rest l))) )))
(symbols-in-seq '(a (b c (d e)) f))
=> 6
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
In lab assignment 3 you will work with graph
search with different methods.
The basic structure of the algorithm is the same
for several of the methods. The difference in the
methods are how to evaluate nodes and how to
order them on a queue.
The main search function is a higher order function and is used by giving a function as parameters for describing the specialized method.
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Lab 2 Pattern matching
Pattern matching
used in:
In this example we will perform matching on lists.
We will have the following match symbols:
- command languages
& matching an arbitrary element
to end commands
-- match a segment - none, one, two or more elements
cl-allegESC or cl-allegTAB
using wild-card
delete a*.lsp
The pattern (a & b &) matches (a x b y), (a a b b)
but will not match (b x a y), (a b), (a x b y c)
The pattern (a -- b) matches (a b), (a x b), (a x y b),
(a 1 2 3 4 5 6 7 8 9 0 b) but will not match (a x y),
(a x b y)
- search engines in Internet
The match may not be unique:
The pattern (a -- b --) matches (a x b y b z) by two
different ways:
(a x b y b z)
(a x b y b z)
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Pattern matching
The only difficult part is to match a segment. What
strategy should we use?
Start to let the segment pattern to match no element,
try to match the rest of the pattern and the list. If it succeeds we have a match, otherwise let the segment pattern match one element, match the rest, then 2
elements if necessary ...
Match (a -- b) with (a x y b)
The segment pattern matches no element, try to match
(b) with (x y b). Failed.
Match the segment pattern with x. Try to match (b) (y
b). Failed.
Match the segment pattern with x and y. Try to match
(b) with (b). Success!
When we fail we must ”back track” to the closest segment pattern and let it match next element. We may
have several segment patterns, so it may be complicated to follow.
A pattern matcher patmatch
(defun patmatch (l pat)
(cond
((endp pat) (endp l)) ; end of list and pattern?
((eq (first pat) ' --) ; segment pattern
(cond
((patmatch l (rest pat)) t)
; match the rest?
((endp l) nil)
; failed, the segment pattern
; matched the entire list
(t (patmatch (rest l) pat))))
; let the segment pattern
; match also next element
; nothing left to match against
'&) ; match an arbitrary element
((endp l) nil)
((eq (first pat)
(patmatch (rest l) (rest pat)))
((eql (first pat) (first l)) ; elements match
(patmatch (rest l) (rest pat)))
(t nil))))
; failed
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Lab 2
Define this pattern matcher and extend it also to do
- matching in i arbitrary lists, (lists with lists)
- matching ”greater than” and ”less than” of numbers
- matching ”not” of a pattern
Data base:
(((author (anders haraldsson)) (title (programmering i lisp))
(year 1993) (place (room (left 4))))
((author (anders haraldsson)) (title (programmering i
pascal)) (year 1979) (place (room (left 2))))
((author (nils nilsson)) (title (artificial intelligence))
(year 1999) (place (library (pu 1573)))) ... )
(find-books ’((author (? haraldsson)) --))
=> (((author (anders haraldsson)) (title (programmering i
lisp)) (year 1993) (place (room (left 4))))
((author (anders haraldsson)) (title (programmering i
pascal)) (year 1979) (place (room (left 2)))) )
How to write Lisp-programs
Lisp is an old language and has developed
during over 40 years. Old constructs are still in
use in the language and new have been added.
Today’s standard Common Lisp contains
hundreds of functions, often variants of each
other.
There are attempts to develop code standards,
see web-pages for the course.
(find-books ’((author (anders haraldsson)) ? (year (> 1985))
--))
=> (((author (anders haraldsson)) (title (programmering i
lisp)) (year 1993) (place (room (left 4)))))
(find-books ’((author (anders haraldsson)) (title (not (-- lisp))) --))
=> (((author (anders haraldsson)) (title (programmering i
pascal)) (year 1979) (place (room (left 2)))))
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Some ideas and practices
nil
car - cdr chains
The symbol nil is used for different purposes, the false
value, the empty list or as the symbol itself. There are
alternative external notations to describe it.
To access elements in a list the basic functions are car
and cdr and we can use combinations such as (cadr l)
= (car (cdr l))
An alternative and more readable is to use first and rest
instead of car and cdr (they are only synonyms and exactly the same functions). There are also functions for
accessing other elements: second, third etc. up to
tenth.
(add-points ’((kalle 23) (stina 15) (john 20))
=> 58
(defun add-points (l)
(if (endp l)
0
(+ (second (first l)) (add-points (rest l)))))
nil
()
’()
’nil
false value
empty parameter list
empty list, to be quoted
if the symbol nil is mentioned
(defun f () (g ’() ’nil))
(defun g (list symbol)
(if (endp list)
nil
t)
The function f has an empty parameter list and calls
the function g with an empty list and the symbol nil.
The function g returns a false value if the list is empty,
otherwise a true value.
Use (endp l) to check for the empty list (if you know l
is a list), use (null l) if l can be anything and (eq l ’nil)
or (equal l ’nil) if l is a symbol.
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Local variables
The most common way is to use let
(let ((a 1) (b 2)
(+ a b))
=> 3
(let* ((a 1) (b (+ a 2)))
(+ a b))
=> 4
Use abstractions
(abstract data types, object oriented style)
In a real example see lists as representation of a data type or
class and introduce ”abstract” primitives as constructors, selectors, recognizers, iterators etc.
You can use the way you learnt in other programming language and data structure courses.
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Assume the previous example with add-points and
see the problem as defining a data type result-database.
Such an object consists of a sequence of results. A result is
a record of a name (as a symbol) and points (as an integer).
(setq Lisp-results
(create-db (make-result ’kalle 23)
(make-result ’stina 15)
(make-result ’john 20)))
(defun make-result (person points) (list person points))
(defun person (result) (first result))
(defun points (result) (second result))
(defun create-db (&rest result-records) result-records)
(defun empty-db (result-db) (endp result-db))
(defun first-result (result-db) (first result-db))
(defun rest-result-db (result-db) (rest result-db))
(defun add-points (db)
(if (empty-db db)
0
(+ (points (first-result db))
(add-points (rest-result-db db)))))
(add-points Lisp-results) => 58
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Assignment (mutator functions)
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Methodology
There are specialized functions to change parts of objects,
such as giving a new value to an element in an array or
record, or change a pointer in a linked structure (a Lisp list)
Common Lisp contains all kind of functions so you may
use your favourite paradigm to program.
A recommendation is to use generalized assignment (which
we find in mostly all languages through = or :=)
The most common way is to use Lisp in a functional programming style, writing recursive functions over structures, such as sequences (lists), binary trees, graphs etc.
(setf place expression)
compare place = expression
To us a global variable and assignment of a local variable
you may use setq or setf
(setq/setf my-pi 3.14)
Changing a pointer in a cons-cell in a Lisp list.
(setf (first l) p)
(setf (rest l) p)
Assigning a new value to an element in an array:
(setf (aref vector n) 100)
In the functional programming style you do not need assignment and special iterative constructs.
If you program in a more iterative way there are a lot of
constructs such as
(dotimes i 5 ...) - repeat 5 times, let i go from 0 to 4
(dolist e ’(a b c d) ...) - for each element in the list
(loop .. (return ..) ..) - finish the loop by calling return
(do ....) - general iteration
Often you may mix paradigms, but try in this course to
program as recursive as possible and use iterative construct only when it seems better (may be of efficiency
reasons)
Universitetet i Linköping
Institutionen för datavetenskap
Anders Haraldsson
Program structure
The incremental system around Lisp makes it easy to develop smaller units and test them separately. Observe that
you do not need any input and output, during the tests.
Define first your functions globally, test them, use trace
for recursive functions.
You can later structure your functions in a block-structured way by using labels.
(defun my-reverse (l)
(labels ((rev2 (l m)
(if (endp l)
m
(rev2 (rest l) (cons (first l) m)))))
(rev2 l ’())))
(my-reverse ‘(a b c)) => (c b a)
Download