Edit: lispnik on Livejournal pointed out this section of the Hyperspec which gaurantees that my pointer math is valid. I’d still like to know how to make a closure like this though, for future reference.
Edit(2): See tfb’s comment below for a solution that does make a closure using a nested (LAMBDA …) inside the backquote.
My Fast Fourier Transform library operates on multi-dimensional arrays. When you do a Fourier Transform on a two-dimensional array, you do a Fourier Transform on each row and then on each column of the result (or each column and then on each row of the result). When you do a three-dimensional Fourier Transform, you do a two-dimensional Fourier Transform on each horizontal slice and then a one-dimensional Fourier Transform on each vertical column.
The problem
To accomplish this on arrays of any number of dimensions, I wrote a wrapper class called virtual-row that one can use to index any axis-parallel row in a data hypercube with an API that makes it look like a one-dimensional array. So, for example, if I had a four-dimensional array, zero-ing one of those rows explicitly might look like this:
(setf (aref array 1 3 kk 5) 0)
In my abstracted world, it would look like this:
(dotimes (kk (row-length row))
(setf (row-ref row kk) 0)))
A simple, straightforward implementation of the virtual row setter function might look like this:
(with-slots (array pre post) row
(let ((index (apply #'array-row-major-index
array
(append pre (list index) post))))
(setf (row-major-aref index) value))))
The problem with that implementation is that it allocates a new list every time. This function is called times per row (and there are virtual rows in a -dimensional hypercuboid where the -th dimension has entries).
The Goal
What I wanted to do instead was to generate a closure that incorporated the pre and post lists. What I wanted was almost this:
(eval `(lambda (index value)
(setf (aref array ,@pre index ,@post) value))))
I say almost because (EVAL …) occurs in the null lexical environment
. This means that it doesn’t capture the array variable. If I put a comma in front of the array, then I get a representation of the value to which array is bound, which doesn’t help me. Nor does passing in ’array since array is a lexical variable where I’m calling it from, too.
Putting the (EVAL …) inside the (LAMBDA …) doesn’t help because then it happens at runtime and I just spend all of my time in crazy functions like (APPEND …) and (READ-FROM-STRING …).
I tried assembling a list by hand instead of with the backquote. This was hampered both by the fact that (LAMBDA …) is a macro and because I still couldn’t capture a lexical variable (only its value). Other attempts that lead nowhere are:
(let ((ff (read-from-string "(lambda () aa)")))
(funcall ff)))
(let ((aa "ABCDEFG"))
(let ((ff (compile nil (macroexpand '(lambda () aa)))))
(funcall ff)))
The (READ-FROM-STRING …) tells me that AA is UNBOUND. This is unsurprising. Why should it care about my current lexical environment?
The (MACROEXPAND …) gives me a fabulous message under SBCL:
Wanted one of (SB-C:LAMBDA-WITH-LEXENV LAMBDA
SB-KERNEL:INSTANCE-LAMBDA SB-INT:NAMED-LAMBDA).
[Condition of type SB-KERNEL:CASE-FAILURE]
It seems like it might be slightly possible to work something out with an extra macro-level and some use of the &ENVIRONMENT lambda list keyword. But, I am not even sure where to start trying to figure out how that all plays together.
The Hack
Having failed to make a good closure on the fly and not being interested in consing as much as the simple implementation above does, I have settled instead for cheating. Here is how I am cheating.
(flet ((compute-index (pre index post)
(apply #'array-row-major-index
(append pre (list index) post))))
(let* ((axis (length pre))
(offset (compute-index pre 0 post))
(second (min 1 (1- (array-dimension array axis))))
(span (- (compute-index pre second post) offset)))
(lambda (index value)
(setf (row-major-aref array (+ (* index span) offset)) value)))))
The idea here is to compute the difference in (ARRAY-ROW-MAJOR-INDEX …) between the first and second elements in the row. (Note: the whole business with (MIN …) is to avoid trouble if there is no second element in the row.) Then, I assume that the -th element of the row will be times that difference beyond the initial element of the row.
This works well and quickly. But, it is somewhat cheesy. There is no requirement that my index math be correct for any given implementation. It would shock me to find that some implementation disagreed with the above index math. Still, I’d really like to just fabricate a proper closure around (AREF …) instead.
Can anyone lend me a half a cup of clue?
Feh, I suppose that I can get away with it if I pass in the buffer each time. I’d rather have a closure, but this will work at the expense of having to pull the buffer out of my virtual-row each call:
(eval `(lambda (buffer index value)
(setf (aref buffer ,@pre index ,@post) value))))
Hi,
not answering your question, really, but have you looked at bordeaux-fft?
No… I haven’t looked at Bordeaux FFT. It didn’t come up when I searched for FFT on the Cliki or when I searched for FFT and Lisp together on Google. I will look at it.
Actually, it’s on my first page of Google results at the moment. I don’t know what I had searched for before, or how I had missed it. Definitely, it could use a mention on the Cliki.
The simple, straightforward implementation could look like this due to how APPLY and AREF are specified to work together:
(with-slots (array pre post) row
(setf (apply #'aref array (append pre (list index) post)) value)))
(Why isn’t this post tagged “lisp”?)
I thought that the (APPLY …) would totally mess with (SETF …)‘s head. Though, I suppose that I didn’t try it that way. That was my first implementation of the non-SETF version of this function. I see now where there is an example of using (APPLY #’AREF …) in a (SETF …) in the Hyperspec. Thanks for pointing that out.
If I can just get the closure part to work then, I’ll have a much more readable version than all this messing with ARRAY-ROW-MAJOR-INDEX.
(As for the tag: It wasn’t tagged Lisp because I’m still lame with WordPress. I typed in “lisp” into the tags box. It showed me the list of auto-completes, and I thought I was done. I forgot that I have to commit the tags with an “enter” or a mouse click… having the tags there when I hit “Publish” isn’t enough. Oops.)
You were close with
(eval `(lambda (index value)
(setf (aref array ,@pre index ,@post) value))))
If you generate a function that, when called, returns the closure, you can get what you were trying for there.
(let ((make-closure
(eval `(lambda (array)
(lambda (index value)
(setf (aref array ,@pre index ,@post) value))))))
(funcall make-closure array)))
And of course, you probably want to replace EVAL with COMPILE…
That… Is… Crafty….
Very nice. Thank you.
I had a similar problem some time ago, where EVAL took place in a null lexical environment, but needed to use some stuff defined in the function that contained the EVAL. My solution was to declare the stuff I needed inside the EVAl as special, giving it dynamic scope. Because the EVAl took place in the dynamic scope that included the special variables, I was able to access them from inside the EVAL. It looed something like this:
(defun name (arg1 arg2)
(declare (special arg1 arg2))
...
; the code in (nth item results) can access arg1 and arg2
(eval (nth item results)))
I thought about that for a bit here, too. In this case though, I would then need to be sure that special was appropriately bound during each invocation of my lambda.