Adding useful functions to uLisp
Inevitably due to size limitations there are many useful functions in Common Lisp that are not provided in uLisp. If one of your favourite functions is missing, this page shows how to write the most popular ones in uLisp.
You can make sure that the functions you want are always available in your copy of uLisp using the Lisp Library feature as described in Adding these functions using the Lisp Library below.
Note that in some cases these aren't as fully featured as the full Common Lisp versions. For example:
- The sequence functions only support lists.
- Keyword arguments such as :key and :test aren't supported.
- There's reduced error checking.
These functions are all written using short variable names of three characters or less for the parameters, to take advantage of the fact that uLisp stores these more efficiently.
Update
5th November 2023: This page describes an updated version of the function library with the following changes:
- Some functions have been removed as they are now built-in functions in the latest versions of uLisp.
- Some functions have been rewritten more efficiently using updates in the latest versions of uLisp.
- The function library now uses a C++ Raw String Literal to avoid the need to enclose each Lisp Library line in double quotes, and escape special characters.
- An alternative version of the function library is provided with integral documentation for each of the functions.
Adding these functions using the Lisp Library
The function library is provided in two alternative versions:
- Minimal-memory version, for platforms with limited Lisp workspace: Minimal Lisp Library.
- Version with integral documentation, for larger memory platforms: Lisp Library with documentation.
Since the Lisp Library is stored in program memory, adding it has no impact on the workspace or performance of uLisp until you load the definitions you want, as described below.
To add this to your copy of uLisp
- Download the appropriate version and save it as LispLibrary.h.
- Put this in the same Arduino project folder as the uLisp source file.
- Comment out the definition of LispLibrary[] at the start of the uLisp source:
// Lisp Library // const char LispLibrary[] PROGMEM = "";
- Add a reference to the LispLibrary.h file in the main uLisp source file by uncommenting the line:
#include "LispLibrary.h"
- Compile and upload uLisp to your board.
To list the symbols in the Lisp Library:
Give the command:
(list-library)
To load a single definition
You can load definitions only when you need them, to minimise the amount of workspace used, by giving a command such as:
(require 'count)
To load all the definitions
You can make all the functions defined in the Lisp Library get loaded at startup by uncommenting the #define:
#define lisplibrary
Now you can see that all the definitions have been added to your workspace by doing:
(pprintall)
Function summary
butlast
Returns all but the last item in lst.
(defun butlast (lst) (unless (null lst) (subseq lst 0 (1- (length lst)))))
count
Counts the number of items eq to x in lst.
(defun count (x lst) (if (null lst) 0 (+ (if (eq x (car lst)) 1 0) (count x (cdr lst)))))
count-if
Counts the number of items in lst for which tst is true.
(defun count-if (tst lst) (if (null lst) 0 (+ (if (funcall tst (car lst)) 1 0) (count-if tst (cdr lst)))))
count-if-not
Counts the number of items in lst for which tst is false.
(defun count-if-not (tst lst) (if (null lst) 0 (+ (if (funcall tst (car lst)) 0 1) (count-if-not tst (cdr lst)))))
eql
In uLisp eq and eql are equivalent.
(defvar eql eq)
every
Returns t if tst is true for every item in lst, or returns nil on the first false item.
(defun every (tst lst) (if (null lst) t (and (funcall tst (car lst)) (every tst (cdr lst)))))
find
Returns x if x is in lst, or nil otherwise.
(defun find (x lst) (car (member x lst)))
find-if
Returns the first item in lst for which tst is true, or nil otherwise.
(defun find-if (tst lst) (cond ((null lst) nil) ((funcall tst (car lst)) (car lst)) (t (find-if tst (cdr lst)))))
find-if-not
Returns the first item in lst for which tst is false, or nil otherwise.
(defun find-if-not (tst lst) (cond ((null lst) nil) ((not (funcall tst (car lst))) (car lst)) (t (find-if-not tst (cdr lst)))))
fourth
Returns the fourth item in lst.
(defun fourth (lst) (car (cdddr lst)))
identity
Returns its argument.
(defun identity (x) x)
last
Returns the last cdr of lst.
(defun last (lst) (unless (null lst) (subseq lst (1- (length lst)))))
mapl
Applies fn to successive cdrs of lst, and returns lst.
(defun mapl (fn lst) (mapl2 fn lst) lst) (defun mapl2 (fn lst) (cond ((null lst) nil) (t (funcall fn lst) (mapl2 fn (cdr lst)))))
maplist
Applies fn to successive cdrs of lst, and returns a list of the results.
(defun maplist (fn lst) (if (null lst) nil (cons (funcall fn lst) (maplist fn (cdr lst)))))
nconc
Destructively appends its arguments together, which must be lists.
(defun nconc (&rest ls) (mapcan #'(lambda (x) x) ls))
nthcdr
Returns the nth cdr of lst.
(defun nthcdr (n lst) (when (< n (length lst) (subseq lst n))))
position
Returns the position of the first x in lst, or nil if it's not found.
(defun position (x lst &optional (n 0)) (cond ((null lst) nil) ((eq x (car lst)) n) (t (position x (cdr lst) (1+ n)))))
position-if
Returns the position of the first item in lst for which tst is true, or nil if none is found.
(defun position-if (tst lst &optional (n 0)) (cond ((null lst) nil) ((funcall tst (car lst)) n) (t (position-if tst (cdr lst) (1+ n)))))
position-if-not
Returns the position of the first item in lst for which tst is false, or nil if none is found.
(defun position-if-not (tst lst &optional (n 0)) (cond ((null lst) nil) ((not (funcall tst (car lst))) n) (t (position-if-not tst (cdr lst) (1+ n)))))
reduce
Returns the result of applying fn to successive pairs of items from lst.
(defun reduce (fn lst) (if (null (cdr lst)) (car lst) (funcall fn (car lst) (reduce fn (cdr lst)))))
remove
Removes all occurrences of x from lst.
(defun remove (x lst) (mapcan #'(lambda (y) (unless (eq x y) (list y))) lst))
remove-if
Removes all items from lst for which tst is true.
(defun remove-if (tst lst) (mapcan #'(lambda (x) (unless (funcall tst x) (list x))) lst))
remove-if-not
Removes all items from lst for which tst is false.
(defun remove-if-not (tst lst) (mapcan #'(lambda (x) (when (funcall tst x) (list x))) lst))
subseq (for lists)
Returns the subsequence of lst from item n to item m-1.
(defun subseq* (lst n m) (cond ((> n 0) (subseq* (cdr lst) (1- n) (1- m))) ((zerop m) nil) (t (cons (car lst) (subseq* (cdr lst) 0 (1- m))))))
third
Returns the third item in a list.
(defvar third caddr)