Documentation page.
This is the zlisp documentation page.
If you want to visit the zlisp applet, please click on the above
image.
This page, as zlisp itself is still under construction.
Contents of this document:
Preface
Here is my own LISP interpreter.
I made it just as an exercise while I was learning Java, but it grew up
and actually became a acceptable tool for all those who are interested
in LISP or want to learn it, so I decided to make it accessible on the
Internet. There are of course lots of high performance LISP interpreters,
but they are usually big and platform-specific. zlisp can be run
in any system who implements a Java VM, and can be thrown as an applet
inside your Web browser. I think zlisp would be useful for all those
who are interested on computational sciences, the basis of artificial intelligence,
or the mathematical substrate of computers. LISP is really a very simple
programming language, and it nicely shows the formal logic of algorithms,
so it would be a good idea for anyone who wants to learn about computers
to start with it. Specially, humanities students, interested in semiotics
and formal languages, may find it helpful. In fact, for many years, LISP
has been the standard language for all these studies.
But zlisp is not only another LISP interpreter,
its flexibility an extensibility make it a useful tool to do multiple tasks.
Due to the way it interacts with the Java VM, it can perform eventually
any operation that can be made using Java. So if you are already a Java
programmer, you can add zlisp new methods even at runtime.
There are also some differences between zlisp
and the standard LISP implementation Lisp 1.5 that will be discussed
later on this document. Most of them are intended to make LISP an even
more regular and elegant formal language, and many others have been made
to accomplish all zlisp objects to be also Java objects. zlisp
does
not use CONS cells to internally represent its structures as many others
interpreters do, and dot pairs have been completely eliminated (it was
just a confusing and even hard to handle notation).
This document is an introduction to LISP and zlisp.
Although it is not my natural language , I decided to write it down in
English to make it more accessible to the Internet community. Please, send
me any suggestion about gramathics, orthography or style.
As zlisp was made and is maintained only as
an exercise, feel free to download it and use it in your own Web page and
please, e-mail me if you find any bug (I am sure there are many
bugs). I am now correcting the source code and trying it to look prettier;
as soon as I can, I will make it available. Hope you find zlisp
useful,
Alejandro Luque Estepa.
1. Overview
1.1. A brief historical review of LISP.
1.2. What is zlisp and what
is it intended for.
zlisp is a small, plataform-independent LISP
interpreter, mostly designed for educational purposes and as an exercise
of Java programming. However, zlisp is also a strong, reliable interpreter
that you can use to make your own programs, although I suggest you look
for another one if you are to make a large, complex project (probably,
GNU Emacs-LISP will fit your needs)
zlisp is completely designed and compiled
in Java, so it is plataform-independient and can be run in almost any machine.
zlisp
inherits many powerful capabilities from Java: safety (is eventually impossible
for zlisp to cause any damage to your machine), smart and intensive
memory handling, run time link (it allows you to extend
zlisp while
you are running it), etc. As you may know, Java is a completely object-oriented
language, but that's something zlisp
haven't inherited. However,
Lisp objects are also Java objects and every object in Lisp has its own
Java class; this allows you to treat zlisp as something like an
object-oriented language while you are extending it with your own Java
code. The extension capability of zlisp was one of the most
important items at its design and is one the most relevant differences
between it and other interpreters. In fact, you can say that
zlisp
is not only a LISP interpreter, but a complete tool to design new Java
methods and invoke them within a LISP program. However, you don't need
to do this and you can look at zlisp as a complete LISP interpreter.
Indeed, as it was proved that any computable algorithm can be implemented
in LISP, zlisp can be noted as a proof that this also applies to
Java (of course we must prove that zlisp is indeed a LISP
interpreter first).
Perhaps, one of the most interesting (from my
point of view) features of zlisp is that to use it, you have only
to point your web browser to its home page (and you can do this just by
clicking on the image on the top of this document). There you will see
the very simple -and quite obvious- instructions to interact with the interpreter.
As an example, you can write (plus 2 2) and tell the
interpreter to evaluate that expression. Of course, if all's going
well, you will note that the answer is 4. But probably,
if you have done that, you have noticed the long time your computer takes
to perform such a simple calculation: don't worry, it was because the program
had to load all the Java classes before you use them the first time, but
when the classes are in memory, very few time it will take to calculate
a sum (but remember: Java is a semi-interpreted language and zlisp
is a interpreter of LISP that runs upon the Java VM so it won't
be expected to run too fast)
2. Data types in zlisp. List and
atoms.
2.1. Standard LISP data types.
Now that we have introduced
LISP and zlisp, we can take a look a little deeper and see how do
they run. First of all, we will review the basis of LISP for all those
who are not familiarized with them. In the last chapter, when we wanted
zlisp
to perform a simple operation, such as 2+2, we wrote (plus
2 2). Notice that: (1) the operator goes before the parameters
it takes (this is called Polish notation) and (2) both the operators
and its parameters appear between parenthesis and form what we call a list.
Really, what any LISP interpreter does is to evaluate lists, indeed,
"LISP" stands for "LISt Processing". To understand what a list is and what
is used for, first we shall study the components of lists, i.e. atoms,
by introducing some types zlisp supports.
2.1.1. Numbers.
Everybody is supposed to know
what a number is (or at least what it is used for) so we won't take
a long time talking about numbers. We will only notice that there are two
number types zlisp allows you to use: integer and float
numbers: integers are 32-bits signed and floats are 64-bits IEEE754 floating-point.
The interpreter treats a number as a float when you introduce it using
a separating point ".", or comma ",", depending on your language settings.
When the interpreter performs any operation between numbers, it returns
a number with the same type that the one with the greatest precision you
gave him, i.e. if you evaluate the expression (quotient 5
2) that computes 5 / 2, you will get the integer quotient 2,
not the exact quotient 2.5 you may have expected.
To get a more accurate result, you should use at least one floating-point
number at the operator parameters, v.g. (quotient 5.0 2).
Note that numbers are always evaluated to themselves, so if you write 3.14159265
and evaluate this expression, it will return just the same number you introduced.
2.1.2. Strings.
A string is a set of characters delimited by two
double quotes (") at the end and at the beginning. Examples of strings
are "to be or not to be" , "words, words,
words" , "3.141592" , "(plus
3 4)". As numbers, strings are evaluated to themselves, so it will
be a foolish thing to evaluate "(plus 3 4)" because
you will get "(plus 3 4)" again.
2.1.3. Symbols.
Symbols are probably one of
the most important data type in LISP, in fact LISP was designed to put
stress not on numerical, but symbolic calculus. A symbol is just
a set of alphanumeric characters, starting with an alphabetical one, not
containing any spaces and not delimited by any special character. Thus,
examples of symbols are pi, me,
you
number_25 or even el_perro_de_San_Roque_no_tiene_rabo_porque_Ramon_Rodriguez_se_lo_ha_cortado
(as can be noted, there's no limitation for the length of symbols). We
will see later that a symbol can be linked to any LISP object of
any data type, but you can use them anyway on your expressions: the problem
appears only if you tell the interpreter to evaluate an unlinked
symbol;
if you try to do this (e.g. by writing rose_of_Castille
without
having given any value to that symbol) the interpreter will return an error
i.e. an unlinked symbol is evaluated to an error. A linked symbol
is evaluated, of course, to the object it is linked to. A list of all linked
symbols zlisp have at any moment, can be obtained by evaluating
the expression (lbound). If you do that, you'll see
that there are a lot of pre-linked symbols in your machine: that's because,
as we will see later, in zlisp, all operators (as
plus,
quotient, etc.) are symbols linked to objects of the type method
(vid. inf., 2.2.2).
Now, you may ask, how can I link a symbol?. Well,
most LISP interpreters use a operator called setq
to do this. setq takes two parameters: first, the
symbol you want to link and then the object you want that symbol to be
linked to. For example, to link the symbol this_year
to the value 1999 you have only to write (setq
this_year 1999) and tell the interpreter to evaluate. Then evaluate
the symbol this_year and the interpreter will gently
remember you which year you live in. We must take care when using setq
because the interpreter evaluates the second parameter before it is assigned
to the symbol. In the last example, we had no problems because, as we have
noted, numbers are evaluated to themselves, neither we will have problems
with strings. But what if we want to link a symbol to another symbol? How
can we avoid the interpreter to evaluate something? Well, really you can't
avoid it because the interpreter always evaluate this second parameter,
but you can build an expression that will be evaluated to whatever you
want: the way to do this is the operator quote. This
operator takes one parameters and just returns it without evaluating, so
if you write (quote what_I_want) or, which is more
common, by using the simple quote ('), 'what_I_want
you will get just what_I_want, Therefore, if you want
to link the symbol my_symbol to the value other_symbol,
just write (setq my_symbol 'other_symbol) and when
the interpreter evaluates my_symbol , it returns other_symbol,
as we wanted.
There's an interesting thing about that: you can
link a symbol to itself. Just write (setq the_snake_that_bites_its_tail
'the_snake_that_bites_its_tail) and whenever you evaluate such a
descriptive symbol, you get it again and again. In LISP there are two important
symbols that get linked at the moment you start using the interpreter:
t
and nil. Though they are used on logical expressions
as equivalents of true and false, we will note here that
zlisp treats them as the other symbols, and that you can (although
it is not recommended) assign them the value you want. Initially, nil
is linked to the empty list () and t
is linked to itself (like the former snake). The reason you shouldn't change
their linkages is that many zlisp methods are implemented to return
these particular symbols and it will get a little confusing if you manipulate
them.
2.1.4. Lists.
These former three data types are referred as atoms,
because they are all individual things and not sets. In LISP, sets of ordered
objects are called lists, and they are, as we noted above, the basis
of the language. A list may be composed of atoms of any type or other lists,
that we may call sublists, all of them mixed and combined in anyway.
The format widely used to represent lists is to put all their elements
between brackets and separated by spaces. Thus, examples of lists are (spain
portugal france) , (1 2 3 4 5 6 7) , (1 (2 3) (4 5) (6 7)) , (me) , ((3
apples) (2 strawberries)) , (((life) (is not)) (but (a tale))) and
any other as complex as you want. Remember that elements in lists are ordered,
so (1 2 3) and (3 2 1) are different
lists (some people call this kind of objects vectors, but LISP-speaking,
they are always called lists).
Now that we understand exactly
what a list is, we can also understand what have we been doing when we
wrote things such (plus 3 4): we were evaluating lists.
We will get a little deeper on this subject in the next chapter, but note
that when you want a list to be evaluated, its first element must be something
to tell the interpreter what to do (an operator or a defined
function), and the rest of the elements must be the parameters of the
procedure (operation) you want to perform.
Of course, as you may have realized, a symbol can
be also linked to a list, and if you don't want a list to be evaluated,
you can also use the quote: (setq andalucia '(almeria cadiz
cordoba granada huelva jaen malaga sevilla)). But if you want a
list to be evaluated before its value its assigned to a symbol, you can
also write (setq five (plus 2 3)), and then, whenever
you write five it will be evaluated to the numeric
value 5 so you can also write (setq
six (plus five 1)) and henceforth.
2.2. Other data allowed by zlisp.
These last four data types are common to most LISP
interpreters and you will be using them most of the time you write your
LISP code, but zlisp extends those types with two others: errors
and
methods, which are very important to understand how does
zlisp
work and what the differences are with other interpreters.
2.2.1. Errors.
To allow programs running in zlisp to handle
their own errors, a chance was taken: the interpreter does not break the
program execution when an error occurs, instead of that, it just evaluates
the problematic expression to be an object of error type and it's
the programmer task to face and solve the problem. An error is just another
data type (an atom) that contains the information needed to know which
error has occurred. Although this will probably will be improved in later
versions of zlisp, by now, this information is codified into a single
number that refers to the error type. There are few types of errors:
Error #0: Unknown error type.
This type is a trash where all errors not defined yet go.
Error #1: Not enought arguments.
An operator or function has been called with too few arguments.
Error #2: Incompatible type.
An operator or function has been called with an argument of a improper
type.
Error #3: Undefined symbol
.
An unlinked symbol has been evaluated.
Error #4: Function not defined
as list .
A function incorrectly defined has been called (vid.
3.2)
Error #5: Incorrect format of function.
""
""
Error #6: Can only run lists and
methods.
Something different of a list or method has been tried to run.
Error #7: goto and return must
be called inside a prog method. (vid 4.5)
Error #8: Class not found.
(vid 2.2.2)
Error #9: Method not found in class.
(vid 2.2.2)
Error #10: File not found.
An attempt has been done to read a file that does not exist.
You can always obtain
an error object by using the error operator which
takes an error number as its only parameter: (error 0)
will return an error of the type 0 listed above (undefined error).
2.2.2. Methods. Direct links to
Java.
As we noted early zlisp is completely
Java-based so you can add new operators by writing java methods. The object
type created to allow the user to do this extensions is method.
Briefly, a method object is a LISP object (an atom indeed) that points
to a java.lang.reflect.Method object (if you are a Java programmer, you
probably know what are we talking about, if you are not, you will never
have to use that kind of object). This object tells the Java VM how to
manage with the parameters passed. To invoke a method object, you
have only to write it at the beginning of a list, as we have been doing
all the time. In fact, you can realize that things such plus,
quote, difference etc. actually are symbols linked to objects of
the type method, so if you tell the interpreter to evaluate an expression
like plus you will see it is evaluated to something
that sounds like public static zlisp.LispObject zlisp.Methods.plusMethod(zlisp.LispObject[])
that
is the format used to print methods objects. Java methods are always contained
in a class (again, you must have some Java knowledge to understand
this). In the example above, there were a method called plusMethod
contained in the class zlisp.Methods, which is the class where all
the standard methods are contained.
You can at any time create a new zlisp-method
object by specifying the names of a class and a method within it and using
the method operator, For example (method
"zlisp.Methods" "plusMethod") will be evaluated to an object that
points to the zlisp standard method to add numbers and if you evaluate
(setq sum (method "zlisp.Methods" "plusMethod"))
you can henceforth use an operator named sum instead
of plus although the later is still valid. Note that
the convention used to name the Java methods contained in the the class
zlisp.Methods
is to add the word "Method" to the name of the LISP operator. Note also
that you can do operations like this:
and thus change the meaning a some LISP operators. You can experiment with
this, but probably it will get a little messy in a few minutes and you'll
need to restart the interpreter to restore the primitive meanings. Soon,
a document specifying how to write new Java methods for zlisp will
be available, by now, just think of them as objects that take some parameters,
do something with them and then return another object.
3. How does zlisp work.
3.1 Defining new functions. The lambda
operator. Local variables.
Now that we know all the data types used when programming
in zlisp, let's talk a little about what can we do with them. First
of all, you may take a look at chapter 4, where all predefined
methods are listed and then you would be able to start writing a little
more complex programs, but in this chapter we will introduce some important
concepts to understand the internals of the interpreter and so allow you
to take advantage of the most interesting features of LISP and this interpreter.
The first thing we will introduce is the defun
operator, that will allow you to define new functions. We have already
seen arithmetic operators such as plus, difference, quotient,
times and so on, but suppose you are implementing a program on which
you have to evaluate many times a function like f(x)=5x+3. Of course,
you don't want to write the complete expression (involving plus
and
times operators) every time you need to use the function.
Almost all programming languages have some way to avoid you doing this
by defining a function that is the stored on memory and used whenever
yo need. The exact name of the LISP operator used to define functions varies
a little on different dialects, but the most common one is defun.
We will see how this operator works by defining the function f(x):
(defun f (x) (plus 3 (times 5 x)))
defun takes three parameters: the first one is the
name of the function you want to define, the second is a list of
the parameters your function will use, and the third is the body
of the function, i.e. the expression that will be evaluated whenever you
call
the function. Now that you have defined the function f(x), you can
use it as if it were another LISP operator, making no difference, so evaluate
(f 3) and you will get 18. Note
that:
1. You can define functions that take two or more parameters. For example,
to define a new function to add two numbers, you can write
(defun plus_clone (x y) (plus x y))
2. By no way user defined functions are limited to arithmetic operations.
You can also perform more complex operations inside them and then return
any kind of object.
3. When you call a function, the variables of the function definition
are linked to the parameters you specify when calling the function at
the same order. If you call a function with more arguments than it
is defined to take, there will be no problem, but if you try to call a
function with less arguments, you will obtain an Error #1 object.
4. No one of the three arguments you pass to the defun
operator is evaluated until you call the function, so you need not to quote
them.
5. The function parameters are linked only while the function body
is being evaluated (vid. infra).
But what sort of thing is a defined function? Well,
we have seen all data types of zlisp, so a function must be one
of the types listed on the last chapter. By the way we write it, without
double-quotes, nor brackets, you may realize that f
is, indeed, a symbol. But it have to be linked to something to tell
the interpreter how to handle when it finds it in an expression like (f
3). Ok, let's see if we can evaluate the symbol f
alone: write it and evaluate. You probably got something like this:
((x) (plus 3 (times 5 x)))
Just a list!! That's right: a function is nothing but a list, the
first of whose elements is the list of function parameters, and the second
the function body. Now this question arises: Had we obtained the same results
if had we written (setq f '((x) (plus 3 (times 5 x))),
instead of using the defun operator? The answer is
yes;
it would had been exactly the same, so the defun
operator
is not unavoidable if you want to define functions. Furthermore, you can
use lists directly as if they were functions, without defining them before:
('((x) (plus 3 (times 5 x))) 3)
will have the same effect as (f 3). That sort of things,
however, are not true on most LISP interpreters and the way to do
things like the above one is to use the lambda operator.
Using it, the latter expression will sound like
((lambda (x) (plus 3 (times 5 x))) 3)
As you may realize, in zlisp, the lambda operator
is very simple: it just return a list composed by its parameters, not evaluating
them. It's recommendable to use the lambda operator
instead of quoted lists, because is the widely used convention on most
LISP interpreters. The usefulness of using functions without assigning
a name will be noted latter, when we discuss the applicative operators.
Finally, we will talk a little about LISP variables.
On chapter 3, we saw the setq operator
that allowed us to link symbols to other objects. We will call symbols
linked on this way global variables because they have an unlimited
field of visibility, that is, their linkages can be seen anywhere at anytime
. But on this chapter we have seen how function parameters are linked before
the function body is evaluated and then unlinked. These are called local
variables because they can only be seen on a limited space of the program
(the function body). On chapter 4 we will see that there are different
places where local variables can be defined. This problem arises: What
happens if there are global and local variables with the same name? Does
a collision occur?. Let's see if there is any problem by writing this code:
(setq x 5)
(defun add_ten (x) (plus x 10))
(add_ten 10)
The latter expression will be evaluated to 20. But
if you ask the interpreter about the value of the x
symbol, it will return 5, not 10.
That is because when two variables have the same name, the local one prevails
over the global, so when the body of the function add_ten
is evaluated, the global variable is hidden and x
is assigned to 10, but as soon as the local variable
is released, x gets again its original value 5.
If two local variables with the same name are defined
at the same time, the lower level variable prevails, so if you evaluate
the expressions
(defun double (x) (times x 2))
(defun minus_three (x) (difference x 3))
(minus_three (double 10))
you will get 17, as you would have expected. Thus,
once you have defined a function, it doesn't matter what names did you
give to the variables, the behavior will be always the same, and you don't
have to care. Function variables are so called mute variables.
3.2 Evaluating and running objects.
On this section, we will go further about some concepts
introduced on the latter one and actually know the behavior of the interpreter.
We have been using the word evaluate since the very beginning of
this document and probably you already know what does it mean for the interpreter:
it takes an expression, it looks trough it and then returns something in
response. On chapter one, we saw how atoms are evaluated: numbers, strings,
errors an methods are evaluated to themselves, symbols are evaluated to
the object they are linked to, if there is one, or to an error if there
is not, but we don't know what does the interpreter do when it evaluates
a list.
To exemplify it we will use the following few lists
that can be evaluated:
a) (plus 2 2)
b) (plus_clone 2 2)
c) ((lambda (x y) (plus x y)) 2 2)
where plus_clone is a function previously defined
as (defun plus_clone (x y) (plus x y)). The procedure
to evaluate a list is:
1. The interpreter takes its first element and then evaluates it.
This means that if the the first element is an atom it will be evaluated
using the rules listed above, but if the first element is also a list,
all this procedure is performed recursively. In our examples,
a) The first element of (plus 2 2) is the symbol
plus,
which is evaluated to a method that points to the Java method zlisp.Methods.plusMethod.
b) plus_clone is a symbol and, as it was noted on the
past section is linked to the list ((x y) (plus x y)).
c) The first element of ((lambda (x y) (plus x y))
2 2) is the list (lambda (x y) (plus x y)), and to evaluate
it the interpreter goes through steps 1 and 2. Finally, as we pointed out
on section 3.1, it is evaluated to the list ((x y) (plus x y)) (the
same as in example b)
2. Now, depending of what kind of object the first element was evaluated
to, three cases arise:
2.1 If we obtained a number, string, symbol or error, our list is evaluated
to Error #6.
2.2 If it was a method object, the Java method that it points
to is invoked, taking the other elements of the list we are evaluating
as parameters. Usually they are also evaluated before passed, but there
are exceptions in some predefined zlisp methods, as setq,
defun, lambda and quote. Finally, the list
is evaluated to the return value of the Java method.
a) This is the case of example a). When zlisp.Methods.plusMethod
is invoked, it adds all the parameters it takes (if possible) and then
returns the result. Thus, (plus 2 2) is evaluated to 4.
2.3 If it was a list what step 1 got, then the list is run (care
must be taken here: this list is not evaluated). To run a list,
the interpreter first checks if it has the correct format: that is, is
formed by a list of parameters first and then a function body. It it isn't,
Error #5 is returned. If the format is ok, then the interpreter creates
local variables with the names specified by the parameters and links them
to the other elements of the list it is evaluating (our original
list), which are also evaluated before. Finally, it evaluates the body
of the function (the second element of the list step 1 returned) and returns
the result. Local variables are released.
b) and c) When the list ((x y) (plus x y)) is
run with the parameters 2 2, the interpreter creates two local variables
called x and y and it assigns both the value 2. Then
(plus
x y) is evaluated (again, the interpreter goes through steps 1 and
2) and it finally returns 4.
Now we have understood all things we have been doing all the time. We understand
how zlisp works and have arrived to the top of the mountain. Next
chapters will be the way down, to make use of all things we have learned.
First we should take a look over all the standard methods of zlisp.
4. zlisp standard methods.
This chapter contains a list of all symbol linked to
methods zlisp starts with. On naming them, I tried to use the most
common notations and sometimes I have used several names for a single method
so you can use the one you like most; in particular, there are some methods
that have a symbolic abreviation, as plus, times, etc.
In these cases, I list the abreviation in parenthesis after the full name.
I know there are some useful methods zlisp lacks and I will try
to implement them as soon as possible. In the meanwhile, you can try to
define new zlisp functions to fill the gap. Remember that you can
use setq to link methods to new symbols; it will be
useful if you do not like the names I use.
4.1 General purpose methods.
and (&):
Evaluates its two parameters. If no one of them is evaluated to nil,
returns t, in other case, returns nil.
Example: (and (equal 'uno 'one) (equal 'dos 'two)) ->
nil
atom:
Returns t if the argument passed is an atom, nil
else.
Example: (atom 'atom) -> t
boundp:
Checks if the symbol passed as argument is linked. If it is, returns
t,
else nil.
Example: (boundp 'plus) -> t
concat:
Joins a number of symbols, returning the new one formed.
Example: (concat 'sub 'book 'keeper) -> subbookkeeper
cond:
cond takes a number of lists as its parameters.
Cond goes through them and evaluates their first elements. When one is
found that is not evaluated to nil, cond
returns the result of evaluating the second element of the list that element
was contained into, and the process stops.
Example: (cond ((equal 1 2) 'a) ((equal 1 1) 'b) (t 'c))
-> b
defun:
This operator was introduced on section 3.1 and
no further explanation is needed.
equal (=):
Takes two parameters, evaluates each one and returns t
if the results are the same, else nil.
Examples: (equal 1 2) -> nil
(equal 10 (times 5 2)) -> t
eval:
Evaluates its first parameter twice and returns the result.
Example: (eval (quote (plus 3 4))) -> 7
if:
if takes three parameters; the first one
is the conditional and when it is evaluated, it may return either
nil
or a non-nil object. If this second case applies, the second parameter
is evaluated and returned as the final result, else the third parameter
is evaluated and returned. Note that if is a special
form (i.e. it does not evaluate all its parameters as they are passed;
instead it evaluates only the first one and either the second or the third,
but never both).
Examples: (if (equal (plus 2 2) 4) 'yes 'no) -> no
(if (greaterp 3 2) (msg "3 is greater than 2") (msg "3 is not greater than
2")) -> Displays "3 is greater than 2".
import:
Used to load an extension package (vid.
chap. 5). import takes the name of a package class,
introduced as a string,
and loads it into memory.
Example: (import "zlisp.LispGraph")
lambda:
Described with detail on section 3.1.
lbound:
Shows all the linked symbols zlisp has in memory.
Example: (lbound)
let:
let allows you to use local variables inside a
set of instructions. The first argument you should pass to let is a list
of assignation clauses, each one of them consisting in a list with the
name of a variable and the value you want it to be linked at first (later,
you will be able to manipulate it as any other local variable). The rest
of the arguments you should pass to let are simply
instructions that are evaluated by order and that usually (but not necessarily)
will contain references to the local variables you defined. As return value,
let
retrieves the result of the evaluation of the last instruction you passed.
Example: (let ((i 0) (j 10)) (setq i (plus i j)) (add1
i)) -> 11
load:
(load filename) loads a file specifyed
by the string filename This files should contain
a set of LISP instructions that zlisp will evaluate. If succes,
returns t. If some error occured during the operation,
Error
#10 is returned.
method:
Creates a method object (vid. 2.2.2) pointing
to the java.lang.reflect.Method object specified by the name of a class
and the name of a method contained in this class.
Example: (method "zlisp.Methods" "plusMethod") -> public
static zlisp.LispObject zlisp.Methods.plusMethod(zlisp.LispObject[])
msg:
Shows a message on your zlisp user interface. msg
takes
an indefinite number of arguments, evaluates them and prints then one by
one on the standard output device defined for zlisp. When t
is to be printed, msg prints a carriage return.
Always returns t.
Example: (msg "This is " 'zlisp " version" 0 t)
-> Prints This is zlisp version 0
not (!):
null:
nullp:
Returns t if nil is passed,
else nil.
Example: (not (equal 2 2)) -> nil
or (|):
Returns t if at least on of the two arguments
passed is different from nil, else nil.
Example: (or (equal 2 3) (lessp 2 3)) -> t
quote:
quote is a special form that returns the
argument passed as-is (i.e. not evaluating it).
Example: (quote zlisp) -> zlisp
set:
set takes two arguments and evaluates both of
them (cf. setq). The result of evaluating the first
one should be a symbol and then, that symbol is linked to the result of
evaluating the second argument.
Example: (set (quote zlisp) "Java LISP interpreter")
setq:
The same as set, but setq does
not evaluate its first parameter (is a special form).
Example: (setq zlisp "Java LISP interpreter")
stringp:
Returns t if the argument passed is a string,
else nil.
Example: (stringp "Inclitas razas uberrimas, sangre de
Hispania fecunda...") -> t
symbolp:
Returns t if the argument passed is a symbol,
else nil.
Example: (symbolp 'zlisp) -> t
tostring:
Changes anything into a string.
Example: (tostring 'zlisp) -> "zlisp"
4.2 Arithmetic operators.
add1:
Adds an unit to the numeric parameter passed.
Example: (add1 0) -> 1.
difference (-):
Subtracts a pair of numbers.
Example: (difference 1998 1978) -> 20
greaterp (>):
Takes two numbers as parameters and returns t
if the first one is greater (not equal) than the second, else nil
Example: (greaterp 5 4) -> t
lessp (<):
Takes two numbers as parameters and returns t if
the first one is lesser (not equal) than the second, else nil.
Example: (lessp 5 4) -> nil
numberp:
Returns t if the argument passed is a number,
else nil.
Example: (numberp 3) -> t
plus (+):
Takes an indefinite number of numbers and returns the sum of all of
them.
Example: (plus 1 2 3) -> 6
quotient (-):
Returns the quotient of a pair of numbers.
Example: (quotient 20 10) -> 2
sub1:
Substract an unit from the numeric argument passed.
Example: (sub1 10) -> 9
times (*):
Takes an indefinite number of numbers and returns the product of all
of them.
Example: (times 2 3 4) -> 24
zerop:
Returns t if the argument passed is numerically
equal than zero, else nil.
Example: (zerop (difference 3 3)) -> t
4.3 List manipulating methods.
append:
Takes two lists as parameters and merges them into a new one.
Example: (append '(When they were up) '(they were up))->
(When they were up they were up)
assoc:
Looks for a table entry. The tables assoc manages
are formed by a list containing a number of sublists, each one composed
by a key (its first element) and anything else the key is supposed to be
related to. The first parameter assoc takes is a key.
Then it looks through the table, passed as the second parameter, and, if
a key matches the one passed, assoc returns the whole
table entry containing that key. Else (i.e. if no key matches), returns
nil.
Example: (assoc 'dos '((uno one) (dos two) (tres three)
(cuatro four))) -> (dos two)
car:
Returns the first element of the list passed as its only argument.
Example: (car '(Hipogrifo violento que corriste parejas
con el viento)) -> Hipogrifo
cdr:
Returns the list passed as its only argument, without its first element
(its car).
Example: (cdr '(Dichoso el humilde estado del sabio que
se retira)) -> (el humilde estado del sabio que se retira)
cons:
cons takes two parameters: the first one is an
atom an the second one must be a list (nil is allowed)
and adds the atom at the beginning of the list.
Examples: (cons 'zero '(one two three)) -> (zero one
two three)
(cons 'one nil) -> (one)
intersection:
Takes two lists as parameters and returns a list formed by the elements
that are included in both (the order is that of the first list).
Example: (intersection '(John Mary Amy Sue) '(Amy Peter
Richard Mary )) -> (Mary Amy)
last:
Returns a list containing the las element of the list passed.
Example: (last '(dejadme llorar orillas del mar)) ->
(mar)
length:
Returns the number of elements contained in a list.
Example: (length '(Almeria Cadiz Cordoba Granada Huelva
Jaen Malaga Sevilla)) -> 8
list:
Forms a list containing the arguments passed.
Example: (list 'you 'and 'me) -> (you and me)
listp:
Returns t if the argument passed is a list, else
nil.
Example: (listp '(this is a list)) -> t
member:
(member x list) returns the sublist of list
formed from the first appearance of x, nil
if
x is not contained in list.
Example: (member 'two '(one two three four)) -> (two
three four)
nth:
(nth n list) returns element number n(the
first one numbering 0) contained in list.
Example: (nth 2 '(zero one two three four five)) -> two
nthcdr:
(nthcdr n list) returns the sublist of list
starting from element number n.
Example: (nthcdr 2 '(zero one two three four five)) ->
(two three four five)
reverse:
Returns in the inverse order the list passed as argument.
Example: (reverse '(you and me)) -> (me and you)
setdifference:
(setdifference list1 list2) returns a list containing
those elements of list1 not contained in list2.
Example: (setdifference '(a b c d e) '(d b)) -> (a c
e)
sublis:
sublis is designed to do substitutions inside
a list. To tell it what substitutions to do, you specify as the first argument
a table of substitutions, consisting on a list formed by others
list, each one of them containing the thing you want to change and the
object you want it to change into. The list on which you want to make substitutions
is specified as the second argument.
Example: (sublis '((one uno) (three tres)) '(one two
three)) -> (uno two tres)
subst:
(subst x y list) replaces all appearances of x
inside list with y.
Example: (subst one two '(I want one)) -> (I want two)
union:
(union list1 list2) returns a list containing
only once all elements contained either in list1 or
in list2.
Example: (union '(John Mary Kate) '(Rose Mary Peter))
-> (John Mary Kate Peter)
4.4 Applicative operators.
applytoall:
Used to perform the same operation over all the elements of a list.
The first parameter (a function name or a lambda expression) specifies
the operation to perform; the second one is a list containing the elements
the operation will take as it parameter. The returned object is a list
containing the results of performing the operation over each one of the
elements of the original list.
Examples: (applytoall '(lambda (x) (times x 2)) '(1 2
3 4 5)) -> (2 4 6 8 10)
(applytoall 'add1 '(1 2 3 4 5)) -> (2 3 4 5 6)
findif:
Similar to applytoall, it applies an operator
(defined as the first parameter) one-by-one to the elements of a list passed
as the second parameter, and returns the first element of this list that
makes the operator to be evaluated to something different than nil.
Example: (findif '(lambda (x) (greaterp x 10)) '(1 5
12
3 15)) -> 12
subset:
As any applicative operator, subset applies an
operator (the first argument) to every element of a list (the second argument).
subset
returns a list containing all the elements of the list passed that make
the operator to be evaluated to something different than nil.
Example (subset '(lambda (s) (greaterp x 10)) '(1 5 12
3 15)) -> (12 15)
4.5 Iterators.
do:
do is an iterator; this means that it is
used to perform a single or multiple operation many times. The first element
do
takes is a list composed by another lists, each one concerning a variable
(that is treated as a local variable, linked only while evaluating
the do-expression. For each variable you must specify by this order: its
name, an initial value and an updating expression. The second parameter
of the do-expression is a list of two elements: the first one is a conditional
that tells the interpreter when the iteration must be stopped (the interpreter
will do so when the conditional is evaluated to something different than
nil); the second is an expression that, once evaluated,
will be the resulting value of all the do-expresion. Finally, do
expects a number of expressions (not contained in a single list) that will
be evaluated on every loop of the iterator these are called the body
of the loop. The way do works is:
1. Links the specified variables to their initial values.
2. Test whether the finalization clause is nil
or not. If it is, finishes and returns the finalization value, if it is
not:
3. Evaluates by order all the lists in the body. If an atom
(usually a symbol) is found in the body, it is treated as a label
and it can be referred into a goto expression to tell
the interpreter to jump directly to the list immediately after the label
and continue the loop there.
4. Updates the variables using their respective updating clauses.
5. Repeats steps 2-4 until the finalization clause is evaluated to
nil.
Examples: (do ((i 1 (add1 i))) ((equal i 100) nil) (msg
"2*" i "=" (times i 2) t)) -> Writes the double of all numbers 1 trough
100.
(do ((i 1 (add1 i)) (sum 0 (plus sum i))) ((equal i 100) sum) nil) -> Computes
1+2+3+...+99+100.
(do ((i 10 (sub1 i)) (l nil (cons i l))) ((equali 0) l) nil) -> (0 1 2
3 4 5 6 7 8 9 10)
go:
goto:
Used inside an iterator, go (or goto,
that stands for the same) tells the interpreter to jump to the expression
pointed by the label introduced as the only parameter (see above).
Example: (goto label01)
prog:
prog is in some way a more primitive iterator
than do. The first argument prog
receives is a list containing names for some local variables that will
be initialized to nil. Then, prog is
passed a number of instructions (a body), possibly including labels,
go
and return forms, that will be evaluated. When
the last instruction is evaluated (or when a return
is found), prog finishes.
Example: (prog (i) (setq i 0) label (setq i (add1 i))
(msg i t) (if (lessp i 10) (go label) (msg "The end" t))) -> Writes all
numbers from 1 to 10.
return:
Called inside an iterator, it causes it to finish an return the object
specified.
Example: (return nil)
5. Packages.
5.1. What is a zlisp package.
Those described on the above chapter are the native
operators of zlisp, all of them are loaded and ready to be used
at the very time you tell the interpreter to evaluate your first expression
and you do not have to do anything before. However, we have seen how zlisp
is designed to be easily extended via Java code, and the most practical
way to do this is creating a package. If you are interested
in writing new packages, you will have to wait for the document the author
is preparing on which you will read as much as you want about extending
zlisp.
By now, this chapter treats only about how can you use some precompiled
packages included with zlisp.
Let us be clear from the beginning: a zlisp package
is a Java class. This class may contain some Methods and initialization
code and is dynamically loaded when you use the zlisp operator import.
Note
that when you specify the class name you want to load, you should write
the whole Java name of the class (i.e. you should write zlisp.LispMath
instead
of LispMath) so that the Java machine would be able
to fetch the class.
5.2. Available packages for zlisp.
Two predefined packages are included, by now, with
zlisp:
LispMath extends zlisp ability for numerical calculus and LispGraph
allows the user, when running zlisp in a graphic environment (as
an applet in your browser, for example), to draw graphics on the screen.
5.2.1. The LispMath package.
When you tell the interpreter to evaluate the expression
(import
"zlisp.LispMath") these new symbols get linked:
sin, cos, tan, asin, acos, atan:
The standard trigonometric functions. (no further explanation required)
Example: (sin 3.141592) -> Approximately 0.
log, exp:
The natural logarithm (on base e= 2.718281...) and its inverse, the
exponential function.
Example: (log 1,0) -> Approximately 0.
sqrt:
The square root function.
Example: (sqrt 4.0) -> 2,0.
random:
When called, returns a random number between 0.0 and 1.0.
Example: (random) -> [Any]
round:
(round x) returns the nearest integer number to
x
Example: (round 3.1415) -> 3
e, pi:
Linked to numbers, not methods, they have the numeric values e=2.718281828459045,
pi=3.141592653589793
Example: e -> 2.718281...
5.2.2. The LispGraph package.
When you run zlisp inside a graphic
environment (e.g. as an applet), you can take advantages of its graphics
capabilities. At any time you can switch between the text input/output
and the graphics output by clicking on the Text/Graph button. However,
you must remember that when you you start zlisp, graphics are not
initialized, so you will get nothing when you switch to graph mode. To
initialize graphics, you have just to load the LispGraph package by evaluating
(import
"zlisp.LispGraph"). The graphics canvas is then set up to
a white rectangle and you can start drawing. When you need to specify a
pixel of the rectangle, you have to give its cartesian coordinates; remember
that they are referred to the upper left corner and that they should always
be two integers. On the other hand, when you want to specify a color, you
must give its three RGB components (red, green and blue) in a range from
0 to 1; they must be, therefore, float numbers. The symbols contained in
the LispGraph package are:
circle:
(circle x y r) draws a circle at coordinates (x,y)
with
radius r
Example: (circle 50 50 25)
clear:
Clears the graphics canvas by filling it with color white.
Example: (clear)
cls:
(cls r g b) fills the canvas with the color
specified by its three components.
Example: (cls 0,0 0,0 1,0) paints the canvas on blue.
color:
(color r g b) selects the
specified color as the current pen color. This means that all drawing functions
will use this color. The color is selected until you select a new one with
this same function.
Example: (color 1,0 0 0) selects the red as the current
pen color.
drawmsg:
(drawmsg x y object)
prints the specifyed object at coordinates (x,y),
using the selected font and color.
Example: (drawmsg 20 20 "O wild west
wind, thou breath of Autumn's being...")
line:
(line x1 y1 x2 y2) draws a line from point
(x1,y1)
to point (x2,y2).
Example (line 0 0 50 50)
rect, fillrect:
(rect x1 y1 x2 y2) draws a rectangle with
its upper left corner at (x1,y1) and
its lower right corner at (x2,y2). fillrect fills
the rectangle with the currently selected color.
Example: (rect 20 20 50 60)
point:
(point x y) colorizes a single pixel at
coordinates (x,y)
Example: (point 100 100)
setfont:
(setfont name style size)
selects a font from the available Java fonts. These are implementation
dependent, but as zlisp is intented to run under Java 1.0 (still, most
browsers don't support Java 1.1 nor 1.2), fonts Helvetica, Courier,
TimesRoman and Symbol are granted. To specify the style of the
font you may use one of the predefined symbols included in zlisp.LispGraph:
BOLD,
PLAIN and ITALIC as well as sums of them, such
as (+ BOLD ITALIC).
Example: (setfont "TimesRoman" ITALIC 16)
pendown:
zlisp includes a turtle-based graphics system, just like
the one LOGO popularized. On a system like this, you must tell the turtle
whether is he has to draw when is moving or not. When you call pendown,
the turtle knows that he has to put his pen down and draw his path across
the canvas. Note that you must have the pen down if you want to draw something
even when you are not using turtle based functions, so you have always
to call pendown before you start drawing graphics.
Example: (pendown)
penup:
The opposite to pendown, penup tells the turtle
to put his pen up and not to draw anything.
Example: (penup)
forward:
(forward s) tells the turtle to advance a distance
s
in the direction he is actually facing. If the pen is down, a line will
be drawn, if the pen is up, the turtle will move but he will not draw anything.
Example: (forward 50)
left:
(left n) tells the turtle to turn
left n sexagesimal degrees.
Example: (right 60)
right:
(right n) tells the turtle to turn
right n sexagesimal degrees.
Example: (right 60)
turtle:
(turtle x y a) puts the turtle on position
(x,y)
and facing a direction that forms an angle a
with the horizontal.
Example: (turtle 20 20 0)
height, width:
Linked to integer numbers that specify, respectively, the height and
width of the graphics canvas.
Example: width -> 400 (e.g.)
6. Examples on using zlisp.
Once we have introduced
all the elements of zlisp, we are able to show how to program some
toys to give you some examples on the LISP language and on zlisp.
Some of them (namely the Hanoi towers example) are classic illustrations
on the LISP language that you will find on many treatises on this language,
but others, as the one of the Koch curves, take advantage of the graphics
capabilities of zlisp that are absent on many other LISP dialects.
The purpose of this examples is giving you some code to get started about,
it will be a good idea for you to modify and rewrite some parts of the
code to familiarize yourself with the language. If you have some suggestion
about these programs, please, e-mail me.
6.1. The sieve of Eratosthenes.
As the 1979 edition of the Enciclopaedia Britannica
defines it, the sieve of Eratosthenes is a
"systematic procedure for finding prime numbers (numbers
not divisible by an integer greater than one, except themselves) with all
the natural numbers arranged in order. After striking out the number 1,
one simply strikes out every second number following the number 2, every
third number following the number 3, and continues in this manner to strike
out every nth number following the number n. The remaining
numbers, from 2 on, will be prime numbers. The procedure is named for the
3rd-century-BC Greek astronomer-mathematician Eratosthenes of Cyrene."
Of course, it would be impossible for any computer to perform such a procedure
over all the natural numbers, so we will limit ourselves to a finite
set of numbers ranging from 1 to 99. Thus, the first thing we have to do
is to create a list containing all these numbers. This list, which we will
call numbers can be created by many ways. We will
use the following one:
(setq numbers (do ((i 100 (sub1 i)) (l nil (cons i l)))
((equal i 2) l)))
Now we will define a function to now if a number n is divisible
by m:
(defun divisible (n m) (zerop (mod n m)))
Thus we can create a function that takes a list and returns its cdr
except
those elements that are factorized by the car:
(defun cribe (list) (subset '(lambda (x) (not (divisible
x (car list)))) (cdr list)))
So if we call (cribe '(2 3 4 5 6 7 8 9)) we will get
(3
5 7 9). We are close to our target; to finish our task, we have
only to create a recursive function that calls cribe
and goes through the list avoiding the numbers we know they are not primes:
(defun sieve (list)
(cond
((null list) nil)
(t (cons (car list) (sieve (cribe list)))
)
)
Finally, to make a list containing all the prime numbers from 2 to 100,
we have only to evaluate the expression
and it will give us the list (2 3 5 7 11...) that
we were looking for.