Declarative Languages
Lecture #12

Purpose: Loose ends / CLOS in 10 minutes / revision quiz / summary

12.1 Loose ends

There are a couple of specific points that I want to tie up. The first is that one of the great things about lisp is that it offers so many different ways of expressing your intent. Recall from the first lecture:

A declarative language is a language in which you describe the problem you are working on and the computer decides how to solve it. Lisp and Prolog are declarative languages.

On the other hand a procedural language is a language in which you tell the computer what to do. The machine will then do what you tell it and you hope that what it tells you is the answer to the question you wanted to ask. Pascal is a procedural language.

I am still unhappy with the thought of languages being declarative (if that is what the word means) - although code like
(def-url "/" ...)     ; define root page of my web server
is getting close to that ideal. However, I think it is indisputable that lisp gives you so many ways of telling the computer what to do that you can effectively get on with the task of describing your actual problem relatively unencumbered by small details.

For example, the following would all find the position in the list things of a sublist of length 1. The first is fairly compact, and fine if you know how to use the :key argument to position. The next is almost as neat but depends on knowing about position-if, the third works but might be criticized for wasteful allocation (generating a list of length (length things) and then throwing it away again). The dolist form uses "primitive" parts, which is fine and might even in some circumstances run faster than the others; the mapc-closure solution is quite frankly revolting. Presumably, we could also have written a recursive function to do the search.

(position 1 things :key 'length)

(position-if #'(lambda (x) (= (length x) 1)) things)

(position 1 (mapcar 'length things))

(let ((where 0))
  (dolist (x things)
    (when (= (length x) 1)
      (return where))
    (incf where)))

(block found
  (let ((where 0))
    (mapc #'(lambda (x)
              (when (and x (not (cdr x)))
                (return-from found where))
              (incf where))

A lot of the functions I have told you about take additional (optional) arguments, and I may not always have told you about these. They quite frequently permit greater flexibility, and sometimes additional sophistication, in how you use them. For example, the following
(or (gethash key table)

(gethash key table default)

both allow you to retrieve a value from a hash-table and state a default value in case the key wasn't present. However, suppose that the key is present in the table, and the value stored against it is nil. In the first case above, gethash returns nil and so the or form returns default; in the second, gethash again finds key and returns nil. So the second form, while ostensibly similar to the first, can be used to distinguish between a stored value of nil and no stored value.

You'll find many cases like this in lisp: where different treatments can have similar results but for fine tuning you have to really know how each operator works.

12.2 What did we miss out?   (incorporating "CLOS in ten minutes"TM)

We have covered around 160 Common Lisp symbols - looked at one way that's one sixth of the language. In practice, it's considerably more than that because a lot of the remaining symbols involve more book-work to cover less ground. (Or: less dramatic ground.) I'd reckon we're about half-way there. We didn't touch:

12.3 Summary

So why use Lisp? Because it is

It offers (see also the white paper) It is particularly suitable for applications which are 12.4 Revision Quiz
  1. How would you add (i.e. insert) an extra element to the start of a list?
  2. How could you splice two lists together?
  3. How would you add (i.e. insert) an extra element to the end of a list?
  4. Suppose you wanted to perform some operation on each element of a list: how could you do this? (3 different answers...)
  5. What kinds of things can you use lists for?
  6. Give an example of a special operator.
  7. How could you find out the variable value of a symbol?
  8. How could you find out whether a symbol had a functional value?
  9. Give an example of a lisp object which is of two types, neither of which is a subtype of the other.
  10. True or false: every valid expression, when evaluated, returns a value?
  11. True or false: and is a function?
  12. How could you define a function so that it can take 0 or more parameters?
  13. How do you give a default value for an optional argument?
  14. Give five lisp datatypes.
  15. What does eval do?
  16. What defining forms do you know?
  17. Give some advantages of macros.
  18. Give an example of an iterative operator.
  19. Give 3 different equality predicates.
  20. How would you decide which equality predicate to use?
  21. If you wanted to find an operator which worked on strings, you might look in a reference manual in the chapter on string operators. Which other chapters would you look in?
  22. What is tail recursion?
  23. How do you write comments in lisp code?
  24. What conventions do you know for lisp code?
  25. How would you choose whether to use structures, hash-tables, lists or arrays for a particular application?
  26. What kinds of applications do you think lisp is good for?
  27. How do you ask a lisp programmer if they want a drink?
12.5 Practical session / Suggested activity 12.6 Revision reading As a final word, I just want to thank all my students at APU for helping me deliver this course, for working so hard and (I hope) for coming to appreciate at least the edges of why for me lisp is the only language to write your programs in.
Copyright (C) Nick Levine 1999. All rights reserved.
Last modified 2000-09-14
$Id: // $