0% found this document useful (0 votes)
206 views

Module 1lisp

LISP is one of the oldest programming languages. It was invented in the late 1950s and is particularly suited for AI programs because it can effectively process symbolic information. LISP has a simple syntax with little or no data typing and dynamic memory management. There are several dialects of LISP, and Common LISP is a recent attempt to standardize the language. LISP has become popular for AI applications and special LISP processing machines were built to further its use in different sectors.

Uploaded by

Fahma Mol
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
206 views

Module 1lisp

LISP is one of the oldest programming languages. It was invented in the late 1950s and is particularly suited for AI programs because it can effectively process symbolic information. LISP has a simple syntax with little or no data typing and dynamic memory management. There are several dialects of LISP, and Common LISP is a recent attempt to standardize the language. LISP has become popular for AI applications and special LISP processing machines were built to further its use in different sectors.

Uploaded by

Fahma Mol
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 40

3.1 Specific Instructional Objectives LISP is one of the oldest programming languages.

It was invented by John McCarthy during the late 1950s, shortly after the development of FORTRAN, LISP is particularly suited for AI programs because of its ability to process symbolic information effectively. It is a language with a simple syntax, with little or no data typing and dynamic memory management. There are several dialects of LISP including FRANZLISP, INTERLISP,MACLISP, QLISP, SCHEME and COMMON LISP. The COMMON LISP version is a recent attempt to standardize the language to make it more portable and easier to maintain.

LISP has become the language of choice for most AI practitioners. It was practically unheard of outside the research community until AI began to gain some popularity ten to fifteen years ago. Since then, special LISP processing machines have been built and its popularity has spread to many new sectors of business and government. In this chapter we give a summary of the important features of LISP and briefly introduce PROLOG and other AI languages.

3.2 Introduction to LISP

LISP is the oldest computer programming language It has the ability to process symbolic information effectively It is a language with simple syntax, with little or no data typing and dynamic memory management

3.3 Syntax and numeric functions Lisp operates on forms. Each Lisp form is either an atom or a list of forms. Atoms are numbers, strings, symbols and some other structures. When Lisp is forced to evaluate the form it looks whether it's an atom or a list. If it's an atom then its value is returned (numbers, strings and other data return themselves, symbols return their value).

If the form is a list Lisp looks at the first element of the list - its car. The car of that list should be a symbol or a lambda-expression.

If it's a symbol Lisp takes its function and executes that function with the arguments taken from the rest of the list.

Example: (+ 1 2 3) returns 6. Symbol "+" is associated with the function + that performs the addition of its arguments. (+ 1 (+ 2 3) 4) returns 10. The second argument contains a form and is evaluated before being passed to the outer +.

The Basic building blocks of LISP are atom, list and the string An atom is a number or string of contiguous characters, including numbers and special characters. Ex: this-is-a-symbolic-atom, kill, a1234, item#4

A list is a sequence of atoms or list enclosed within parentheses. Ex: (this is a list), (x(x y) z pq), ( ), (jan, feb, mar)

A string is a group of characters enclosed in double quotation marks. Ex: this is a string, what is your name

The top elements of the list (a b (c, d) e (f)) are a, b, (c d), e and (f). The elements c and d are the top elements of the sublist (c d)

Atoms, lists and strings are called symbolic expressions or s-expressions LISP programs run either on an interpreter or as compiled code The interpreter examines source programs in a repeated loop called the read-evaluate-print loop. This loop reads the program code, evaluates it and prints the values returned by the program

The interpreter signals to accept code for execution by printing a prompt such as the - > Example: To find the sum of three numbers 2, 3, 4 ->(+ 2 3 4) 9 ->

LISP uses prefix notation, and the + symbol is the function name for the sum of the arguments that follow

The read-evaluate-print loop reads the expression, evaluates it and prints the value returned (9)

Example: The mathematical expression (50 * 9 / 5) + 32 -> (+ (* ( / 9 5) 50) 32) 122

-> Each function call is performed in the order in which it occurs within the parenthesis To compute the sum, the argument (* ( / 9 5) 50) must first be evaluated. This requires that the product of 50 and 9 / 5 be computed, which in turn requires that the quotient 9 / 5 be evaluated. The embedded function (/ 9 5) returns the quotient 1.8 to the multiply function to give (* 1.8 50). This is then evaluated and the value 90 is returned to the sum function to give (+ 90 32). The final result is the sum 122 returned to the read-evaluateprint loop for printing The basic numeric operations are +, -, * and /. Arguments may be integers or real values Examples of function calls and the results returned are listed below (+ 3 5 8 4) (- 10 12) (/ 25 2) -2 12.5 20

The letter t signifies logical true and nil signifies logical false NIL is also the same as the empty list ( ) Example: ->t T ->NIL NIL

3.4 Basic list manipulation functions in LISP The atom or list is represented with a single quotation mark. E.g.: man, (a b c d) The quotation mark informs the interpreter that the atom or list should not be evaluated Variables in Lisp are symbolic atoms Symbolic atoms may be assigned values i.e. bound to values with the function setq setq takes two arguments The first argument must be a variable. It is never evaluated and should not be in quotation marks Second argument is evaluated and the result is bound to the first argument

When variables are evaluated, they return the last value bound to them

Some of the examples are setq are ->(setq x 10) 10 ->x 10 ->(setq x(+ 3 5)) 8 ->(setq x(+ 3 5)) (+ 3 5) ->y Unbound variable:Y ; the number 10 evaluates to itself ; is bound to x and 10 is returned ; the variable x is evaluated to ; return the value it is bound to ; x is reset to the value (+ 3 5) ; and that value returned ; x is reset to the literal value ; (+ 3 5), quote inhibits evaluation ; the variable y was not previously ; bound to a value causing an error

Some basic symbol processing functions are car, cdr, cons and list

Function call (car(a b c)) (cdr(a b c))

Value returned a (b c)

Remarks car takes one argument, a list, and returns the first element cdr takes one argument, a list, and returns a list with the first element removed cons takes two arguments, an element and a list and returns a list with the element inserted at the beginning list takes any number of arguments and returns a list with the arguments as elements

(consa(b c))

(a b c)

(lista(b c))

(a(b c))

Example: ->(cons(* 2 3)(1)) ((* 2 3)1) ->(cons(* 2 3)(1)) (6 1) ; the literal list (* 2 3) is ; consed to the list (1) ; the evaluated list (* 2 3) is ; concerned to the list (1)

->(setq x(a b c)) (A B C) ->x X ->x (A B C) ; (a b c)

; x is bound to the literal list

; the quote inhibits the evaluation

; but unquoted x evaluates to its previously bound value

The syntax for a function call is (function-name arg1 arg2) When a function is called, arguments are evaluated from left to right and then the function is executed using the evaluated argument values

Example: ->(car(cdr(a b c d)) B ->(cdr car((a b)c d)) (B) ->(consone(two three)) (ONE TWO THREE) ->(cons(car(a b c) (cdr(a b c))) (A B C) ->(list (a b)cd)) ; inserts the element one in ; the list (two three) ; lists may continue on ; several lines but parents ; must always balance ; makes a list of the top ; elements ; extracts the list (b) ; extracts the second element

((A B) C D) Additional list manipulation functions

Function call (append(a)(b c)) (last(a b c d)) (memberb(a b d)) (reverse(a(b c) d))

Value returned (a b c) (d) (b d) (d(b c) a)

Remarks Merges two or more lists into a single list Returns a list containing the last element Returns remainder of second argument list starting with element matching first argument Returns list with top elements in reverse order

Examples: ->(append(a(b c))(d e)) (A(B C)D E) ->(append( a)(b c)(d)) (A B C D) ->(last(a b(c d)(e))) ((E)) ->(member(d)(a(d)e f)) ((D)E F) ->(reverse(a b(c d) e)) (E(C D)B A) ; returns the last top ; element as a list ; returns the tail of ; list from member element ; returns the list with top ; elements in reverse order ; returns a single list of ; top element input list

3.5 Defining functions The function named defun is used to define functions. It requires three arguments The new function name The parameters for the function The function body or lisp code which performs the desired function operations

The format is (defun name(parm1 parm2 ) body) defun does not evaluate its arguments. It simply builds a function which may be called like any other function

Example, we define a function named average3 to compute the average of three numbes ->(defun average3(n1 n2 n3)

(/(+ n1 n2 n3)3)) AVERAGE3 ; defun returned the name of the function

To call the function average3 we give the function name followed by the actual arguments ->(average3 10 20 30) 20

When a function is called the arguments supplied in the call are evaluated unless they are in quotation marks and bound to the function parameters

The argument values are bound to the parameters in the same order they were given in the definition

The parameters are dummy variables (n1, n2, n3 in average3 ) used to make it possible to give a general definition

3.6 Predicates and Conditionals Predicates are function that test their arguments for some specific condition Except for the predicate member, predicates return true (t) or false (nil), depending on the arguments The most common predicates are atom equal evenp greaterp (or >) <= lessp (or <) >= listp null numberp oddp zerop

The predicate atom takes one argument. It returns t if the argument is an atom and nil otherwise

Equal takes two arguments and returns t if they evaluate to the same value, and nil otherwise

Evenp, numberp, oddp and zerop are tests on a single numeric argument. They return t if their argument evaluates to an even number, a number, an odd number, or zero respectively. Otherwise they can return nil

Greaterp and lessp each takes one or more arguments. If there is only one argument, each returns t. If more than one argument is used, greaterp returns t if the arguments, from left to right, are successively larger, otherwise nil is returned

Lessp requires that the arguments be smaller from left to right to return t, otherwise it returns nil

Predicates are to make tests in programs and take different actions based on the outcome of the test

Cond (for conditional) is like the if..then..else construct The syntax for cond is (cond (<test1> <action1>) (<test1> <action1>) . . . .

(<testk> <actionk>))

Each (<testi> <actioni>), i = 1, . . ., k is called a clause. Each clause consists of a test portion and an action or result portion The first clause following the cond is executed by evaluating <test1>. If this evaluates to non nil, the <action1> portion is evaluated, its value is returned and the remaining clauses are skipped over

If <test1> evaluates to nil, control passes to the second clause without evaluating <action1> and the procedure is repeated. If all tests evaluates to nil, cond returns nil. For example, to find maximum of two numbers -> (defun max (a b) (cond (( > a b) a) (t b))) MAX

When max function is executed, it starts with the first clause following the cond The test sequence is as follows: if a is greater than b, return a, else return b Note the t in the second clause preceding b. This forces the last clause to be evaluated when the first clause is not -> (max 10 20) 20

Logical functions may also be used for flow of control The basic logical operations are and or not

not takes one argument and returns t if the argument evaluates to nil, it returns nil if its argument evaluates to non-nil

The functions and and or both take any number of arguments. For both, the arguments are evaluated from left to right

In the case of and, if all arguments evaluate to non-nil, the value of the last argument is returned, otherwise nil is returned

The arguments of or are evaluated until one evaluates to non-nil, in which case it returns the argument value, otherwise it returns nil

For example: -> (setq x (a b c)) (A B C) -> (not (atom x)) T -> (not (listp x)) NIL -> (or (member e x) (member b x)) (B C) -> (or (equal c(car x)) (equal b(car x)))

NIL -> (and (listp x) (equal c (caddr x))) C -> (or (and (atom x) (equal a x)) (and (not (atom x)) (atom (car x)))) T 3.7 Input, Output and local variables

The most commonly used I/O functions are read, print, prinl, princ, terpri and format Read takes no arguments. When read appears in a procedure, processing halts until a single sexpression is entered from the keyboard. The s-expression is then returned as the value of read and processing continues. For example: -> (+ (read)) 6 11

When the interpreter looked for the second argument for +, if found the read statement which caused it to halt and wait for an input from the keyboard. If we entered 6 as indicated, read returns this value, processing continues, and the sum 11 is returned

Print takes one argument. It prints the argument as it is received, and then returns the argument. This makes it possible to print something and also pass the same thing on to another function as an argument.

When print is used to print an expression, its argument is preceded by the carriage-return and line-feed characters and is followed by a space. For example: -> (print (a b c)) (A B C) (A B C)

-> (print hello) hello hello

The first print prints its argument and then returns it, causing it to be printed by the readevaluate-print loop The print even prints the double quotation marks defining the string Prinl is same as the print except that the new-line characters and space are not provided For example: -> (prinl (hello)) (prinl (hello))) (HELLO)(HELLO)

We can avoid the double quotation marks in the output by using the printing function princ. It is same as prinl except it does not print the unwanted quotation marks -> (princ hello) Hello HELLO

Princ eliminated the quotes, but the echo still remains. Again, that is because princ returned its argument and since that was the last thing returned, it was printed by the read-evaluateprint loop

The function terpri takes no argument. It introduces a new-line wherever it appears and then returns nil For example: to compute the area of a circle -> (defun circle-area () (terpri) (princ enter the radius) (setq rad (read)) (princ The area of the circle is: ) (princ (* 3.1416 rad rad))

(terpri)) CIRCLE-AREA -> (circle-area) Enter the radius 4 The area of the circle is: 50.2656

The princ permits us to print multiple items on the same line and to introduce a new-line sequence we use terpri The format function permits us to create cleaner output than is possible with just the basic printing functions It has the form (format <destination> <string> arg1, arg2, ) Destination specifies where the output is to be directed, like to the monitor or some other external file Destination will always be t to signify the default output, the monitor String is the desired output string, but intermixed with format directives which specify how each argument is to be represented Directive appear in the string in the same order the arguments are to be printed Each directive is preceded with a tilde characters (~) to identify it as a directive The most common directives are

~A The argument is printed as though princ were used ~S The argument is printed as though prinl were used ~D The argument which must be an integer is printed as a decimal number ~F The argument which must be a floating point number is printed as a decimal floating-point number ~C The argument is printed as character output ~% A new-line is printed

The field width for appropriate argument values are specified with an integer immediately following the tilde symbol For example: ~5D specifies an integer field of width 5 -> (format t Circle radius = ~2F~% Circle area = ~3F x y)

"Circle radius = 3.0 Circle area = 9.42

Designate local variables rather than the global assignments which result from setq Parameters named as arguments in a function are accessible only within the function For example, consider the variables x and y in the following -> (setq y (a b c)) (A B C) -> (setq x (d e f)) (D E F) -> (defun local-var (x) (setq y (cons x y))) LOCAL-VAR -> (local-var 6) (6 A B C) -> x (D E F) -> y (A B C)

The variable x in the defun is local in scope. It reverts back to its previous value after the function local-var is exited The variable y is global. It is accessible from any procedure and retains its value unless reset with setq The let and prog constructs permits the creation of local variables The syntax for the let function is (let ((var1 val1) (var2 val2)) <s-expressions> )

Where each vari is a different variable name and vali is an initial value assigned to each vari respectively When the let is executed, each vali is evaluated and assigned to the corresponding vari, and the s-expressions which follow are then evaluated in order. The value of the last expression evaluated is then returned

If an initial value is not returned with a vari , it is assigned nil, and the parentheses enclosing may be omitted -> (let ((x a) (y b) (z c)) (cons x (cons y (list z)))) (A B C)

The prog function is similar to let in that the first arguments following it are a list of local variables where each element is either a variable name or a list containing a variable name and its initial value

This is followed by the body of the prog, and any number of s-expressions Prog executes list s-expressions in sequence and returns nil unless it encounters a function call named return. In this case a single argument of return is evaluated and returned Prog also permits the use of unconditional go statements and labels to identify the go-to transfer locations With the go and label statements, prog permits the writing of unstructured programs For example, the main function memb requires two arguments, an element and a list -> (defun memb (el 1st) (prog () Start (cond (( equal el (car 1st)) (return 1))) (setq 1st (cdr 1st)) (go start)))

MEMB

The prog here requires no local variables. The label start, the transfer point for the go statement. The second clause of the cond executes when the first clause is skipped because setq is non-nil

3.8 Iteration and recursion

The structured form of iteration with the do construct is (do (<var1 val1> <var-update1>) (<var2 val2> <var-update2>) . . (<test> <return-value>) (<s-expression>))

The vali are initial values which are all evaluated and then bound to the corresponding variables vari in parallel After the variables are updated during an iteration, the test is evaluated, and if it returns true, the return value is evaluated and returned The s-expressions forming the body of the construct are optional. If present, they are executed each iteration until an exit test condition is encountered For example, the factorial function -> (defun fact(n) (do ((count n(- count 1)) (product n (* product (- count 1)) ((equal 0 count) product))) FACT

In this definition there is no need to include a body

There is also a do* construct which is the same as do, except the assignment of initial values is made to each vari sequentially before the next form is evaluated Loop has the simple form (loop <s-expressions>)

Where the s-expressions are evaluated repeatedly until a call to a return is encountered let and other functions can be embedded within the loop construct if local variables are needed A recursive function is one which calls itself successively to reduce a problem to a sequence of similar steps Recursion requires a stopping condition and a recursive step For example, the factorial of a number -> (defun fact(n) (cond (( zerop n) 1) (t (* n (fact (-n 1)))))) FACT -> (fact 6) 720

3.9 Property lists and arrays

The most useful unique features of LISP as an AI language is the ability to assign properties to atoms Foe example: an object, say an atom which represents a person, can be given a number of properties such as height, weight, address Property list functions permit one to assign such properties to an atom, and to retrieve, replace or remove them as required.

The function putprop assigns properties to an atom. It takes three arguments: an object name, a property or attribute name, and a property or attribute value For example to assign properties to a car -> (putprop car ford make) FORD -> (putprop car 2009 year) 2009 -> (putprop car red color) RED

The form of putprop is (putprop object value attribute) where value is returned The remprop function takes two arguments, the object and its attributes Properties are global assignments To retrieve a property value, such as color of car, use the function get The function get takes two arguments object and attribute -> (get car color) RED -> (get car make) FORD -> (putprop car green color) GREEN -> (get car color) GREEN

The property value may be an atom or list Some versions of common LISP do not provide the putprop function, so define our own -> (defun putprop (object value property)

(setf (get object property) value)) PUTPROP

Setf function is like setq function Setf is an assignment function , which takes two arguments, the first may be either an atom or an access function (like car, cdr and get) and the second arguments are the value to be assigned

For example:

-> (setf (get car color) pink) PINK -> (get car color) PINK

Single and multiple-dimension arrays may be defined in LISP using the make-array function Foe example, to create an array with ten cells named myarray, we bind the unquoted name to an array using sef or setq with the make-array function and a specification of the number of cells -> (setf myarray (make-array (10))) # A(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)

The function returns the pound sign followed by an A and the array representation with its cells initially set to nil To access the contents of cells, use the function aref which takes two arguments, the name of the array and the index value. The cells are indexed starting at zero -> (aref myarray 9) NIL

To store the items in the array, use the function setf For example: to store the items 25 and red in the first and second cells of myarray -> (setf (aref myarray 0) 25) 25 -> (setf (serf myarray 1) red)

RED 3.10 PROLOG and other AI programming languages Prolog is a logical and a declarative programming language. The name itself, Prolog, is short for PROgramming in LOGic. Prolog's heritage includes the research on theorem provers and other automated deduction systems developed in the 1960s and 1970s. The "first" Prolog was "Marseille Prolog" based on work by Colmerauer (1970). The first detailed description of the Prolog language was the manual for the Marseille Prolog interpreter (Roussel, 1975). The other major influence on the nature of this first Prolog was that it was designed to facilitate natural language processing. Prolog is the major example of a fourth generation programming language supporting the declarative programming paradigm. The Japanese Fifth-Generation Computer Project, announced in 1981, adopted Prolog as a development language, and thereby focused considerable attention on the language and its capabilities. Lisp and Prolog are the most frequently used symbolic programming languages for artificial intelligence. They are widely regarded as excellent languages for "exploratory" and "prototype programming". How to Run Prolog

To start an interactive SWI-Prolog session under Unix, open a terminal window and type the approprite command. For example, on our Mac this is ... /opt/local/bin/swipl $

Under Windows, SWI-Prolog installs a start icon that can be double-clicked to initiate the interpreter. The interpreter then starts in its own command window. A startup message or banner may appear, and that will soon be followed by a goal prompt looking similar to the following ?- _ Interactive goals in Prolog are entered by the user following the '?- ' prompt.

Many Prologs have command-line help information. SWI Prolog has extensive help information. This help is indexed and guides the user. To learn more about it, try? help (help).

Notice that all of the displayed symbols need to be typed in, followed by a carriage return.

To illustrate some particular interactions with prolog, consider the following sample session. Each file referred to is assumed to be a local file in the user's account, which was created by the user, obtained by copying directly from some other public source, or obtained by saving a text file while using a web browser. The way to achieve the latter is either to follow a URL to the source file and then save, or to select text in a Prolog Tutorial web page, copy it, paste into a text editor window and then save to file.

The comments /* ... */ next to goals are referred to in the notes following the session. ?- ['2_2.pl']. yes ?- listing(factorial/2). /* 2. List program to the screen*/ /* 1. Load a program from a local file*/

factorial(0,1).

factorial(A,B) :A > 0, C is A-1, factorial(C,D), B is A*D. yes

?- factorial(10,What). What=3628800

/* 3. Compute factorial of 10 */

?- ['2_7.pl']. ?- listing(takeout).

/* 4. Load another program */

takeout(A,[A|B],B). takeout(A,[B|C],[B|D]) :takeout(A,C,D). yes

?- takeout(X,[1,2,3,4],Y). /* 5. Take X out of list [1,2,3,4] */ X=1 Y=[2,3,4] ; X=2 Y=[1,3,4] ; X=3 Y=[1,2,4] ; X=4 Y=[1,2,3] ; no Prolog waits ... User types ';' and Enter again ... again ... again ... Means: No more answers.

?- takeout(X,[1,2,3,4],_), X>3. /* 6. Conjunction of goals */ X=4 ; no

?- halt.

/* 7. Return to OS */

A Prolog goal is terminated with a period "." In this case the goal was to load a program file. This "bracket" style notation dates back to the first Prolog implementations. Several files can be chain loaded by listing the filenames sequentially within the brackets, separated by commas.

The built-in predicate 'listing' will list the program in memory -- in this case, the factorial program. Compiled predicates do not have an interactive source listing that can be supplied by a 'listing' goal. So, in order to illustrate this Prolog interpreter feature, the predicates were declared as dynamic in the source code before this sample run.

The goal here, 'factorial(10,What)', essentially says "the factorial of 10 is What?". The word 'What' begins with an upper-case letter, denoting a logical variable. Prolog satisfies the goal by finding the value of the variable 'What'.

Both "programs" now reside in memory, from the two source files 2_2.pl and 2_7.pl. In the program just loaded is a definition of the logical predicate 'takeout'. The goal 'takeout(X,[1,2,3,4],Y)' asks that X be taken out of list [1,2,3,4] leaving remainder list Y, in all possible ways. There are four ways to do this, as shown in the response. However, how Prolog is prodded to produce all of the possible answers: After producing each answer, Prolog waits with a cursor at the end of the answer. If the user types a semicolon ';' , Prolog will look for a next answer, and so on. If the user just hits Enter, then Prolog stops looking for answers.

A compound or conjunctive goal asks that two individual goals be satisfied. Note the arithmetic goal (built-in relation), 'X>3'. Prolog will attempt to satisfy these goals in

the left-to-right order, just as they would be read. In this case, there is only one answer. Note the use of an anonymous variable '_' in the goal, for which no binding is reported ("don't-care variable"). The 'halt' goal always succeeds and returns the user to the operating system.

Two factorial definitions To compute the mathematical functions using Prolog. Two predicate definitions that calculate the factorial function are in file 2_2.pl, which the reader can view by clicking on the 'Code' link at the bottom of this page. The first of these definitions is: factorial(0,1).

factorial(N,F) :N>0, N1 is N-1, factorial(N1,F1), F is N * F1. This program consists of two clauses. The first clause is a unit clause, having no body. The second is a rule, because it does have a body. The body of the second clause is on the right side of the ':-' which can be read as "if". The body consists of literals separated by commas ',' each of which can be read as "and". The head of a clause is the whole clause if the clause is a unit clause, otherwise the head of a clause is the part appearing to the left of the colon in ':-'. A declarative reading of the first (unit) clause says that "the factorial of 0 is 1" and the second clause declares that "the factorial of N is F if N>0 and N1 is N-1 and the factorial of N1 is F1 and F is N*F1". The Prolog goal to calculate the factorial of the number 3 responds with a value for W, the goal variable: ?- factorial(3,W). W=6 Consider the following clause tree constructed for the literal 'factorial(3,W)'. The clause tree does not contain any free variables, but instead has instances (values) of

variables. Each branching under a node is determined by a clause in the original program, using relevant instances of the variables; the node is determined by some instance of the head of a clause and the body literals of the clause determine the children of the node in the clause tree.

Fig. 3.1 All of the arithmetic leaves are true by evaluation (under the intended interpretation), and the lowest link in the tree corresponds to the very first clause of the program for factorial.

That first clause could be written factorial (0,1): - true. and, in fact, ?- true is a Prolog goal that always succeeds (true is built-in). For the sake of brevity, we have not drawn 'true' leaves under the true arithmetic literals.

The program clause tree provides a meaning of the program for the goal at the root of the tree. That is, 'factorial(3,6)' is a consequence of the Prolog program, because there is a clause tree rooted at 'factorial(3,6)' all of whose leaves are true. The literal 'factorial(5,2)' is, on the other hand, not a consequence of the program because there is no clause tree rooted at 'factorial(5,2)' having all true leaves. Thus the meaning of the program for the literal 'factorial(5,2)' is that it is false. ?- factorial(3,6). yes ?- factorial(5,2). no

Clause trees are so-called AND-trees, since, in order for the root to be a consequence of the program, each of its subtrees must also be rooted at literals which are themselves consequences of the program. We have indicated that clause trees provide a meaning or semantics for programs. Clause trees do provide an intuitive, as well as a correct, approach to program semantics.

To distinguish between the program clause trees and so-called Prolog derivation trees. The clause trees are "static" and can be drawn for a program and goal regardless of the particular procedural goal-satisfaction mechanism. The clause trees correspond to the declarative reading of the program.

Derivation trees, on the other hand, take into account the variable-binding
mechanism of Prolog and the order that subgoals are considered. A trace of a Prolog execution also shows how variables are bound in order to satisfy goals. The following sample shows how a typical Prolog tracer is turned on and off. ?- trace. % The debugger will first creep -- showing everything (trace).

yes [trace] ?- factorial(3,X). (1) 0 Call: factorial(3,_8140) ? (1) 1 Head [2]: factorial(3,_8140) ? (2) 1 Call (built-in): 3>0 ? (2) 1 Done (built-in): 3>0 ? (3) 1 Call (built-in): _8256 is 3-1 ? (3) 1 Done (built-in): 2 is 3-1 ? (4) 1 Call: factorial(2, _8270) ? ... (1) 0 Exit: factorial(3,6) ? X=6 [trace] ?- notrace. % The debugger is switched off

yes Here is the other one, with the same predicate name, but using three variables. factorial(0,F,F). factorial(N,A,F) :N > 0, A1 is N*A, N1 is N -1, factorial(N1,A1,F). For this version, use the following type of a goal: ?- factorial(5,1,F). F=120 The second parameter in the definition is a so called an accumulating parameter. This version is properly tail recursive. Prolog Lists Prolog uses brackets [...] as a list builder. The notation [X|Y] refers to a list whose first element is X and whose tail is Y. A finite list can be explicitly enumerated, such as [1,2,3,4]. The following three definitions should make sense to a Lisp programmer, where 'car' refers to the first element of a list, 'cdr' refers to the tail or rest of the list, and 'cons' is the list constructor. car([X|Y],X).

cdr([X|Y],Y).

cons(X,R,[X|R]).

meaning ...

The head (car) of [X|Y] is X. The tail (cdr) of [X|Y] is Y. Putting X at the head and Y
as the tail constructs (cons) the list [X|R]. However, we will see that these explicit definitions are unneeded. A list whose head is X and whose tail is Y can just be referred to using the Prolog term [X|Y]. Conversely, if the list can be unified with the Prolog term '[X|Y]' then the first element of the list is bound to (unified with) X and the tail of the list is bound to Y. Consider the following definition of the predicate 'member/2'. member(X,[X|R]). member(X,[Y|R]) :- member(X,R). One can read the clauses the following way, respectively:

X is a member of a list whose first element is X. X is a member of a list whose tail is


R if X is a member of R.
This program can be used in numerous ways. One can test membership: ?- member(2,[1,2,3]). Yes One can generate members of a list: ?- member(X,[1,2,3]). X=1; X=2; X=3; No Here is a derivation tree showing how this last goal generated all of the answers.

Fig. 3.2 Each left branch corresponds to a match (unification) against the first clause for 'member' and each right branch corresponds to a match against the second clause. The subgoal 'member(X,[])' on the lowest right branch will not match the head of any 'member' clause. In particular '[]' will not unify with a pattern of the form '[X|R]' because the latter represents a list with at least one element. We will find many other uses for 'member'. This example query ... ?- member([3,Y], [[1,a],[2,m],[3,z],[4,v],[3,p]]). Y=z; Y=p; No ... suggests a use where one intends to search in order to find elements paired with a specified element. Here is another, finding elements of a list which satisfy some constraint: ?- member(X,[23,45,67,12,222,19,9,6]), Y is X*X, Y < 100. X = 9 Y = 81 ; X = 6 Y = 36 ; No The definition for 'member' is usually written

member(X,[X|_]). member(X,[_|R]) :- member(X,R). where '_' (underscore) designates a "don't-care" variable, usually called anonymous variables. In general, such variables have names whose first character is the underscore. In effect, they match any Prolog term, but no variable binding results from the free match. Notice that this is consistent with the original intentions of the definition of 'member'. Not having to bind values to anonymous variables saves a little run-space and run-time. Related to 'member' is the following definition for 'takeout'. takeout(X,[X|R],R). takeout(X,[F|R],[F|S]) :- takeout(X,R,S). These clauses can be paraphrased in English as follows:

When X is taken out of [X|R], R results. When X is taken out of the tail of [X|R], [X|S] results, where S is

the result of taking X out of R.


For example, ?- takeout(X,[1,2,3],L). X=1 L=[2,3] ; X=2 L=[1,3] ; X=3 L=[1,2] ; No Notice that it would not be appropriate to use any anonymous variables in the definition of 'takeout'. Here is a program clause tree showing that 'takeout(3, [1,2,3],[1,2])' is a consequence of the definition. Pay particular attention to exactly how the clauses are used to construct the tree. takeout(3,[1,2,3],[1,2]) | | takeout(3,[2,3],[2]) |

| takeout(3,[3],[]) | | true The following goal, ?- takeout(3,W,[a,b,c]). W = [3,a,b,c] ; W = [a,3,b,c] ; W = [a,b,3,c] ; W = [a,b,c,3] ; No shows that 'takeout(X,Z,W)' can also be interpreted as "insert X into W to produce Z". That is, 'takeout' got its name from just one of its uses. Of course, one could define putin(X,L,R) :- takeout(X,R,L). Here is a definition for appending, or concatenating, two Prolog lists. append([X|Y],Z,[X|W]) :- append(Y,Z,W). append([],X,X). Several kinds of goals are possible: ?- append([1,2,3],[4,5],[1,2,3,4,5]). Yes

?- append([1,2,3],[4,5],A). A = [1,2,3,4,5]

?- append([1,2,3],W,[1,2,3,4,5]). W = [4,5]

... and so on. Reversing a list can be done with

reverse([X|Y],Z,W) :- reverse(Y,[X|Z],W). reverse([],X,X). This program illustrates Prolog's approach to an important strategy -- using an accumulating parameter (the middle variable) -- to accumulate a list answer until the computation is finished. For example, consider the following (partial) derivation tree ?- reverse([1,2,3],[],A) | | reverse([2,3],[1],A) | | reverse([3],[2,1],A) | | reverse([],[3,2,1],A) | | true A = [3,2,1] where the first 'reverse' clause is use three times, and then the second 'reverse' clause is used to "capture" the answer by matching the second and third arguments. One could use the following definition to "hide" the accumulating parameter. reverse(A,R) :- reverse(A,[],R). Here is an interesting definition designed to produce all possible permutations of a list. perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W). perm([],[]). Think of 'takeout(X,Z,W)' as being used in the "X put into W yields Z" sense here. Then the definitions could paraphrased as follows:

Z is a permutation of [X|Y] provided W is a permutation of Y and

then X is put into W to produce Z . [] is the (only) permutation of [].

Here is a sample goal for 'perm': ?- perm([1,2,3],P). P = [1,2,3] ; P = [2,1,3] ; P = [2,3,1] ; P = [1,3,2] ; P = [3,1,2] ; P = [3,2,1] ; No The user should try the goal '?- perm(P,[1,2,3]).' It is common to represent sets as Prolog lists. This representation has some flaws, such as the fact that Prolog lists are inherently ordered (sets are not), and a list can have multiple occurrences of a particular element (sets do not). However, the list representation is very convenient. Set membership can be computed using the 'member' relation for lists previously discussed. Subsets can be tested using subset([X|R],S) :- member(X,S), subset(R,S). subset([],_). Goals like ?- subset([4,3],[2,3,5,4]). Yes work fine. Why would '?- subset([1,2],W)' and '?- subset(A,[1,2,3])' not be reasonable goals? Union and intersection can be approximated using the following Prolog list versions: union([X|Y],Z,W) :- member(X,Z), union(Y,Z,W).

union([X|Y],Z,[X|W]) :- \+ member(X,Z), union(Y,Z,W). union([],Z,Z).

intersection([X|Y],M,[X|Z]) :- member(X,M), intersection(Y,M,Z). intersection([X|Y],M,Z) :- \+ member(X,M), intersection(Y,M,Z). intersection([],M,[]). These are intended to be used for goals where the first two variables already have a list value. Sometimes this intention is indicated by writing something like 'union(+,+,-)' to indicate the intended variable profile. For example, ?- union([1,2,3,4],[1,a,b,4],A). A = [2,3,1,a,b,4]

?- intersection([1,2,3,4],[1,a,b,4],B). B = [1,4] Why would goals like '?- union(X,[2,3],[1,3,a,2])' cause difficulty? Some run-time inefficiency results from having to rematch the heads of clauses for both of the definitions. Here is an alternate version of union, using ! instead: union([X|Y],Z,W) :member(X,Z), !, /* do not use next clauses */

union(Y,Z,W). union([X|Y],Z,[X|W]) :- union(Y,Z,W). union([],Z,Z). Ex: Design and test 'prune(A,B)' which is intended to remove multiple occurrences of elements from A to produce result B. For example, ?- prune([a,1,b,2,a,3,a,4,b],B). B = [a,1,b,2,3,4] Try to make it so that B has remaining elements in the order that they occurred in A.

Ex: Design and test 'prefix(A,B)' which tests to see if A is a list prefix of B, and which can generate prefixes of a given list. For example, ?- prefix([1,2,3],[1,2,3,4,5]). yes

?- prefix([1,2],[1,a,2,3]). No

?- prefix(W,[1,2,3]). W = [] ; W = [1] ; W = [1,2] ; W = [1,2,3] ; No Ex: Design a Prolog predicate 'segment', that tests whether its first list argument is a contiguous segment contained anywhere within the second list argument. For example, ?- segment([a,b,c],[1,c,a,b,c,3]). Yes

?- segment([a,b],[c,a,c,b]). No Various sorting strategies can be implemented using lists in prolog. Here is a Prolog version of merge sort, with intended profile mergesort(+,-). mergesort([],[]). /* covers special case */

mergesort([A],[A]). mergesort([A,B|R],S) :split([A,B|R],L1,L2), mergesort(L1,S1), mergesort(L2,S2), merge(S1,S2,S).

split([],[],[]). split([A],[A],[]). split([A,B|R],[A|Ra],[B|Rb]) :- split(R,Ra,Rb).

merge(A,[],A). merge([],B,B). merge([A|Ra],[B|Rb],[A|M]) :- A =< B, merge(Ra,[B|Rb],M). merge([A|Ra],[B|Rb],[B|M]) :- A > B, merge([A|Ra],Rb,M). Here is a sample goal: ?- mergesort([4,3,6,5,9,1,7],S). S=[1,3,4,5,6,7,9] Prolog list sorting routines suffer all of the relative space and time inefficiencies related with sorting dynamic structures, but often have very cogent specifications in Prolog. Prolog Sequences The kind of sequences most used in Prolog are "comma" sequences. There is

no empty sequence (unlike for lists). The shortest sequence has one element. Longer sequences have elements separated by commas ",". An appropriate declaration for the comma operator would be :- op(1000,xfy,','). meaning that comma is right-associative with precedence 1000. (The comma operator is actually built-in.) Here is some Prolog behavior. ?- (H,T) = (1,2,3,4). H=1 T = 2,3,4

?- (a) = a. Yes

?- (H,T) = (a). No

?- (A,B,C) = (1,2,3,4,5). A=1 B=2 C = 3,4,5 Prolog clauses use comma sequences. ?- assert((a(X) :- b(X),c(X),d(X))). %% Note parens around clause X = G1056

?- clause(a(X),Body), Body=(First,Next). First = b(G1176) Next = c(G1176), d(G1176) Body = b(G1176), c(G1176), d(G1176) X = G1176 Procesing sequences is similar to processing lists, except that the base case

for sequences is a unit sequence (one element), whereas for lists the base case is for the empty list. For example, here is a program to append comma sequences ... sequence_append((X,R),S,(X,T)) :!, sequence_append(R,S,T). sequence_append((X),S,(X,S)). Note the use of cut (!) to make sure that the second clause is not available as a alternate choice for multi-element sequences. ?- sequence_append((1,2,3),(a,b,c,d),S). S = 1, 2, 3, a, b, c, d Other kinds of sequences can be defined by the user. For example, to make leftassociative sequences separated by '#', one might use an operator declaration like this ... ?- op(500,yfx,'#'). Yes

?- (A#B) = 1#2#3#4. B=4 A=1#2#3

Notice how left-associativity was what determined the bindings in the second goal! Prolog may be considered as a relational language, a non-deterministic language, and a database language. Prolog as a Relational Language Prolog is an interpreted language. The current program consists of whatever procedures have been loaded. A call to any procedure in the program may be made by typing the call in response to the prompt. A number of procedures are built-in, that is they exist before any other program is loaded. Among the built-in procedures is consult, which loads a program from a file. Type: consult (<filename>). in response to the prompt, and the procedures in the file name will be loaded. Note the full-stop at the end. If the filename starts with a capital letter, or contains non-alphanumeric ('<filename>'). The file will be taken from your top level directory, but you may use a full path to access files in other directories, for instance: consult(~/prologprogs/ex1.pro). To exit the Prolog system type: halt. Help on any Prolog built-in procedure can be obtained by calling the built-in procedure help, e.g: help (consult). Prolog is a relational language. That expressing a relation between its is, a Prolog call may can be be regarded as characters, will have to enclose it within quotes: consult

arguments.

This

contrasted with

functional languages where a call may be regarded as a mathematical function. Both functional and relational languages however are considered declarative, that is they break away from the idea of a programming language as a set of instructions to manipulate the memory of a computer (such languages are referred to as imperative), and instead concentrate on providing a precise notation in which computation is a form of reasoning. Prolog originates from attempts to provide a computer-executable form of mathematical logic notation (its name comes from Programming in Logic), thus it and similar languages are often referred to as the logic languages. To return to the idea of relations, in a functional language we might have the function square, which takes an integer and squares it; a call square(5) is considered as rewriting to 25, expressing the functional relation square(5)?25. The equivalent in

Prolog is a call square(5,X).

Here X is a Prolog variable. If a correct program for

square has been loaded, typing square(5,X). will cause X = 25 to be printed, indicating that X must have the value 25 for the relation square(5,X) to be true. You need to type a carriage return after this to get the prompt for the next call. A program for square consists of just the single line: square(X,Y) :- Y is X*X. This says that X and Y are in the relationship square if Y is X*X evaluated arithmetically. Considering declarative languages in terms of the imperative

language like C, a functional language may be considered as programming in terms of functions which return values but do not have any side-effects. A logic language may be considered as programming in procedures which do not

return values directly (void in C terminology) and in which variable parameters are always passed by reference (preceded by & in C terminology) though the variables can only be assigned new values within the procedure if they were unassigned when the procedure was called. A Prolog procedure for a k-ary relation (i.e. one which takes k arguments), named proc takes the form: proc(X 1 , X k ) :- call 1 , , call n . where X 1 , X k are Prolog variables, and call 1 , , call n Prolog procedure calls. Note that a variable name may be any alphanumeric string starting with a Capital letter, while a procedure name may be any alphanumeric string starting with a small letter. The underscore character '_' is treated as an additional capital letter in names. Each of call 1 , , call n may contain variables from X 1 , X k among its arguments, or new variables, or constants. When a call proc(arg 1 , arg k ) is made, any occurrence of a variable X i among the calls is replaced by arg i . Each of call 1 , , call n is then executed in

turn. As an example, if we have the existing procedure square, a procedure to give the difference of two squares may be given by: diff2squares(X,Y,D) :- square(X,X2), square(Y,Y2), D is X2-Y2. The call is (expressible in infix form) is used to give arithmetic calculation. Dont mistakenly use = in its place, as that has a different effect.

If the left-hand side of is is a variable, and the right-hand side an arithmetic formula, the result is that execution of the is call will cause the variable to store the result of evaluating the formula.

One very important point to note is that Prolog variables may only be assigned a value once in a procedure body. You could not have: diff2squares(X,Y,A) :- square(X,A), square(Y,B), A is A-B. since here, once A has been assigned a value in the call square(X,A), that value cannot be changed by the further call A is A-B.

3.11 Summary In this chapter we introduced LISP, the programming language of AI. We defined LISP syntax and examined a number of built-in functions, including the numeric functions (+, -, *, /), and the list manipulation functions (car, cdr, cons, list, append, last, member, and reverse). We also defined a number of predicate functions such as equal, null, numberp, atom, listp, and so on. We then described how to define our own functions using defun. We saw how to use the conditional cond and the logical functions and, or, and not. The input-output functions print, prinl, princ, read, terpri, and format were defined and example programs presented. We saw how to write iterative programs with the do, loop, and prog constructs. We saw how to define and use local variables with the let and prog functions. We discussed recursion, and saw examples of its use. We then looked at property lists, a valuable method with which to assign multiple values or properties to variable atoms. Array definitions and examples were briefly introduced. And a few miscellaneous topics such as mapping functions (mapcar), the lambda form, and internal list representations concluded our treatment of LISP. Finally, we introduced the logic programming language PROLOG, and gave some simple examples of its use. We concluded the chapter with a few comments regarding other AI languages.

3.12 Keywords LISP Prolog Atom

List String Setq Cons Car Cdr Append Member reverse Last Defunct Equal Evenp Numberp Greatyerp Lessp cond

3.13 Exercise 1. 2. Write a LISP program to convert centigrade temperatures to fahrenheit. Define a function called first element that takes a list as its argument and returns the first top element of the list. 3. Define a function called number of elements that takes a list as its only argument and returns the number of top elements in the list. 4. Define a function called rotate that takes a list and rotates the element by one position as in (rotate (a b c d)) return (D A B C). 5. Define a function new list that takes one argument and returns it as a list. If the argument is already a list, including the empty list, new list returns it without change. If the argument is an atom it returns it as a list. 6. Define a function named add list that takes two arguments an item and a list. If the item is in the list, the function returns the list unaltered. If the item is not in the list the function returns the list with the item entered as the first element. For example: (add list c (a b c d)) returns (a b c d) but (add list e (a b c)) returns (e a b c)

7.

Write a function called word member, which takes one list as argument. The function should print a prompt to the user (please type any word). If the word typed is a member of the list, the function should return t otherwise nil.

8.

Write an iterative function named nth item that takes two arguments, a positive integer and a list. The function returns the item in the nth position in the list. If the integer exceeds the number of elements in the list, it returns nil.

9. Write a recursive function named power that takes two numeric arguments n and m. The
function computes the nth power of m(mn). Be sure to account for the case where n = 0. 10. Write a function called intersection, which takes two lists as arguments. The function should return a list containing single occurrences of all elements which appear in both input list.

You might also like