Vous êtes sur la page 1sur 19

1: Variables and Data Types

In Lisp, nearly everything is a function. Even the mathematical operators. For example:
(+ (* 2 3) 1)
equals 7.
As you can tell, the functions open and close with parenthesis. The format for calling functions
in Lisp is
(function arg1 arg2)
setq is used to set variables.
(setq var 32)
(setq str "Texas")
(setq lst '(1 2 3))
The three types of data here are numbers, strings, and lists. Notice that Lisp, unlike Java, is
dynamically typed.
Note: The ' in list statement is called the quote operator, and tells Lisp that the input is a list,
and not to interpret it as a function.
setq sets the variable globally. To create a local variable (e.g. inside a function), use the let
function.
(let
((a 1)
(b 2))

... )
The variables a and b will only be defined within let's parentheses.
To comment in Lisp, prefix the line with ;
; This is a comment!





2: LISP Conditionals
The if statement is a bit different from other programming languages.
(if (< 2 3)
(... true ...)
(... false ...))
Lisp executes the first block of code if the conditional statement is true. The second statement
(which is optional) serves as the else statement.
If you want to execute multiple functions in the if statement (which is common) use the progn
function, which serves to group multiple functions together.
(if (> 3 4)
(progn
(setq x (+ x 1))
(setq y x))
(setq x 0))
If you want an if else statement, then you'd want to use the cond function
(cond
((> x 1) (setq y 1))
((< x 1) (setq y 2))
(t (setq y 0)))
For boolean values, t represents true, and nil represents false. Lisp treats an empty list '() (or
nil) as false and all other inputs as true.
This is a convenient feature of the language to know. For instance, to do something only if a list
is not empty, the following two chunks of code are identical.
(if (> (length lst) 0)
(...))
(if lst
(...))
While loops are accomplished in the following manner:
(loop while (> n 0)
(setq n (- n 1)))
Although for the most part, recursion is the more popular way to accomplish loops.



3: Lists in LISP
Lists are important in Lisp, which is why Lisp stands for LISt Processing. I'd recommend
reading up on Wikipedia's article on linked lists to fully understand this section.
(setq lst '(1 2 3))
To get the first item from the list, use the first function. To get the rest of the items, use rest.
The are historically known as car and cdr, so you may see it referred to as such in older texts.
(first lst) => 1
(rest lst) => (2 3)
Lisp provides some helpful shortcuts to access other items in the list as well.
(second lst) => 2
(third lst) => 3
(fourth lst) => nil
Note: You could access these elements without these functions through repeatedly using first
and rest. For instance, second is equivalent to (first (rest lst))). These functions are to
save you a bit of typing.
To add an item to the beginning of the list, use the cons function. cons returns a new list with
the element prefixed to the beginning of the list.
(cons 0 lst) => (0 1 2 3)
These functions really only make sense in the context of recursion, which is very prevalent in
Lisp. Below is an example of a recursive sum function which uses both first and rest in a
recursive context.
(defun sum (lst)
(if (not lst)
0
(+ (first lst) (sum (rest lst)))))







4: Functional Programming
defun is used to define functions.
(defun square (x)
(* x x))

(defun add (x y)
(+ x y))
Note: Lisp implicitly returns the value of the last statement in a function.
Calling functions is pretty straight forward; we've been doing it throughout this guide.
(square 9) => 81
(add 2 4) => 6

















5: Code for Breadth First Search
;;; Solve by breadth-first search
(in-package "USER")

(defun bfs (state limit)
(setf *nodes* 0)
(setf *expanded* 0)
(setf *branches* 0)
(setf *limit* limit)
(setf *result* (bfs1 (list (list state))))
(print (list *nodes* *expanded* *branches*))
(reverse *result*))

(defun bfs1 (queue)
(setf *nodes* (+ 1 *nodes*))
(cond
((null queue) nil)
((goalp (caar queue)) (car queue))
((> *nodes* *limit*) nil)
((let ((children (new-states (caar queue))))
(setf *expanded* (+ 1 *expanded*))
(setf *branches* (+ (length children) *branches*))
(bfs1
(append
(cdr queue)
(mapcar
#'(lambda (state)
(cons state (car queue)))
children)))))))
Sample Run
;;; Sun Common Lisp, Development Environment 4.0.0 , 6 July 1990
;;; Sun-4 Version for SunOS 4.0.x and sunOS 4.1

> (compile-file "wj.lisp")
#P"/usr2/mlm/wj.sbin"

> (compile-file "bfs")
#P"/usr2/mlm/bfs.sbin"

> (compile-file "dfs")
#P"/usr2/mlm/dfs.sbin"

> (load "wj")
> (load "dfs")
> (load "bfs")

> *start*
(0 0)

> (new-states *start*)
((4 0) (0 3))

> (dfs *start* 7 100000)
(584 206 591) ; Branching factor 2.86 (591/206)
((0 0) (4 0) (1 3) (1 0) (0 1) (4 1) (2 3))

> (bfs *start* 100000)
(341 340 981) ; Branching factor 2.88 (981/340)
((0 0) (4 0) (1 3) (1 0) (0 1) (4 1) (2 3))























5: Water Jug Solution
Lisp code to for goal and new-states
;;; Solve the Water Jug problem
(in-package "USER")

(defvar *start* '(0 0))

(defun first-jug (state) (car state))
(defun second-jug (state) (cadr state))
(defun mk-state (f s) (list f s))

(defun goalp (state)
(eq (first-jug state) 2))

(defun new-states (state)
(remove-null
(list
(fill-first state)
(fill-second state)
(pour-first-second state)
(pour-second-first state)
(empty-first state)
(empty-second state))))

(defun remove-null (x)
(cond
((null x) nil)
((null (car x)) (remove-null (cdr x)))
((cons (car x) (remove-null (cdr x))))))

(defun fill-first (state)
(cond
((< (first-jug state) 4) (mk-state 4 (second-jug state))))))

(defun fill-second (state)
(cond
((< (second-jug state) 3) (mk-state (first-jug state) 3))))


(defun pour-first-second (state)
(let ( (f (first-jug state))
(s (second-jug state)))
(cond
((zerop f) nil) ; Cant pour nothing
((= s 3) nil) ; Second full
((<= (+ f s) 3) ; Empty first into second
(mk-state 0 (+ f s)))
(t ; Fill second from first
(mk-state (- (+ f s) 3) 3)))))

(defun pour-second-first (state)
(let ( (f (first-jug state))
(s (second-jug state)))
(cond
((zerop s) nil) ; Cant pour nothing
((= f 4) nil) ; First full
((<= (+ f s) 4) ; Empty second into first
(mk-state (+ f s) 0))
(t ; Fill first from second
(mk-state 4 (- (+ f s) 4))))))

(defun empty-first (state)
(cond
((> (first-jug state) 0) (mk-state 0 (second-jug state)))))

(defun empty-second (state)
(cond
((> (second-jug state) 0) (mk-state (first-jug state) 0))))
Code for Depth First Search
;;; Depth first search with state limit
(in-package "USER")

(defun dfs (state depth limit)
(setf *nodes* 0)
(setf *expanded* 0)
(setf *branches* 0)
(setf *limit* limit)
(setf *result* (dfs1 state depth))
(print (list *nodes* *expanded* *branches*))
*result*
)

;;; dfs1 expands a node and calls dfs2 to recurse on it

(defun dfs1 (state depth)
(setf *nodes* (+ 1 *nodes*))
(cond
((goalp state) (list state))
((zerop depth) nil)
((> *nodes* *limit*) nil)
((let ((children (new-states state)))
(setf *expanded* (+ 1 *expanded*))
(setf *branches* (+ (length children) *branches*))
(let ((result (dfs2 children (- depth 1))))
(and result (cons state result)))))))

;;; dfs2 recurses on each sibling from a single node, calling dfs1
(defun dfs2 (states depth)
(cond
((null states) nil)
((dfs1 (car states) depth))
((dfs2 (cdr states) depth))))
6: Steepest-Ascent Hill climbing using LISP
;;;Steepest-Ascent Hill climbing, Best-first-tree-search, Best-
first-graph-search
;;;Assumes you have defined
;;;1. A set of operators as lisp function
;;;2. a function solution-state?(description) to determine if a
state is a solution
;;;3. a function to judge how close a state is to the goal:
estimated-distance-from-goal(description)
;;;4. a function that indicates the cost of applying an operator
cost-of-operator(Current-state, operator)


(defvar *trace-search* nil)

(defvar *graphing* nil "Should the search space be graphed? Only
works for cities")
(defvar *pausing* nil "Should the system pause after each item
graphed? Only works for cities")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Utilities to measure size of search space



(defstruct search-statistics
(nodes-visited 0)
(maximum-length-of-node-list 0)
(length-of-solution 0)
(maximum-depth 0)
(cost-of-solution 0))

(defparameter *stats*(make-search-statistics))

(defun reset-stats()
(setq *stats* (make-search-statistics)))

(defun update-heuristic-statistics (stats e node-list)
"Changes stat structure to reflect new number of nodes
visited,
and optionally new maximum length and depth"
(setf (search-statistics-nodes-visited stats)
(+ 1 (search-statistics-nodes-visited stats)))
(when (> (length node-list)
(search-statistics-maximum-length-of-node-list
stats))
(setf (search-statistics-maximum-length-of-node-list stats)
(length node-list)))
(when (> (length (hnode-path e))
(search-statistics-maximum-depth stats))
(setf (search-statistics-maximum-depth stats)
(length (hnode-path e)))))


;;;Search utilities
(defstruct hnode ;elements of node-list
state
path
ESTIMATED-DISTANCE-FROM-GOAL
COST-OF-PLAN-SO-FAR)


(defun successor-state (description operator)
"Given a state description and an operator,
returns the state when the operator is applied
or nil if the operator can't be applied.
Operator is the name of a function of 1 argument, a state
description"
(if (fboundp operator) ;;if it has a function definition
(funcall (symbol-function operator) description)
(error "The operator ~a does not have a function definition"
operator)))

(defun successor-hnode(node operator)
"Given a node and an operator,
returns the node when the operator is applied.
or nil if the operator can't be applied.
The next node is formed from the next state and adding the
operator
to the front of the path of the current node
Operator is the name of a function of 1 argument, a state
description"
(let ((next (successor-state (hnode-state node) operator)))
(when next
(when *graphing* (draw-transition (hnode-state node) next)
(when *pausing*
(format t "~%pause> ")
(setq *pausing* (not (eq #\n (read-char))))))
(make-hnode :state next
:path (add-to-end operator (hnode-path node))
:estimated-distance-from-goal (estimated-
distance-from-goal next)
:cost-of-plan-so-far (+ (cost-of-applying-
operator (hnode-state node) operator)
(hnode-cost-of-plan-so-
far node))))))


(defun add-to-front(atom list)
"Create a new list with atom at the end of list"
(cons atom list))

(defun add-to-end(atom list)
"Create a new list with atom at the end of list"
(append list (list atom)))

(defun steepest-ascent-hill-climbing-search (initial-state
operators)
"Returns a list whose first element is the search statistics
and
whose remaining elements are an ordered list of operators
that need
to be applied to get to the goal state. See pg 67."
(let ((current-node (make-hnode :state initial-state
:path nil
:cost-of-plan-so-far 0
:estimated-distance-from-goal
(estimated-distance-from-goal
initial-state)))
(solved nil)
(next-node nil)
(best-successor nil)
(minimum-distance-to-goal nil)
)
(when *graphing* (create-map-window "Steepest ascent hill
climbing search"))
(loop until (or (null current-node) ;;no state was better
than current state
solved)
do (setq minimum-distance-to-goal (hnode-estimated-
distance-from-goal current-node))
(setq best-successor nil)
(update-heuristic-statistics *stats* current-node
(list current-node))
(when *trace-search*
(format t "~%~%Exploring ~a" current-node))
(cond ((solution-state? (hnode-state current-node))
;;if current-node is a solution, exit with
success
(setq solved t))
(t ;;otherwise apply each operator
;;set best-successor to closest state to goal
(provided it is better than
;;than any state explored so far (minimum-
distance-to-goal)
;;when done, set current-node to best-successor
;;fail if no successor is better (best-
successor = nil)
(loop for rule in operators
do (setq next-node (successor-hnode
current-node rule))
(when (and next-node
(< (hnode-estimated-distance-
from-goal next-node)
minimum-distance-to-goal))
(setq best-successor next-node)
(setq minimum-distance-to-goal (hnode-
estimated-distance-from-goal best-successor))
))
(if (null best-successor)
(setq current-node nil)
(setq current-node best-successor))
)))
(when solved
(setf (search-statistics-length-of-solution *stats*)
(length (hnode-path current-node)))
(setf (search-statistics-cost-of-solution *stats*)
(hnode-cost-of-plan-so-far current-node))
(setf (search-statistics-cost-of-solution *stats*)
(hnode-cost-of-plan-so-far current-node))
(when *graphing*
(draw-solution (cons *start-state* (hnode-path current-
node))))
current-node)))


(defun best-first-tree-search (initial-state operators)
"Returns a list whose first element is the search statistics
and
whose remaining elements are an ordered list of operators
that need
to be applied to get to the goal state. See pg 75
Like breadth first search, but sorts by estimated-distance to
goal"
(let ((open (list (make-hnode :state initial-state
:path nil
:cost-of-plan-so-far 0
:estimated-distance-from-goal
(estimated-distance-from-goal
initial-state))))
(solved nil)
(next-node nil)
(e nil))
(when *graphing* (create-map-window "best first tree
search"))
(loop until (or (null open) ;;no more states
solved)
do (setq e (first open))
(update-heuristic-statistics *stats* e open)
(when *trace-search*
(format t "~%~%Exploring ~a" e))
(setq open (rest open))
(cond ((solution-state? (hnode-state e))
;;if e is a solution, exit with success
(setq solved t))
(t ;;otherwise add next-node to the end of open
(loop for rule in operators
do (setq next-node (successor-hnode e
rule))
(when next-node
(setq open (add-to-front next-node
open))))
(setq open (sort-by-estimated-distance
open)))))
(when solved
(setf (search-statistics-length-of-solution *stats*)
(length (hnode-path e)))
(setf (search-statistics-cost-of-solution *stats*)
(hnode-cost-of-plan-so-far e))
(when *graphing*
(draw-solution (cons *start-state* (hnode-path e))))
e)))


(defun sort-by-estimated-distance(list)
"Sort list so that those with lowest distance to the goal are
first"
(sort list #'< :key #'hnode-estimated-distance-from-goal))

(defun best-first-graph-search (initial-state operators)
"Returns a list whose first element is the search statistics
and
whose remaining elements are an ordered list of operators
that need
to be applied to get to the goal state. See pg 75
Like best first tree search, but detects duplicate nodes"
(let ((open (list (make-hnode :state initial-state
:path nil
:cost-of-plan-so-far 0
:estimated-distance-from-goal
(estimated-distance-from-goal
initial-state))))
(solved nil)
(CLOSED NIL) ;; A LIST OF STATES ALREADY EXPLORED
(next-node nil)
(e nil))
(when *graphing* (create-map-window "best first graph
search"))
(loop until (or (null open) ;;no more states
solved)
do (setq e (first open))
(SETQ CLOSED (CONS (hnode-STATE E) CLOSED)) ;;ADD E to
CLOSED

(update-heuristic-statistics *stats* e open)
(when *trace-search*
(format t "~%~%Exploring ~a" e))
(setq open (rest open))
(cond ((solution-state? (hnode-state e))
;;if e is a solution, exit with success
(setq solved t))
(t ;;otherwise add next-node to the end of open
(loop for rule in operators
do (setq next-node (successor-hnode e
rule))
(when (and next-node
(NOT (ALREADY-VISITED? (hnode-
STATE NEXT-NODE) CLOSED)))
;;ONLY ADD IF YOU HAVEN'T SEEN IT YET
(setq open (add-to-front next-node
open))))
(setq open (sort-by-estimated-distance
open)))))
(when solved
(setf (search-statistics-length-of-solution *stats*)
(length (hnode-path e)))
(setf (search-statistics-cost-of-solution *stats*)
(hnode-cost-of-plan-so-far e))
(when *graphing*
(draw-solution (cons *start-state* (hnode-path e))))
e)))

(defun already-visited? (state visited)
"Determines if a state is in the closed list"
(member state visited :test #'equalp))


(defun hill-climbing-minimization (initial-state operators)
(let ((current-node (make-hnode :state initial-state
:path nil
:cost-of-plan-so-far 0
:estimated-distance-from-goal
(estimated-distance-from-goal
initial-state)))

(next-node nil)
(best-successor t)
(minimum-value-of-state (value-of-state initial-state))
)
(when *graphing*
(setq *goal-state* initial-state)
(create-map-window "Hill Climbing minimization")

(loop until (null best-successor)
do (setq best-successor nil)
(update-heuristic-statistics *stats* current-node
(list current-node))
(when *trace-search*
(format t "~%~%Exploring ~a" current-node))
(loop for o in operators
do (setq next-node (successor-hnode current-
node o))
(when (and next-node
(< (value-of-state (hnode-state
next-node))
minimum-value-of-state))
(setq best-successor next-node)
(setq minimum-value-of-state (value-of-state
(hnode-state next-node)))
))

(when best-successor
(setq current-node best-successor)))

current-node)))


(defvar *search-methods* nil "Used by Search Menu")
(unless (member 'steepest-ascent-hill-climbing-search *search-
methods*)
(setf *search-methods* (cons 'steepest-ascent-hill-climbing-
search *search-methods*)))
(unless (member 'hill-climbing-minimization *search-methods*)
(setf *search-methods* (cons 'hill-climbing-minimization
*search-methods*)))
(unless (member 'best-first-tree-search *search-methods*)
(setf *search-methods* (cons 'best-first-tree-search *search-
methods*)))
(unless (member 'best-first-graph-search *search-methods*)
(setf *search-methods* (cons 'best-first-graph-search *search-
methods*)))

















::Lab programs::


1: Variables and Data Types
2: LISP Conditionals
3: Lists in LISP
4: Functional Programming
5: Code for Breadth First Search
5: Water Jug Solution
6: Steepest-Ascent Hill climbing using LISP

Vous aimerez peut-être aussi