lisp expressions

39
The name LISP derives from "LISt Processing". Lisp (historically, LISP) is a family of computer programming languages with a long history and a distinctive, fully parenthesized Polish prefix notation. Originally specified in 1958, Lisp is the second-oldest high-level programming language in widespread use today; only Fortran is older (by one year). Like Fortran, Lisp has changed a great deal since its early days, and a number of dialectshave existed over its history. Today, the most widely known general-purpose Lisp dialects are Common Lisp and Scheme. Lisp was originally created as a practical mathematical notation for computer programs, influenced by the notation of Alonzo Church's lambda calculus. It quickly became the favored programming language for artificial intelligence (AI) research. As one of the earliest programming languages, Lisp pioneered many ideas in computer science, including tree data structures, automatic storage management, dynamic typing, conditionals, higher-order functions, recursion, and the self-hosting compiler. Linked lists are one of Lisp language's major data structures, and Lisp source code is itself made up of lists. As a result, Lisp programs can manipulate source code as a data structure, giving rise to the macro systems that allow programmers to create new syntax or new domain-specific languages embedded in Lisp. The interchangeability of code and data also gives Lisp its instantly recognizable syntax. All program code is written as s- expressions, or parenthesized lists. A function call or syntactic form is written as a list with the function or operator's name first, and the arguments following; for instance, a function f that takes three arguments might be called using (f arg1 arg2 arg3). There are actually a lot of different ideas that all can be used together in Lisp - functional programming, object oriented programming, etc.

Upload: jaspreet-singh

Post on 20-Apr-2017

223 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: LISP Expressions

The name LISP derives from "LISt Processing". Lisp (historically, LISP) is a family of computer programming languages with a long history and a distinctive, fully parenthesized Polish prefix notation. Originally specified in 1958, Lisp is the second-oldest high-level programming language in widespread use today; only Fortran is older (by one year). Like Fortran, Lisp has changed a great deal since its early days, and a number of dialectshave existed over its history. Today, the most widely known general-purpose Lisp dialects are Common Lisp and Scheme.Lisp was originally created as a practical mathematical notation for computer programs, influenced by the notation of Alonzo Church's lambda calculus. It quickly became the favored programming language for artificial intelligence (AI) research. As one of the earliest programming languages, Lisp pioneered many ideas in computer science, including tree data structures, automatic storage management, dynamic typing, conditionals, higher-order functions, recursion, and the self-hosting compiler. Linked lists are one of Lisp language's major data structures, and Lisp source code is itself made up of lists. As a result, Lisp programs can manipulate source code as a data structure, giving rise to the macro systems that allow programmers to create new syntax or new domain-specific languages embedded in Lisp.The interchangeability of code and data also gives Lisp its instantly recognizable syntax. All program code is written as s-expressions, or parenthesized lists. A function call or syntactic form is written as a list with the function or operator's name first, and the arguments following; for instance, a function f that takes three arguments might be called using (f arg1 arg2 arg3).There are actually a lot of different ideas that all can be used together in Lisp - functional programming, object oriented programming, etc.

Symbolic expressions (S-expressions)Lisp is an expression-oriented language. Unlike most other languages, no distinction is made between "expressions" and "statements";all code and data are written as expressions. When an expression is evaluated, it produces a value (in Common Lisp, possibly multiple values), which then can be embedded into other expressions. Each value can be any data type.

McCarthy's 1958 paper introduced two types of syntax: S-expressions (Symbolic expressions, also called "sexps"), which mirror the internal representation of code and data; and M-expressions (Meta Expressions), which express functions of S-expressions. M-expressions never found favor, and almost all Lisps today use S-expressions to manipulate both code and data. However, the S-expression syntax is also responsible for much of Lisp's power: the syntax is extremely regular, which facilitates manipulation by computer. However, the syntax of Lisp is not limited to traditional parentheses notation. It can be extended to include alternative notations. XMLisp, for instance, is a Common Lisp extension that employs the metaobject-protocol to integrate S-expressions with the Extensible Markup Language (XML).The reliance on expressions gives the language great flexibility. Because Lisp functions are themselves written as lists, they can be processed exactly like data. This allows easy writing of

Page 2: LISP Expressions

programs which manipulate other programs (metaprogramming). Many Lisp dialects exploit this feature using macro systems, which enables extension of the language almost without limit.

ListsA Lisp list is written with its elements separated by whitespace, and surrounded by parentheses. For example, (1 2 foo) is a list whose elements are three atoms: the values 1,2, and foo. These values are implicitly typed: they are respectively two integers and a Lisp-specific data type called a "symbolic atom", and do not have to be declared as such.The empty list () is also represented as the special atom nil. This is the only entity in Lisp which is both an atom and a list.Expressions are written as lists, using prefix notation. The first element in the list is the name of a form, i.e., a function, operator, macro, or "special operator" (see below). The remainder of the list are the arguments. For example, the function list returns its arguments as a list, so the expression (list '1 '2 'foo)evaluates to the list (1 2 foo). The "quote" before the arguments in the preceding example is a "special operator" which prevents the quoted arguments from being evaluated (not strictly necessary for the numbers, since 1 evaluates to 1, etc.). Any unquoted expressions are recursively evaluated before the enclosing expression is evaluated. For example, (list 1 2 (list 3 4))evaluates to the list (1 2 (3 4)). Note that the third argument is a list; lists can be nested.

Numeric values are not the only type of data LISP supports. LISP is designed for symbolic computing. The fundamental LISP data structure for supporting symbolic manipulation are lists. In fact, LISP stands for "LISt Processing."Lists are containers that supports sequential traversal. List is also a recursive data structure: its definition is recursive. As such, most of its traversal algorithms are recursive functions. In order to better understand a recursive abstract data type and prepare oneself to develop recursive operations on the data type, one should present the data type in terms of its constructors, selectors and recognizers.Constructors are forms that create new instances of a data type (possibly out of some simpler components). A list is obtained by evaluating one of the following constructors:

1. nil: Evaluating nil creates an empty list;2. (cons x L): Given a LISP object x and a list L, evaluating (cons x L) creates a list

containing x followed by the elements in L.Notice that the above definition is inherently recursive. For example, to construct a list containing 1 followed by 2, we could type in the expression: (cons 1 (cons 2 nil))(1 2)LISP replies by printing (1 2), which is a more readable representation of a list containing 1 followed by 2. To understand why the above works, notice that nil is a list (an empty one), and thus(cons 2 nil) is also a list (a list containing 1 followed by nothing). Applying the second

Page 3: LISP Expressions

constructor again, we see that (cons 1 (cons 2 nil)) is also a list (a list containing 1 followed by 2 followed by nothing).Typing cons expressions could be tedious. If we already know all the elements in a list, we could enter our list as list literals. For example, to enter a list containing all prime numbers less than 20, we could type in the following expression: (quote (2 3 5 7 11 13 17 19))(2 3 5 7 11 13 17 19)Notice that we have quoted the list using the quote special form. This is necessary because, without the quote, LISP would interpret the expression (2 3 5 7 11 13 17 19) as a function call to a function with name "2" and arguments 3, 5, ..., 19 The quote is just a syntactic device that instructs LISP not to evaluate the a form in applicative order, but rather treat it as a literal. Since quoting is used frequently in LISP programs, there is a shorthand for quote:'(2 3 5 7 11 13 17 19)(2 3 5 7 11 13 17 19)The quote symbol ' is nothing but a syntactic shorthand for (quote ...).The second ingredient of an abstract data type are its selectors. Given a composite object constructed out of several components, a selector form returns one of its components. Specifically, suppose a list L1 is constructed by evaluating (cons x L2), where x is a LISP object and L2 is a list. Then, the selector forms (first L1) and (rest L1) evaluate to x and L2 respectively, as the following examples illustrate: (first '(2 4 8))2 (rest '(2 4 8))(4 8) (first (rest '(2 4 8)))4 (rest (rest '(2 4 8)))(8) (rest (rest (rest '(8))))NILFinally, we look at recognizers, expressions that test how an object is constructed. Corresponding to each constructor of a data type is a recognizer. In the case of list, they are null for nil and consp for cons. Given a list L, (null L) returns t iff L is nil, and (consp L) returns t iff L is constructed from cons.USER(29): (null nil)TUSER(30): (null '(1 2 3))NILUSER(31): (consp nil)NILUSER(32): (consp '(1 2 3))TNotice that, since lists have only two constructors, the recognizers are complementary. Therefore, we usually need only one of them. In our following discussion, we use only null.

Page 4: LISP Expressions

Structural Recursion with ListsAs we have promised, understanding how the constructors, selectors and recognizers of lists work helps us to develop recursive functions that traverse a list. Let us begin with an example. The LISP built-in function list-length counts the number of elements in a list. For example,USER(33): (list-length '(2 3 5 7 11 13 17 19))8Let us try to see how such a function can be implemented recursively. A given list L is created by either one of the two constructors, namely nil or a cons:

Case 1: L is nil.The length of an empty list is zero.

Case 2: L is constructed by cons.Then L is composed of two parts, namely, (first L) and (rest L). In such case, the length of L can be obtained inductively by adding 1 to the length of (rest L).

Formally, we could implement our own version of list-length as follows:(defun recursive-list-length (L) "A recursive implementation of list-length." (if (null L) 0 (1+ (recursive-list-length (rest L)))))Here, we use the recognizer null to differentiate how L is constructed. In case L is nil, we return 0 as its length. Otherwise, L is a cons, and we return 1 plus the length of (rest L). Recall that(1+ n) is simply a shorthand for (+ n 1).Again, it is instructive to use the trace facilities to examine the unfolding of recursive invocations:USER(40): (trace recursive-list-length)(RECURSIVE-LIST-LENGTH)USER(41): (recursive-list-length '(2 3 5 7 11 13 17 19)) 0: (RECURSIVE-LIST-LENGTH (2 3 5 7 11 13 17 19)) 1: (RECURSIVE-LIST-LENGTH (3 5 7 11 13 17 19)) 2: (RECURSIVE-LIST-LENGTH (5 7 11 13 17 19)) 3: (RECURSIVE-LIST-LENGTH (7 11 13 17 19)) 4: (RECURSIVE-LIST-LENGTH (11 13 17 19)) 5: (RECURSIVE-LIST-LENGTH (13 17 19)) 6: (RECURSIVE-LIST-LENGTH (17 19)) 7: (RECURSIVE-LIST-LENGTH (19)) 8: (RECURSIVE-LIST-LENGTH NIL) 8: returned 0 7: returned 1 6: returned 2 5: returned 3 4: returned 4 3: returned 5 2: returned 6 1: returned 7

Page 5: LISP Expressions

0: returned 88The kind of recursion we see here is called structural recursion. Its standard pattern is as follows. To process an instance X of a recursive data type:

1. Use the recognizers to determine how X is created (i.e. which constructor creates it). In our example, we use null to decide if a list is created by nil or cons.

2. For instances that are atomic (i.e. those created by constructors with no components), return a trivial value. For example, in the case when a list is nil, we return zero as its length.

3. If the instance is composite, then use the selectors to extract its components. In our example, we use first and rest to extract the two components of a nonempty list.

4. Following that, we apply recursion on one or more components of X. For instance, we recusively invoked recursive-list-length on (rest L).

5. Finally, we use either the constructors or some other functions to combine the result of the recursive calls, yielding the value of the function. In the case of recursive-list-length, we return one plus the result of the recursive call.

Exercise: Implement a linearly recursive function (sum L) which computes the sum of all numbers in a list L. Compare your solution with the standard pattern of structural recursion.

Sometimes, long traces like the one for list-length may be difficult to read on a terminal screen. Common LISP allows you to capture screen I/O into a file so that you can, for example, produce a hard copy for more comfortable reading. To capture the trace of executing (recursive-list-length '(2 3 5 7 11 13 17 19)), we use the dribble command:USER(42): (dribble "output.txt")dribbling to file "output.txt" NILUSER(43): (recursive-list-length '(2 3 5 7 11 13 17 19)) 0: (RECURSIVE-LIST-LENGTH (2 3 5 7 11 13 17 19)) 1: (RECURSIVE-LIST-LENGTH (3 5 7 11 13 17 19)) 2: (RECURSIVE-LIST-LENGTH (5 7 11 13 17 19)) 3: (RECURSIVE-LIST-LENGTH (7 11 13 17 19)) 4: (RECURSIVE-LIST-LENGTH (11 13 17 19)) 5: (RECURSIVE-LIST-LENGTH (13 17 19)) 6: (RECURSIVE-LIST-LENGTH (17 19)) 7: (RECURSIVE-LIST-LENGTH (19)) 8: (RECURSIVE-LIST-LENGTH NIL) 8: returned 0 7: returned 1 6: returned 2 5: returned 3 4: returned 4 3: returned 5 2: returned 6

Page 6: LISP Expressions

1: returned 7 0: returned 88USER(44): (dribble)The form (dribble "output.txt") instructs Common LISP to begin capturing all terminal I/O into a file called output.txt. The trailing (dribble) form instructs Common LISP to stop I/O capturing, and closes the file output.txt. If we examine output.txt, we will see the following:dribbling to file "output.txt" NILUSER(43): (recursive-list-length '(2 3 5 7 11 13 17 19)) 0: (RECURSIVE-LIST-LENGTH (2 3 5 7 11 13 17 19)) 1: (RECURSIVE-LIST-LENGTH (3 5 7 11 13 17 19)) 2: (RECURSIVE-LIST-LENGTH (5 7 11 13 17 19)) 3: (RECURSIVE-LIST-LENGTH (7 11 13 17 19)) 4: (RECURSIVE-LIST-LENGTH (11 13 17 19)) 5: (RECURSIVE-LIST-LENGTH (13 17 19)) 6: (RECURSIVE-LIST-LENGTH (17 19)) 7: (RECURSIVE-LIST-LENGTH (19)) 8: (RECURSIVE-LIST-LENGTH NIL) 8: returned 0 7: returned 1 6: returned 2 5: returned 3 4: returned 4 3: returned 5 2: returned 6 1: returned 7 0: returned 88USER(44): (dribble)SymbolsThe lists we have seen so far are lists of numbers. Another data type of LISP is symbols. A symbol is simply a sequence of characters:USER(45): 'a ; LISP is case-insensitive.AUSER(46): 'A ; 'a and 'A evaluate to the same symbol.AUSER(47): 'apple2 ; Both alphanumeric characters ...APPLE2USER(48): 'an-apple ; ... and symbolic characters are allowed.AN-APPLEUSER(49): t ; Our familiar t is also a symbol.T

Page 7: LISP Expressions

USER(50): 't ; In addition, quoting is redundant for t.TUSER(51): nil ; Our familiar nil is also a symbol.NILUSER(52): 'nil ; Again, it is self-evaluating.NILWith symbols, we can build more interesting lists:USER(53): '(how are you today ?) ; A list of symbols.(HOW ARE YOU TODAY ?)USER(54): '(1 + 2 * x) ; A list of symbols and numbers.(1 + 2 * X)USER(55): '(pair (2 3)) ; A list containing 'pair and '(2 3).(pair (2 3))Notice that the list (pair (2 3)) has length 2:USER(56): (recursive-list-length '(pair (2 3)))2Notice also the result of applying accessors:USER(57): (first '(pair (2 3)))PAIRUSER(58): (rest '(pair (2 3)))((2 3))Lists containing other lists as members are difficult to understand for beginners. Make sure you understand the above example.Example: nthLISP defines a function (nth N L) that returns the N'th member of list L (assuming that the elements are numbered from zero onwards):USER(59): (nth 0 '(a b c d))AUSER(60): (nth 2 '(a b c d))CWe could implement our own version of nth by linear recursion. Given N and L, either L is nil or it is constructed by cons.

Case 1: L is nil.Accessing the N'th element is an undefined operation, and our implementation should arbitrarily return nil to indicate this.

Case 2: L is constructed by a cons.Then L has two components: (first L) and (rest L). There are two subcases: either N = 0 or N > 0:

o Case 2.1: N = 0.The zeroth element of L is simply (first L).

o Case 2.2: N > 0.The N'th member of L is exactly the (N-1)'th member of (rest L).

The following code implements our algorithm:(defun list-nth (N L)

Page 8: LISP Expressions

"Return the N'th member of a list L." (if (null L) nil (if (zerop N)

(first L) (list-nth (1- N) (rest L)))))Recall that (1- N) is merely a shorthand for (- N 1). Notice that both our implementation and its correctness argument closely follow the standard pattern of structural recursion. Tracing the execution of the function, we get:USER(61): (list-nth 2 '(a b c d)) 0: (LIST-NTH 2 (A B C D)) 1: (LIST-NTH 1 (B C D)) 2: (LIST-NTH 0 (C D)) 2: returned C 1: returned C 0: returned CC

Exercise: LISP has a built-in function (last L) that returns a the last cons structure in a given list L.USER(62): (last '(a b c d))(d)USER(63): (last '(1 2 3))(3)Implement your own version of last using linear recursion. You may assume that (last nil) returns nil. Compare your implementation with the standard pattern of structural recursion.

Notice that we have a standard if-then-else-if structure in our implementation of list-nth. Such logic can alternatively be implemented using the cond special form.(defun list-nth (n L) "Return the n'th member of a list L." (cond ((null L) nil) ((zerop n) (first L)) (t (list-nth (1- n) (rest L)))))The cond form above is evaluated as follows. The condition (null L) is evaluated first. If the result is true, then nil is returned. Otherwise, the condition (zerop n) is evaluated. If the condition holds, then the value of (first L) is returned. In case neither of the conditions holds, the value of (list-nth (1- n) (rest L)) is returned.

Exercise: Survey CLTL2 section 7.6 (pages 156-161) and find out what other conditional special forms are available in Common LISP. Do you know when the special forms when and unlessshould be used instead of if?

Example: member

Page 9: LISP Expressions

LISP defines a function (member E L) that returns non-NIL if E is a member of L.USER(64): (member 'b '(perhaps today is a good day to die)) ; test failsNILUSER(65): (member 'a '(perhaps today is a good day to die)) ; returns non-NIL'(a good day to die)We implement our own recursive version as follows:(defun list-member (E L) "Test if E is a member of L." (cond ((null L) nil) ((eq E (first L)) t) (t (list-member E (rest L))))) The correctness of the above implementation is easy to justify. The list L is either constructed by nil or by a call to cons:

Case 1: L is nil.L is empty, and there is no way E is in L.

Case 2: L is constructed by consThen it has two components: (first L) and (rest L). There are two cases, either (first L) is E itself, or it is not.

o Case 2.1: E equals (first L).This means that E is a member of L,

o Case 2.2: E does not equal (first L).Then E is a member of L iff E is a member of (rest L).

Tracing the execution of list-member, we get the following:USER(70): (list-member 'a '(perhaps today is a good day to die)) 0: (LIST-MEMBER A (PERHAPS TODAY IS A GOOD DAY TO DIE)) 1: (LIST-MEMBER A (TODAY IS A GOOD DAY TO DIE)) 2: (LIST-MEMBER A (IS A GOOD DAY TO DIE)) 3: (LIST-MEMBER A (A GOOD DAY TO DIE)) 3: returned T 2: returned T 1: returned T 0: returned TTIn the implementation of list-member, the function call (eq x y) tests if two symbols are the same. In fact, the semantics of this test determines what we mean by a member:USER(71): (list-member '(a b) '((a a) (a b) (a c))) 0: (LIST-MEMBER (A B) ((A A) (A B) (A C))) 1: (LIST-MEMBER (A B) ((A B) (A C))) 2: (LIST-MEMBER (A B) ((A C))) 3: (LIST-MEMBER (A B) NIL) 3: returned NIL 2: returned NIL 1: returned NIL

Page 10: LISP Expressions

0: returned NILNILIn the example above, we would have expected a result of t. However, since '(a b) does not eq another copy of '(a b) (they are not the same symbol), list-member returns nil. If we want to account for list equivalence, we could have used the LISP built-in function equal instead of eq. Common LISP defines the following set of predicates for testing equality:(= x y) True if x and y evaluate to the same number.(eq x y) True if x and y evaluate to the same symbol.(eql x y) True if x and y are either = or eq.(equal x y) True if x and y are eql or if they evaluate to the same list.(equalp x y) To be discussed in Tutorial 4.

Exercise: What would be the behavior of list-member if we replace eq by =? By eql? By equal?

Example: appendLISP defines a function append that appends one list by another:USER(72): (append '(a b c) '(c d e))(A B C C D E)We implement a recursive version of append. Suppose we are given two lists L1 and L2. L1 is either nil or constructed by cons.

Case 1: L1 is nil.Appending L2 to L1 simply results in L2.

Case 2: L1 is composed of two parts: (first L1) and (rest L1). If we know the result of appending L2 to (rest L1), then we can take this result, insert (first L1) to the front, and we then have the list we want.

Formally, we define the following function:(defun list-append (L1 L2) "Append L1 by L2." (if (null L1) L2 (cons (first L1) (list-append (rest L1) L2))))An execution trace is the following:Eg: (list-append '(a b c) '(c d e)) 0: (LIST-APPEND (A B C) (C D E)) 1: (LIST-APPEND (B C) (C D E)) 2: (LIST-APPEND (C) (C D E)) 3: (LIST-APPEND NIL (C D E)) 3: returned (C D E) 2: returned (C C D E) 1: returned (B C C D E) 0: returned (A B C C D E)(A B C C D E)

Page 11: LISP Expressions

Exercise: LISP defines a function (butlast L) that returns a list containing the same elements in L except for the last one. Implement your own version of butlast using linear recursion. You may assume that (butlast nil) returns nil.

Using Lists as SetsFormally, lists are ordered sequences. They differ with sets in two ways:

1. Sets are unordered, but lists are. (a b c) and (c b a) are two different lists.2. An element either belong to a set or it does not. There is no notion of multiple

occurrences. Yet, a list may contain multiple occurrences of the same element. (a b b c) and (a b c) are two different lists.

However, one may use lists to approximate sets, although the performance of such implementation is not the greatest.We have already seen how we can use the built-in function member to test set membership. LISP also defines functions like (intersection L1 L2), (union L1 L2) and (difference L1L2) for boolean operations on sets. In fact, these functions are not difficult to implement. Consider the following implementation of set intersection:(defun list-intersection (L1 L2) "Return a list containing elements belonging to both L1 and L2." (cond ((null L1) nil) ((member (first L1) L2) (cons (first L1) (list-intersection (rest L1) L2))) (t (list-intersection (rest L1) L2))))The correctness of the implementation is easy to see. L1 is either an empty set (nil) or it is not:

Case 1: L1 is an empty set.Then its interection with L2 is obviously empty.

Case 2: L1 is not empty.L1 has both a first component and a rest component. There are two cases: either (first L1) is a member of L2 or it is not.

o Case 2.1: (first L1) is a member of L2.(first L1) belongs to both L1 and L2, and thus belong to their intersection. Therefore, the intersection of L1 and L2 is simply (first L1) plus the intersection of (rest L1) andL2.

o Case 2.2: (first L1) is not a member of L2.Since (first L1) does not belong to L2, it does not belong to the intersection of L1 and L2. As a result, the intersection of L1 and L2 is exactly the intersection of (rest L1) andL2.

A trace of executing the function is given below:USER(80): (trace list-intersection)(LIST-INTERSECTION)USER(81): (list-intersection '(1 3 5 7) '(1 2 3 4)) 0: (LIST-INTERSECTION (1 3 5 7) (1 2 3 4)) 1: (LIST-INTERSECTION (3 5 7) (1 2 3 4)) 2: (LIST-INTERSECTION (5 7) (1 2 3 4))

Page 12: LISP Expressions

3: (LIST-INTERSECTION (7) (1 2 3 4)) 4: (LIST-INTERSECTION NIL (1 2 3 4)) 4: returned NIL 3: returned NIL 2: returned NIL 1: returned (3) 0: returned (1 3)(1 3)

Exercise: Give a linearly recursive implementation of union and difference.

Lambda expressions and function definition[edit]Another special operator, lambda, is used to bind variables to values which are then evaluated within an expression. This operator is also used to create functions: the arguments to lambda are a list of arguments, and the expression or expressions to which the function evaluates (the returned value is the value of the last expression that is evaluated). The expression (lambda (arg) (+ arg 1))evaluates to a function that, when applied, takes one argument, binds it to arg and returns the number one greater than that argument. Lambda expressions are treated no differently from named functions; they are invoked the same way. Therefore, the expression ((lambda (arg) (+ arg 1)) 5)evaluates to 6.Named functions are created by storing a lambda expression in a symbol using the defun macro. (defun foo (a b c d) (+ a b c d))(defun f (a) b...) defines a new function named f in the global environment. It is a shorthand for the expression: (place-in-function-definition-slot-of-symbol 'f #'(lambda (a) b...))Atoms[edit]In the original LISP there were two fundamental data types: atoms and lists. A list was a finite ordered sequence of elements, where each element is in itself either an atom or a list, and an atom was a number or a symbol. A symbol was essentially a unique named item, written as an alphanumeric string in source code, and used either as a variable name or as a data item in symbolic processing. For example, the list (FOO (BAR 1) 2) contains three elements: the symbol FOO, the list (BAR 1), and the number 2.The essential difference between atoms and lists was that atoms were immutable and unique. Two atoms that appeared in different places in source code but were written in exactly the same way represented the same object,[citation needed] whereas each list was a separate object that could be altered independently of other lists and could be distinguished from other lists by comparison operators.As more data types were introduced in later Lisp dialects, and programming styles evolved, the concept of an atom lost importance.[citation needed] Many dialects still retained the

Page 13: LISP Expressions

predicate atom for legacy compatibility,[citation needed] defining it true for any object which is not a cons.Conses and lists[edit]Main article: Cons

Box-and-pointer diagram for the list (42 69 613)A Lisp list is a singly linked list. Each cell of this list is called a cons (in Scheme, a pair), and is composed of two pointers, called the car and cdr. These are respectively equivalent to the data and next fields discussed in the article linked list.Of the many data structures that can be built out of cons cells, one of the most basic is called a proper list. A proper list is either the special nil (empty list) symbol, or a cons in which the car points to a datum (which may be another cons structure, such as a list), and the cdr points to another proper list.If a given cons is taken to be the head of a linked list, then its car points to the first element of the list, and its cdr points to the rest of the list. For this reason, the car and cdr functions are also called first and rest when referring to conses which are part of a linked list (rather than, say, a tree).Thus, a Lisp list is not an atomic object, as an instance of a container class in C++ or Java would be. A list is nothing more than an aggregate of linked conses. A variable which refers to a given list is simply a pointer to the first cons in the list. Traversal of a list can be done by "cdring down" the list; that is, taking successive cdrs to visit each cons of the list; or by using any of a number of higher-order functions to map a function over a list.Because conses and lists are so universal in Lisp systems, it is a common misconception that they are Lisp's only data structures. In fact, all but the most simplistic Lisps have other data structures – such as vectors (arrays), hash tables, structures, and so forth

LISP ExpressionsWhen you start up the Common LISP environment, you should see a prompt, which means that LISP is waiting for you to enter a LISP expression. In the environment I am using, it looks like the following:USER(1):The Common LISP environment follows the algorithm below when interacting with users:loop read in an expression from the console; evaluate the expression; print the result of evaluation to the console;end loop.

Page 14: LISP Expressions

Common LISP reads in an expression, evaluates it, and then prints out the result. For example, if you want to compute the value of (2 * cos(0) * (4 + 6)), you type in:USER(1): (* 2 (cos 0) (+ 4 6))Common LISP replies:20.0before prompting you to enter the next expression. Several things are worth noting:

LISP expressions are composed of forms. The most common LISP form is function application. LISP represents a function call f(x) as (f x). For example, cos(0) is written as (cos 0).

LISP expressions are case-insensitive. It makes no difference whether we type (cos 0) or (COS 0).

Similarly, "+" is the name of the addition function that returns the sum of its arguments. Some functions, like "+" and "*", could take an arbitrary number of arguments. In our

example, "*" took three arguments. It could as well take 2 arguments, as in "(* 2 3)", or 4 arguments, as in "(* 2 3 4 5)".

In general, a function application form looks like (function argument1 argument2 ... argumentn). As in many programming languages (e.g. C/C++), LISP evaluates function calls in applicative order, which means that all the argument forms are evaluated before the function is invoked. That is to say, the argument forms (cos 0) and (+ 4 6) are respectively evaluated to the values 1 and 10 before they are passed as arguments to the * function. Some other forms, like the conditionals we will see later, are not evaluated in applicative order.

Numeric values like 4 and 6 are called self-evaluating forms: they evaluate to themselves. To evaluate (+ 4 6) in applicative order, the forms 4 and 6 are respectively evaluated to the values 4 and 6 before they are passed as arguments to the + function.

Complex arithmatic expressions can be constructed from built-in functions like the following:Numeric Functions Meaning(+ x1 x2 ... xn) The sum of x1, x2, ..., xn

(* x1 x2 ... xn) The product of x1, x2, ..., xn

(- x y) Subtract y from x(/ x y) Divide x by y(rem x y) The remainder of dividing x by y(abs x) The absolute value of x(max x1 x2 ... xn) The maximum of x1, x2, ..., xn

(min x1 x2 ... xn) The minimum of x1, x2, ..., xn

Common LISP has a rich set of pre-defined numerical functions. For a complete coverage, consult Chapter 12 of the book, Common LISP, The Language (2nd Edition) (CLTL2) by GuySteele. In general, we will not be able to cover all aspects of Common LISP in this tutorial. Adventurous readers should consult CLTL2 frequently for more in-depth explanation of various features of the language.

Page 15: LISP Expressions

Exercise: Look up pages 376-378 of CLTL2 and find out what the functions floor and ceiling are for. Then, find out the subtle difference between mod and rem.

Defining FunctionsEvaluating expressions is not very interesting. We would like to build expression abstractions that could be reused in the future. For example, we could type in the following:USER(2): (defun double (x) (* x 2))DOUBLEIn the above, we define a function named double, which returns two times the value of its input argument x. We can then test-drive the function as below:USER(3): (double 3)6USER(4): (double 7)14

Editing, Loading and Compiling LISP ProgramsMost of the functions we would like to write is going to be a lot longer than the double function. When working with complex programs, it is usually desirable to edit the program with an editor, fully debug the code, and then compile it for faster performance. Use your favorite text editor (mine is emacs) to key in the following function definition:;;; testing.lisp;;; by Philip Fong;;;;;; Introductory comments are preceded by ";;;";;; Function headers are preceded by ";;";;; Inline comments are introduced by ";";;;

;;;; Triple the value of a number;;

(defun triple (X) "Compute three times X." ; Inline comments can (* 3 X)) ; be placed here.

;;;; Negate the sign of a number;;

(defun negate (X) "Negate the value of X." ; This is a documentation string. (- X))

Page 16: LISP Expressions

Save the above in the file testing.lisp. Now load the definition into the LISP environment by typing:USER(5): (load "testing.lisp"); Loading ./testing.lispTLet us try to see if they are working properly.USER(6): (triple 2)6USER(7): (negate 3)-3When functions are fully debugged, we can also compile them into binaries:USER(8): (compile-file "testing.lisp")Depending on whether your code is well-formed, and what system you are using, some compilation messages will be generated. The compiled code can be loaded into the LISP environment later by using the following:USER(9): (load "testing"); Fast loading ./testing.faslTControl Stuctures: Recursions and ConditionalsNow that we are equipped with all the tools for developing LISP programs, let us venture into something more interesting. Consider the definition of factorials:n! = 1 if n = 1n! = n * (n - 1)! if n > 1

We can implement a function to compute factorials using recursion:(defun factorial (N) "Compute the factorial of N." (if (= N 1) 1 (* N (factorial (- N 1)))))The if form checks if N is one, and returns one if that is the case, or else returns N * (N - 1)!. Several points are worth noting:

The condition expression (= N 1) is a relational expression. It returns boolean values T or NIL. In fact, LISP treats NIL as false, and everything else as true. Other relational operators include the following:Relational Operators Meaning(= x y) x is equal to y(/= x y) x is not equal to y(< x y) x is less than y(> x y) x is greater than y(<= x y) x is no greater than y(>= x y) x is no less than y

The if form is not a strict function (strict functions evaluate their arguments in applicative order). Instead, the if form evaluates the condition (= N 1) before further evaluating the

Page 17: LISP Expressions

other two arguments. If the condition evaluates to true, then only the second argument is evaluated, and its value is returned as the value of the if form. Otherwise, the third argument is evaluated, and its value is returned. Forms that are not strict functions are called special forms.

The function is recursive. The definition of factorial involves invocation of itself. Recursion is, for now, our only mechanism for producing looping behavior. Specifically, the kind of recursion we are looking at is called linear recursion, in which the function may make at most one recursive call from any level of invocation.

To better understand the last point, we can make use of the debugging facility trace (do not compile your code if you want to use trace):USER(11): (trace factorial)(FACTORIAL)USER(12): (factorial 4) 0: (FACTORIAL 4) 1: (FACTORIAL 3) 2: (FACTORIAL 2) 3: (FACTORIAL 1) 3: returned 1 2: returned 2 1: returned 6 0: returned 2424Tracing factorial allows us to examine the recursive invocation of the function. As you can see, at most one recursive call is made from each level of invocation.

Exercise: The N'th triangular number is defined to be 1 + 2 + 3 + ... + N. Alternatively, we could give a recursive definition of triangular number as follows:T(n) = 1 if n = 1T(n) = n + T(n-1) if n > 1

Use the recursive definition to help you implement a linearly recursive function (triangular N) that returns the N'th triangular number. Enter your function definition into a text file. Then load it into LISP. Trace the execution of (triangular 6).

Exercise: Write down a recursive definition of BE (assuming that both B and E are non-negative integers). Then implement a linearly recursive function (power B E) that computes BE. Enter your function definition into a text file. Then load it into LISP. Trace the execution of (power 2 6).

Multiple RecursionsRecall the definition of Fibonacci numbers:Fib(n) = 1 for n = 0 or n = 1Fib(n) = Fib(n-1) + Fib(n-2) for n > 1

This definition can be directly translated to the following LISP code:(defun fibonacci (N)

Page 18: LISP Expressions

"Compute the N'th Fibonacci number." (if (or (zerop N) (= N 1)) 1 (+ (fibonacci (- N 1)) (fibonacci (- N 2)))))Again, several observations can be made. First, the function call (zerop N) tests if N is zero. It is merely a shorthand for (= N 0). As such, zerop returns either T or NIL. We call such a boolean function a predicate, as indicated by the suffix p. Some other built-in shorthands and predicates are the following:Shorthand Meaning(1+ x) (+ x 1)(1- x) (- x 1)(zerop x) (= x 0)(plusp x) (> x 0)(minusp x) (< x 0)(evenp x) (= (rem x 2) 0)(oddp x) (/= (rem x 2) 0)Second, the or form is a logical operator. Like if, or is not a strict function. It evaluates its arguments from left to right, returning non-NIL immediately if it encounters an argument that evaluates to non-NIL. It evaluates to NIL if all tests fail. For example, in the expression (or t (= 1 1)), the second argument (= 1 1) will not be evaluated. Similar logical connectives are listed below:Logical Operators Meaning(or x1 x2 ... xn) Logical or(and x1 x2 ... xn) Logical and(not x) Logical negationThird, the function definition contains two self references. It first recursively evaluates (fibonacci (- N 1)) to compute Fib(N-1), then evaluates (fibonacci (- N 2)) to obtain Fib(N-2), and lastly return their sum. This kind of recursive definitiion is called double recursion (more generally, multiple recursion). Tracing the function yields the following:USER(20): (fibonacci 3) 0: (FIBONACCI 3) 1: (FIBONACCI 2) 2: (FIBONACCI 1) 2: returned 1 2: (FIBONACCI 0) 2: returned 1 1: returned 2 1: (FIBONACCI 1) 1: returned 1 0: returned 33Note that in each level, there could be up to two recursive invocations.

Page 19: LISP Expressions

Exercise: The Binomial Coefficient B(n, r) is the coefficient of the term xr in the binormial expansion of (1 + x)n. For example, B(4, 2) = 6 because (1+x)4 = 1 + 4x + 6x2 + 4x3 + x4. The Binomial Coefficient can be computed using the Pascal Triangle formula:B(n, r) = 1 if r = 0 or r = nB(n, r) = B(n-1, r-1) + B(n-1, r) otherwise

Implement a doubly recursive function (binomial N R) that computes the binomial coefficient B(N, R).

Some beginners might find nested function calls like the following very difficult to understand: (+ (fibonacci (- N 1)) (fibonacci (- N 2)))))To make such expressions easier to write and comprehend, one could define local name bindings to represent intermediate results: (let

((F1 (fibonacci (- N 1))) (F2 (fibonacci (- N 2))))

(+ F1 F2))The let special form above defines two local variables, F1 and F2, which binds to Fib(N-1) and Fib(N-2) respectively. Under these local bindings, let evaluates (+ F1 F2). The fibonaccifunction can thus be rewritten as follows:(defun fibonacci (N) "Compute the N'th Fibonacci number." (if (or (zerop N) (= N 1)) 1 (let

((F1 (fibonacci (- N 1))) (F2 (fibonacci (- N 2))))

(+ F1 F2))))Notice that let creates all bindings in parallel. That is, both (fibonacci (- N 1)) and (fibonacci (- N 2)) are evaluated first, and then they are bound to F1 and F2. This means that the following LISP code will not work:(let ((x 1) (y (* x 2))) (+ x y))LISP will attempt to evaluate the right hand sides first before the bindings are established. So, the expression (* x 2) is evaluated before the binding of x is available. To perform sequential binding, use the let* form instead:(let* ((x 1) (y (* x 2))) (+ x y))LISP will bind 1 to x, then evaluate (* x 2) before the value is bound to y.

Page 20: LISP Expressions

Function REMOVE, REMOVE-IF, REMOVE-IF-NOT, DELETE, DELETE-IF, DELETE-IF-NOTSyntax:remove item sequence &key from-end test test-not start end count key => result-sequenceremove-if test sequence &key from-end start end count key => result-sequenceremove-if-not test sequence &key from-end start end count key => result-sequencedelete item sequence &key from-end test test-not start end count key => result-sequencedelete-if test sequence &key from-end start end count key => result-sequencedelete-if-not test sequence &key from-end start end count key => result-sequenceArguments and Values:item---an object.sequence---a proper sequence.test---a designator for a function of one argument that returns a generalized boolean.from-end---a generalized boolean. The default is false.test---a designator for a function of two arguments that returns a generalized boolean.test-not---a designator for a function of two arguments that returns a generalized boolean.start, end---bounding index designators of sequence. The defaults for start and end are 0 and nil, respectively.count---an integer or nil. The default is nil.key---a designator for a function of one argument, or nil.result-sequence---a sequence.

Description:remove, remove-if, and remove-if-not return a sequence from which the elements that satisfy the test have been removed.

delete, delete-if, and delete-if-not are like remove, remove-if, and remove-if-not respectively, but they may modify sequence.

If sequence is a vector, the result is a vector that has the same actual array element type as sequence. If sequence is a list, the result is a list.

Supplying a from-end of true matters only when the count is provided; in that case only the rightmost count elements satisfying the test are deleted.Count, if supplied, limits the number of elements removed or deleted; if more than count elements satisfy the test, then of these elements only the leftmost or rightmost, depending on from-end, are deleted or removed, as many as specified by count. If count is supplied and negative, the behavior is as if zero had been supplied instead. If count is nil, all matching items are affected.

Page 21: LISP Expressions

For all these functions, elements not removed or deleted occur in the same order in the result as they did in sequence.remove, remove-if, remove-if-not return a sequence of the same type as sequence that has the same elements except that those in the subsequence bounded by start and end and satisfying the test have been removed. This is a non-destructive operation. If any elements need to be removed, the result will be a copy. The result of remove may share with sequence; the result may be identical to the input sequence if no elements need to be removed.

delete, delete-if, and delete-if-not return a sequence of the same type as sequence that has the same elements except that those in the subsequence bounded by start and end and satisfying the test have been deleted.Sequence may be destroyed and used to construct the result; however, the result might or might not be identical to sequence.delete, when sequence is a list, is permitted to setf any part, car or cdr, of the top-level list structure in that sequence. When sequence is a vector, delete is permitted to change the dimensions of the vector and to slide its elements into new positions without permuting them to produce the resulting vector.delete-if is constrained to behave exactly as follows: (delete nil sequence :test #'(lambda (ignore item) (funcall test item)) ...)Examples: (remove 4 '(1 3 4 5 9)) => (1 3 5 9) (remove 4 '(1 2 4 1 3 4 5)) => (1 2 1 3 5) (remove 4 '(1 2 4 1 3 4 5) :count 1) => (1 2 1 3 4 5) (remove 4 '(1 2 4 1 3 4 5) :count 1 :from-end t) => (1 2 4 1 3 5) (remove 3 '(1 2 4 1 3 4 5) :test #'>) => (4 3 4 5) (setq lst '(list of four elements)) => (LIST OF FOUR ELEMENTS) (setq lst2 (copy-seq lst)) => (LIST OF FOUR ELEMENTS) (setq lst3 (delete 'four lst)) => (LIST OF ELEMENTS)

(equal lst2 lst3) => false (remove-if #'oddp '(1 2 4 1 3 4 5)) => (2 4 4) (remove-if #'evenp '(1 2 4 1 3 4 5) :count 1 :from-end t) => (1 2 4 1 3 5) (remove-if-not #'evenp '(1 2 3 4 5 6 7 8 9) :count 2 :from-end t)=> (1 2 3 4 5 6 8) (setq tester (list 1 2 4 1 3 4 5)) => (1 2 4 1 3 4 5) (delete 4 tester) => (1 2 1 3 5) (setq tester (list 1 2 4 1 3 4 5)) => (1 2 4 1 3 4 5) (delete 4 tester :count 1) => (1 2 1 3 4 5) (setq tester (list 1 2 4 1 3 4 5)) => (1 2 4 1 3 4 5) (delete 4 tester :count 1 :from-end t) => (1 2 4 1 3 5) (setq tester (list 1 2 4 1 3 4 5)) => (1 2 4 1 3 4 5) (delete 3 tester :test #'>) => (4 3 4 5)

Page 22: LISP Expressions

(setq tester (list 1 2 4 1 3 4 5)) => (1 2 4 1 3 4 5) (delete-if #'oddp tester) => (2 4 4) (setq tester (list 1 2 4 1 3 4 5)) => (1 2 4 1 3 4 5) (delete-if #'evenp tester :count 1 :from-end t) => (1 2 4 1 3 5) (setq tester (list 1 2 3 4 5 6)) => (1 2 3 4 5 6) (delete-if #'evenp tester) => (1 3 5) tester => implementation-dependent (setq foo (list 'a 'b 'c)) => (A B C) (setq bar (cdr foo)) => (B C) (setq foo (delete 'b foo)) => (A C) bar => ((C)) or ... (eq (cdr foo) (car bar)) => T or ...

Side Effects:For delete, delete-if, and delete-if-not, sequence may be destroyed and used to construct the result.Affected By: None.Exceptional Situations:Should be prepared to signal an error of type type-error if sequence is not a proper sequence.Notes:If sequence is a vector, the result might or might not be simple, and might or might not be identical to sequence.The :test-not argument is deprecated.The functions delete-if-not and remove-if-not are deprecated.

Function COUNT, COUNT-IF, COUNT-IF-NOTSyntax:count item sequence &key from-end start end key test test-not => ncount-if predicate sequence &key from-end start end key => ncount-if-not predicate sequence &key from-end start end key => nArguments and Values:item---an object.sequence---a proper sequence.predicate---a designator for a function of one argument that returns a generalized boolean.from-end---a generalized boolean. The default is false.test---a designator for a function of two arguments that returns a generalized boolean.test-not---a designator for a function of two arguments that returns a generalized boolean.

Page 23: LISP Expressions

start, end---bounding index designators of sequence. The defaults for start and end are 0 and nil, respectively.key---a designator for a function of one argument, or nil.n---a non-negative integer less than or equal to the length of sequence.Description:count, count-if, and count-if-not count and return the number of elements in the sequence bounded by start and end that satisfy the test.The from-end has no direct effect on the result. However, if from-end is true, the elements of sequence will be supplied as arguments to the test, test-not, and key in reverse order, which may change the side-effects, if any, of those functions.Examples: (count #\a "how many A's are there in here?") => 2 (count-if-not #'oddp '((1) (2) (3) (4)) :key #'car) => 2 (count-if #'upper-case-p "The Crying of Lot 49" :start 4) => 2 Side Effects: None.Affected By: None.Exceptional Situations:Should be prepared to signal an error of type type-error if sequence is not a proper sequence.See Also:Section 17.2 (Rules about Test Functions), Section 3.6 (Traversal Rules and Side Effects)Notes:The :test-not argument is deprecated.The function count-if-not is deprecated.

So far, I thought it best to create a function called list_member that compares an element to a list and returns T if the element is in the list or nil if it is not in the list.(defun list_member (x L) (cond ((null L) nil) ;if list L is empty, return NIL ((equal x (car L)) T) ;if element x is in L, return T (T (list_member x (cdr L))))) ;else, recursively check remainder of LAnd I want to use it in the function, rem_dup, which I have started filling out below:

(defun rem_dup (L) (cond ((null L) nil) ;if list L is empty, return NIL to user (( list_member (car L) cdr L )) (...) ;part I am having trouble with (T (rem_dup (cdr L))))) ;else, check rest of list recursively

Page 24: LISP Expressions

ASSOC function searches supplied list for cons cell that have item as car part. Return value is the cell with key-value pair which key matched testing conditions, otherwise NIL. Default comparison operator is EQL.Associative list, or for short alist, is a list with key-value pairs in cons cells. That is ((key1 . value1) (key2 . value2) ...)(assoc 'a '((a . 1) (b . 2) (c . 3))) => (A . 1)(assoc 'x '((a . 1) (b . 2) (c . 3))) => NIL(assoc 'b '((a . 1) (b . 2) (c . 3) (b . 4))) => (B . 2) (assoc 7 '((6 . a) (9 . b)) :key #'1+) => (6 . A)(assoc 5 nil) => NIL

The dolist MacroSuppose, for example, you want to reverse a list, so that “first” “second” “third” becomes “third” “second” “first”.

In practice, you would use the reverse function, like this: (setq animals '(gazelle giraffe lion tiger))

(reverse animals)

Here is how you could reverse the list using a while loop: (setq animals '(gazelle giraffe lion tiger))

(defun reverse-list-with-while (list)

"Using while, reverse the order of LIST."

(let (value) ; make sure list starts empty

(while list

(setq value (cons (car list) value))

(setq list (cdr list)))

value))

(reverse-list-with-while animals)

And here is how you could use the dolist macro: (setq animals '(gazelle giraffe lion tiger))

(defun reverse-list-with-dolist (list)

"Using dolist, reverse the order of LIST."

(let (value) ; make sure list starts empty

Page 25: LISP Expressions

(dolist (element list value)

(setq value (cons element value)))))

(reverse-list-with-dolist animals)

In Info, you can place your cursor after the closing parenthesis of each expression and type C-x C-e; in each case, you should see

(tiger lion giraffe gazelle)

in the echo area.

For this example, the existing reverse function is obviously best. The while loop is just like our first example (see A   while   Loop and a List ). The while first checks whether the list has elements; if so, it constructs a new list by adding the first element of the list to the existing list (which in the first iteration of the loop is nil). Since the second element is prepended in front of the first element, and the third element is prepended in front of the second element, the list is reversed.In the expression using a while loop, the (setq list (cdr list)) expression shortens the list, so the while loop eventually stops. In addition, it provides the consexpression with a new first element by creating a new and shorter list at each repetition of the loop.The dolist expression does very much the same as the while expression, except that the dolist macro does some of the work you have to do when writing a whileexpression.Like a while loop, a dolist loops. What is different is that it automatically shortens the list each time it loops—it `cdrs down the list' on its own—and it automatically binds the car of each shorter version of the list to the first of its arguments.In the example, the car of each shorter version of the list is referred to using the symbol ‘element’, the list itself is called ‘list’, and the value returned is called ‘value’. The remainder of the dolist expression is the body.The dolist expression binds the car of each shorter version of the list to element and then evaluates the body of the expression; and repeats the loop. The result is returned in value.

The dotimes MacroThe dotimes macro is similar to dolist, except that it loops a specific number of times.The first argument to dotimes is assigned the numbers 0, 1, 2 and so forth each time around the loop, and the value of the third argument is returned. You need to provide the value of the second argument, which is how many times the macro loops.For example, the following binds the numbers from 0 up to, but not including, the number 3 to the first argument, number, and then constructs a list of the three numbers. (The first number is 0, the second number is 1, and the third number is 2; this makes a total of three numbers in all, starting with zero as the first number.)

(let (value) ; otherwise a value is a void variable

(dotimes (number 3 value)

(setq value (cons number value))))

Page 26: LISP Expressions

⇒ (2 1 0)

dotimes returns value, so the way to use dotimes is to operate on some expression number number of times and then return the result, either as a list or an atom.Here is an example of a defun that uses dotimes to add up the number of pebbles in a triangle.

(defun triangle-using-dotimes (number-of-rows)

"Using dotimes, add up the number of pebbles in a triangle."

(let ((total 0)) ; otherwise a total is a void variable

(dotimes (number number-of-rows total)

(setq total (+ total (1+ number))))))

(triangle-using-dotimes 4)

union and nunion return a list that contains every element that occurs in either list-1 or list-2.For all possible ordered pairs consisting of one element from list-1 and one element from list-2, :test or :test-not is used to determine whether they satisfy the test. The first argument to the :test or :test-notfunction is the part of the element of list-1 extracted by the :key function (if supplied); the second argument is the part of the element of list-2 extracted by the :key function (if supplied).The argument to the :key function is an element of list-1 or list-2; the return value is part of the supplied element. If :key is not supplied or nil, the element of list-1 or list-2 itself is supplied to the :test or :test-notfunction.For every matching pair, one of the two elements of the pair will be in the result. Any element from either list-1 or list-2 that matches no element of the other will appear in the result.If there is a duplication between list-1 and list-2, only one of the duplicate instances will be in the result. If either list-1 or list-2 has duplicate entries within it, the redundant entries might or might not appear in the result.

The order of elements in the result do not have to reflect the ordering of list-1 or list-2 in any way. The result list may be eq to either list-1 or list-2 if appropriate.

Examples:

(union '(a b c) '(f a d))=> (A B C F D)OR=> (B C F A D)OR=> (D F A B C) (union '((x 5) (y 6)) '((z 2) (x 4)) :key #'car)=> ((X 5) (Y 6) (Z 2))OR=> ((X 4) (Y 6) (Z 2))

(setq lst1 (list 1 2 '(1 2) "a" "b") lst2 (list 2 3 '(2 3) "B" "C"))=> (2 3 (2 3) "B" "C")

Page 27: LISP Expressions

(nunion lst1 lst2)=> (1 (1 2) "a" "b" 2 3 (2 3) "B" "C") OR=> (1 2 (1 2) "a" "b" "C" "B" (2 3) 3)

Description:

subsetp returns true if every element of list-1 matches some element of list-2, and false otherwise.Whether a list element is the same as another list element is determined by the functions specified by the keyword arguments. The first argument to the :test or :test-not function is typically part of an element of list-1extracted by the :key function; the second argument is typically part of an element of list-2 extracted by the :key function.The argument to the :key function is an element of either list-1 or list-2; the return value is part of the element of the supplied list element. If :key is not supplied or nil, the list-1 or list-2 element itself is supplied to the:test or :test-not function.

Examples:

(setq cosmos '(1 "a" (1 2))) => (1 "a" (1 2)) (subsetp '(1) cosmos) => true (subsetp '((1 2)) cosmos) => false (subsetp '((1 2)) cosmos :test 'equal) => true (subsetp '(1 "A") cosmos :test #'equalp) => true (subsetp '((1) (2)) '((1) (2))) => false (subsetp '((1) (2)) '((1) (2)) :key #'car) => true