Some small things of no real use
NCASE: CASE using
a jump-tableFOR: Iteration in the
style of PythonLisp is a `verb-noun' language: apart from special forms and
some macros, its syntax is uniformly
(fn argument ...).
Where fn is the `verb' and the arguments are
the things - `nouns' on which the verb operates.
Most traditional programming languages are also verb-noun.
Syntaxes like fn(arg, ...) abound. However some
recent so-called `object oriented' languages are `noun-verb', at
least in their OO parts. Thus you see syntaxes like
object.operator(...) where object is
the `noun', or subject, operator is the `verb', and
the arguments are `objects'.
It has been argued - apparently seriously - that this syntax is
an important component of object orientation.
Clearly, this syntax is very resrictive, since it effectively assumes a single-dispatch system where there is always exactly one privileged `subject'. For most `OO' languages (SmallTalk is a notable exception) things also break down horribly, since there is a conventional `verb-object' syntax as well as the `subject-verb' syntax. This is particularly horrible because it means that you always know whether you are doing `OO' or `ordinary' programming. In particular an interface defined one way can never transparently migrate to the other form. In Lisp you never need know that something has changed.
Be that as it may, if you want this subject-verb-object syntax
for Lisp, you can have it: all you need do is twiddle the first
two things that read returns. This code does it
using [subject verb object ...] instead of
(verb subject object ...).
See here for some notes on
read-delimited-list and related things in CL.
| Copyright | slip.lisp
is copyright 2000 by me, Tim Bradshaw, and may be used for
any purpose whatsoever by anyone. It has no warranty
whatsoever. I would appreciate acknowledgement if you use
it in anger, and I would also very much appreciate any
feedback or bug fixes. |
|---|---|
| Here | is the current version. |
| Please mail me | with any bugs or comments. |
In CL, there is no real notion of a `global lexical
environment'. In particular there is no way of having a
variable defined at top level which behaves the same way as
lexical variables at other levels. Saying (defvar x
...) causes x to be globally special, while
simply doing (setf x ...) is undefined, and
therefore can be correctly intepreted by different
implementations in different ways, including making daemons fly
out of your nose.
The lack of global lexicals is not often a problem, although it causes endless confusion to people who either don't understand or don't want to understand what is going on. Fortunately, it's relatively easy to provide a simulation of global lexicals with symbol macros: all you do is define a secred `global binding environment' - a hash table, say, and then define global lexicals as symbol macros which get their value from this environment. A couple of refinements support unbound global lexicals, and help things work at compile time. Unfortunately, because there is no portable way to teach the compiler only about something, getting the correct compile time behaviour really involves compilation causing the lexical variable to become defined, which is not ideal, but should not hurt too much.
The user macro in this code is defglex (`DEFine
Global LEXical'). An example of using it is as follows.
(defglex /x/ 1)
(defun foo (y)
;; this function can see the global lexical value of /X/
(+ y /x/))
(let ((/x/ 2))
(defun bar (y)
;; This function closes over the lexical value of /X/, 2.
(+ y /x/)))
| Copyright | glex.lisp
is copyright 2003 by me, Tim Bradshaw, and may be used for
any purpose whatsoever by anyone. It has no warranty
whatsoever. I would appreciate acknowledgement if you use
it in anger, and I would also very much appreciate any
feedback or bug fixes. |
|---|---|
| Here | is the current version. |
| Please mail me | with any bugs or comments. |
NCASE: case using
a jump-tableThis is an example of how you can use macros to do some hairy optimisation of code which would not be possible without either modifying the compiler or writing a macro system in languages without proper macros.
The macro ncase is semantically the same as
case. However, in certain cases - when all the keys
are integers, and when they pass a (possibly user-defined) test,
it will rewrite the code using a jump-table - an array is created
containing the bodies of the cases, and an element of that array
is simply funcalled. This means that a possibly large number of
tests are replaced by a single indirection and function
call.
In real life, a compiler is likely to be able to make a better
decision about whether or not to compile a jump-table than
ncase can, and it will have lower-level access to the
machine as well. However, this is still a good example of the
sort of things that macros can do, and things `like'
ncase are often useful in practice, and not
realistically possible without macros.
| Copyright | ncase.lisp
is copyright 2003 by me, Tim Bradshaw, and may be used for
any purpose whatsoever by anyone. It has no warranty
whatsoever. I would appreciate acknowledgement if you use
it in anger, and I would also very much appreciate any
feedback or bug fixes. |
|---|---|
| Here | is the current version. |
| Please mail me | with any bugs or comments. |
FOR: iteration in the style of
PythonPython does iteration by having methods on classes which construct `iterators' - objects which iterate over the contents of the original object. This is similar to how some other languages do it, and it's probably a design pattern of some kind. The nice feature of something like this is that you can iterate over any object which supports the iteration protocol. The bad feature is that it's probably fairly hard to make iteration this way really efficient, although it's probably possible.
This implementation is fairly rudimentary - the protocol is probably inadequate, the iteration macro itself is fairly rudimentary, and iterators are only defined for a few types. The interesting things are:
iternextforlet. Has a cleverness that if the object being
iterated over looks like a call to iter it
doesn't generate one itself, allowing you to use non-default
args to iter.There is also some support for `range' objects - these are things which will iterate over a range of numbers.
| Copyright | for.lisp
is copyright 2004 by me, Tim Bradshaw, and may be used for
any purpose whatsoever by anyone. It has no warranty
whatsoever. I would appreciate acknowledgement if you use
it in anger, and I would also very much appreciate any
feedback or bug fixes. |
|---|---|
| Here | is the current version. |
| Please mail me | with any bugs or comments. |
Based on the above code, it turns out to be
extremely easy to write a macro, gather which works
like a generalised version of Python's list comprehensions.
(gather (* x x) for (x '(1 2 t 3)) when (numberp x) when (oddp x))
is equivalent to
(collecting
(for (x '(1 2 t 3))
(when (numberp x)
(when (oddp x)
(collect (* x x))))))
using a collecting macro which collects lists
forwards.
A more elaborate example:
(gather x for (c (range :from 0 :below 100)) for (l (range :from 1 :to c)) let ((x (random l))) when (evenp x))
Is the same as
(collecting
(for (c (range :from 0 :below 100))
(for (l (range :from 1 :to c))
(let ((x (random l)))
(when (evenp x)
(collect x))))))
Note that forms nest, as they do with Python's list comprehensions.
gather used to restric the `clause keywords' but
in fact it doesn't need to - it simply rewrites things so that
they nest appropriately, and so it can be used with any `standard
pattern with-x macro, such as
with-open-file, dolist or macros of your
own invention.
gather is contained in for.lisp.