Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

How to implement list comprehensions in Lisp is an open-ended topic.

For instance, I did it once in a "researchy" way by implementing monads. With the help of CLOS, I was able to define various monads such as the list monad, identity monad, or state transformer monad (the map, join and unit being methods dispatched on the monad type).

Then a generic monadic comprehension macro provides different functionality based on which monad is selected. The list monad gives rise to a list comprehension. The identity monad gives rise to just sequential variable binding. And the state transformer monad ... to something like a pipeline of succcessive state transformations, I think.

http://www.kylheku.com/cgit/lisp-snippets/tree/monads.lisp

If you just want a list comprehension, it's not very difficult. Basically it has to expand to code which (for example) expresses a nested loop that steps every variable over its respective list. The expression being collected by the comprehension is stuck into the middle of this loop, inside a piece of code which collects its value into a list, which is stored in a hidden local variable. (Perhaps two local variables are used, to keep track of the head and tail of the list for the sake of efficiency.)

ANSI Lisp has functional applicators that process multiple lists. For instance if you want to add together values from two lists, you can do this:

  (mapcar '+ '(1 2 3) '(10 20 30)) 

   -> (11 22 33)
If you wanted to form a cross product of the two lists instead, the obvious thing would be to write a mapcar-like function:

  (mapcar-cross '+ '(1 2 3) '(10 20 30))

   -> '(11 21 31 12 22 32 13 23 33)
This function could be used as the target syntax for a comprehension macro so that, say:

  (list-comprehend (+ b a) (a '(1 2 3)) (b '(10 20 30)))

   -> '(11 21 31 12 22 32 13 23 33)
is macro-expanded into

  (mapcar-cross (lambda (a b) (+ b a)) '(1 2 3) '(10 20 30))
Translating (transliterating, really) the list-comprehend syntax into mapcar-cross is not very difficult: it's just some very straightforward nested list manipulation. The macro has to collect the list of variables from the trailing arguments after the expression, to form the (a b) argument list of the lambda. It has to remove the variables from those trailing arguments to produce the list expressions like '(1 2 3) and '(10 20 30) that will be applied to mapcar-cross. Then it has to assemble the mapcar-cross syntax.

Of course, you also have to write mapcar-cross, which is your "run-time support function" for your list-comprehend syntax (usable directly without that syntax, too).



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: