(INCF CL)is a collection of convenience functions and macros for Common Lisp. The features it provides are:
- List comprehensions.
- Doctest suite for automatic verification of examples in docstrings.
- List manipulation functions similar to those in Haskell's prelude.
- Nesting functions akin to those available in Mathematica.
The easiest way to install
(INCF CL) is to use ASDF-INSTALL:
(asdf:load-system :asdf-install) (asdf-install:install :incf-cl)
$ git clone git://github.com/jmbr/incf-cl.git
and then follow the ASDF installation procedure for your CL implementation.
Note that you will need Stefil if you want to run the test suite.
Loading the library
To begin using the library write:
(asdf:load-system :incf-cl) (use-package :incf-cl)
RANGE is similar to MATLAB's vector notation. Some use cases are:
CL-USER> (range 1 10) (1 2 3 4 5 6 7 8 9 10) CL-USER> (range 0 1/4 1) (0 1/4 1/2 3/4 1)
List comprehensions are a programming language construct that closely mimics the way you declare a set in mathematics and are sometimes more succinct and readable than a composition of
DELETE-IF or a loop.
Here are two examples of how to use the
LC (short for List Comprehension) macro:
CL-USER> (lc (sin x) (<- x (range 0 .25 (/ pi 2)))) (0.0 0.24740396 0.47942555 0.6816388 0.84147096 0.9489846 0.997495) CL-USER> (lc (cons x y) (<- x (range 0 2)) (<- y (range 0 2)) (= (+ x y) 2)) ((0 . 2) (1 . 1) (2 . 0))
DOCTESTchecks documentation strings for correctness. For every exported function in the package name passed to
- each docstring is scanned for pieces of text resembling interactive sessions,
- then those snippets are evaluated,
- and the resulting values are checked against the expected ones.
For example, consider the package
(defpackage :test (:use :common-lisp :incf-cl) (:export :factorial)) (in-package :test) (defun factorial (n &optional (acc 1)) "Returns the factorial of N, where N is an integer >= 0. Examples: TEST> (lc (factorial n) (<- n (range 1 5))) (1 2 6 24 120) TEST> (factorial 450/15) 265252859812191058636308480000000 TEST> (signals-p arithmetic-error (factorial -1)) T TEST> (signals-p type-error (factorial 30.1)) T TEST> (factorial 0) 1" (declare (type integer n)) (cond ((minusp n) (error 'arithmetic-error)) ((/= n (floor n)) (error 'type-error))) (if (= n 0) acc (factorial (1- n) (* n acc))))
You can use
DOCTEST to make sure the examples given in
FACTORIAL's documentation string work as expected by writing
CL-USER> (doctest :test) ..... T
CL-USER> (doctest 'test::factorial) ..... T
PreludeSome list manipulation functions patterned after Haskell's prelude are available. Namely,
CYCLE(and its destructive version
INTERSPERSE(and its destructive version
SCAN*(using the key parameters
:FROM-ENDit works as
Since Common Lisp doesn't guarantee tail call elimination, these functions are written iteratively to avoid stack overflows.
NEST-LIST applies a function to an initial value, then applies the same function to the previous result, and so on. This stops after a specified number of evaluations or when a given predicate is true and a list containing all the results is returned.
NEST works as
NEST-LIST but it only returns the last result, not the whole list.
CL-USER> (setf *print-circle* nil) NIL CL-USER> (nest-list (lambda (x) `(sin ,x)) 'z :max 3) (Z (SIN Z) (SIN (SIN Z)) (SIN (SIN (SIN Z)))) CL-USER> (nest-list #'+ '(1 1) :max 10) (1 1 2 3 5 8 13 21 34 55 89 144) CL-USER> (nest #'+ '(1 1) :max 10) 144 CL-USER> (nest-list (lambda (x) (mod (* 2 x) 19)) 2 :test (lambda (x) (/= x 1))) (2 4 8 16 13 7 14 9 18 17 15 11 3 6 12 5 10 1)
The closely related function
FIXED-POINT returns the fixed point of a function starting from an initial value. Whether a fixed point has been reached or not is determined by a test function (
EQL by default).
For example, the square root of 2 using Newton's method can be computed as:
CL-USER> (fixed-point (lambda (x) (float (- x (/ (- (expt x 2) 2) (* 2 x))))) 1) 1.4142135
There's an implementation of
UNFOLD-RIGHT as specified in SRFI 1: List library. Here's an example of
(defun euler (f x0 y0 interval h) "Computes an approximate solution of the initial value problem: y' = f(x, y), x in interval; y(x0) = y0 using Euler's explicit method. Interval is a list of two elements representing a closed interval. The function returns a list of points and the values of the approximate solution at those points. For example, EULER> (euler (lambda (x y) (declare (ignore y)) (- (sin x))) 0 1 (list 0 (/ pi 2)) 0.5) ((0 1) (0.5 1.0) (1.0 0.7602872) (1.5 0.33955175))" (assert (<= (first interval) (second interval))) (unfold (lambda (x) (> (first x) (second interval))) #'identity (lambda (pair) (destructuring-bind (x y) pair (list (+ x h) (+ y (* h (funcall f x y)))))) (list x0 y0)))
$ returns the composition of several functions. The following example illustrates its use:
CL-USER> (funcall ($ (lambda (x) (* x x)) (lambda (x) (+ x 2))) 2) 16
Hash table utilities
DOHASH iterates over a hash table with semantics similar to those of
CL-USER> (defparameter *hash-table* (make-hash-table)) *HASH-TABLE* CL-USER> (setf (gethash "one" *hash-table*) 1) 1 CL-USER> (setf (gethash "two" *hash-table*) 2) 2 CL-USER> (setf (gethash "three" *hash-table*) 3) 3 CL-USER> (dohash (key value *hash-table*) (format t "~a => ~d~%" key value)) three => 3 two => 2 one => 1 NIL CL-USER> (let ((product 1)) (dohash (key value *hash-table* product) (setf product (* product value)))) 6
STRING-JOIN glues together a list of strings placing a given separator between each string. By default, the separator is a space.
CL-USER> (string-join '("Hello" "world")) "Hello world" CL-USER> (string-join '("Hello" "world") ", ") "Hello, world"
LinksSome of the features of
(INCF CL)are discussed in:
- Playing with List Comprehensions in CL
- Programming Collective Intelligence in Common Lisp, Chapter 5 - Optimizations
Please send suggestions, patches, and bug reports to the author's email address.