Ahh yes I have indeed been partaking of quite a large volume of functional kool-aid lately, its quite scary. This week I’ve mostly just been doing lisp, lisp, lisp, and more lisp. In fact I don’t think I’ve touched Perl this whole week long, which is weird.
Still its been a good time, I’ve grappled with CLOS (let me fetch my crack pipe) and won the functionality I wanted, I’ve started to do more experimentation with some of the finer points of the whole style of coding itself.
For example, let me for a moment take you on a waffly journey around my criteria system (EDIT: Its probably worth knowing that I’m writing software that searches a graph of nodes and looks for suitable paths between any two nodes), for judging if a path has the right metrics/length/whatever to be acceptable.
We start with (make-criteria-pointer), this takes the name of a function and the arguments to pass to it, and returns a pointer to a function that takes a single path and runs the original function/args against it, returning true or false.
While that sounds a bit confusing, let me break it down for you, I’ve got a function for juding if a list is too long, which looks like this:
(defun max-length-p (path n) "Accepts a list-of-nodes (path) and an length (n) and returns T(rue) if the list is shorter than the length" (if (<= (list-length path) n) T nil))
So if I run
(make-criteria-pointer 'max-length-p 5)
it returns a pointer to a new anonymous function that looks like this:
(lambda (x) (max-length-p x 5))
i.e. it always checks to see if x is longer than 5 items long.
Now this doesn’t seem to be too useful, however there are simple ways to build a list of these function pointers, so if I do the following:
;; Maximum path length is 5 (new-criteria (make-criteria-pointer 'max-length-p 5)) ;; Path must include one of the nodes foo,bar or baz (new-criteria (make-criteria-pointer 'path-must-include-p '(foo bar baz))) ;; The minimum level of trust between nodes is 10 (new-criteria (make-criteria-pointer 'metric-p '("trust" 10)))
Then new-criteria keeps adding these function pointers to a list (called *criteria*, although you can tell it to add them to another list), and now if you say:
(check-path-p path-to-check *criteria*)
It will go through the list stored in *criteria*, passing path-to-check to each one of these functions and if the list doesn’t pass muster then check-path-p just returns false.
metric-p is also a generic checker of numeric (or other) values on links in the path, and so you can make it do crazy shit like this:
(metric-p path-to-check '("bananas" "plenty") :test #'(lambda (x y) (check-number-of-bananas x y)))
And it’ll dig up the “bananas” value about each link in path-to-check and instead of just checking this value numerically it will pass x (the value from each link) and y (“plenty”) into the newly defined function (lambda is an anonymous function), which will call check-number-of-bananas on these two values to determine if the link has “plenty” of bananas.
Now I’m sure there are plenty of other ways of doing systems like this but frankly Lisp can be deeply satisfying to write this kind of thing in. Once I got the last bits of this system in place this evening I left work pondering ways to improve it for speed or functionality, and have a list of three/four more things it could do by the end of Monday to complete it.
Just so damn shiny!