Patrick (patrickwonders) wrote in lisp,
Patrick
patrickwonders
lisp

Lisp Troubles: Fabricating a Closure...

The short version of my problem is that I want to do this:

(defun generate-setter (buffer pre post)
  (eval `(lambda (index value)
	   (setf (aref buffer ,@pre index ,@post) value))))

Except that I want the (lambda ...) to be a closure around buffer. Here, pre and post are lists generated at runtime (thus not available as lists at compile time) and hence the (defun ...) and (eval ...) instead of (defmacro ...). Alas, the (eval ...) form uses the null lexical environment, so I cannot capture buffer.

The long form with some of the myriad failed attempts I have made is on this blog post. Please, anyone have half a cup of clue that I could borrow? I promise, I'm going to the store for more clues really soon. I'll pay you back.

Edit: Here is exactly what I was looking for... (courtesy of tfb)

(defun generate-setter (buffer pre post)
  (let ((make-closure
           (compile nil `(lambda (buf)
                           (lambda (index value)
                             (setf (aref buf ,@pre index ,@post) value))))))
    (compile nil (funcall make-closure buffer))))
Subscribe
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 18 comments
(defun generate-setter (buffer pre post)
  (eval `(lambda (index value)
	   (setf (aref ',buffer ,@pre index ,@post) value))))


But these EVALs are worse than allocated list in one of your attempts (that can be potentially optimized by some implementations).

You have to write your own implementation of array-row-major-index-like function to use with row-major-aref, so that closure will calculate row-major index like index*const1+const2 where const1 and const2 depend on pre and post.

patrickwonders

October 8 2009, 12:35:19 UTC 8 years ago Edited:  October 8 2009, 12:38:40 UTC

But, the Eval only gets invoked once per row... not every time I invoke the setter. I don't call it like (funcall (generate-setter buffer pre post) index value). I store the output of (generate-setter buffer pre post) in my virtual-row struct and use it for all O(n lg n) row accesses.

And, yes, I did write my own implementation that did the (index*const1 + const2), but it feels like cheating to assume that if (apply #'array-row-major-index buffer (append pre (list 0) post)) is x and (apply #'array-row-major-index buffer (append pre (list 1) post)) is y, that (apply #'array-row-major-index buffer (append pre (list ii) post)) is going to be (x-y)*ii + x. That isn't guaranteed by the Hyperspec, as near as I can tell. [Though it would be an incredibly odd implementation that did otherwise.]
Ah... Thank you. I just read the array-row-major-index and row-major-aref pages.
why reinvent the wheel (reimplement row-major access)?
just use (setf apply).
It looks like you want a macro. In that case, just replace defun with defmacro.
And remove eval, of course...
No, because pre and post are not set at compile time.

It turns out that making one lambda in there works. See the comments at the blog post.
(defun generate-setter (buffer pre post)
(lambda (index value)
(setf (apply #'aref buffer (append pre (list index) post)) value)))
Yes, but that does an append every time I invoke the setter. I call in n lg n times with the same pre and post.
then you should close over a pre-existing list:
(defun foo (buf pre post)
(let ((index-tail (cond nil post))
(index-all (append pre index-tail)))
(lambda (index value)
(setf (car index-tail) index)
(setf (apply #'aref buf index-all) value))))
But, then I don't trust using it with rotatef and setf and threading....
what is "it" you don't trust?

patrickwonders

8 years ago

aphar

8 years ago

patrickwonders

8 years ago

note that you original "eval" version does the same.
i.e., it conses _more_ than my code because on top of the list of indexes it conses up the lambda and produces and interpreted closure.
note that both my versions produce compiled closures (if, of course, the function itself is compiled).
The goal in my code was to make it so that I can create a setter for this virtual "row" of the matrix and then use it a bunch of times in a row. With my eval case (change it to (compile nil ...) if you like for your implementation) was to only cons the list together once... get it as a function... and call it a bunch of times... those calls do not have to cons.

Your code invoked append every time I called the function it produced. My code only invoked append while producing the function.