kers@otter.HP.COM (Christopher Dollin) (12/17/87)
Another question about doing things in CL! If p and q are functions with arbitrary in-arity and out-arity, what is the idiomatic efficient way of writing a function (compose p q) which delivers a function that when applied, applies q to the result of applying p to the argument? (defun compose (p q) #'(lambda (&rest args) (multiple-value-call q (apply p args)) ) ) is the obvious solution. Would implementors like to comment on its efficiency? Similarly if p is a function, what is an idiomatic or efficient way to define (partapply p a1 ... an) which produces a function which, when applied to arguments b1 ... bn, applies p to a1 ... an b1 ... bn. (defun partapply (p &rest args) #'(lambda (&rest more) (apply p (append args more)) ) ) should work but it certainly doesn't LOOK efficient ... Regards, Kers | "Why Lisp if you can talk Poperly?"
barmar@think.COM (Barry Margolin) (12/19/87)
In article <1350002@otter.HP.COM> kers@otter.HP.COM (Christopher Dollin) writes: >Another question about doing things in CL! Even if your questions were silly (which they aren't), it's just such a refreshing break to see stuff in this newsgroup that isn't flames... > (defun compose (p q) > #'(lambda (&rest args) > (multiple-value-call q (apply p args)) > ) > ) > >is the obvious solution. Would implementors like to comment on its efficiency? That looks right to me. If you are willing to let COMPOSE be a macro instead of a function, then you could do: (defmacro compose (p q) `#'(lambda (&rest args) (apply ,q (multiple-value-list (apply ,p args))))) The only advantage this has is that it doesn't need to create a lexical closure to capture the local values of P and Q. However, this only conses a couple of words in the implementations I'm familiar with; in general, one needn't be afraid of returning lexical closures. > (Defun partapply (p &rest args) > #'(lambda (&rest more) > (apply p (append args more)) > ) > ) > >should work but it certainly doesn't LOOK efficient ... This one can definitely be made more efficient by being a macro, because then it doesn't have to do the APPEND at run time. (defmacro partapply (p &rest args) `#'(lambda (&rest more) (apply ,p ,@args more))) This isn't precisely correct, though, because it will evaluate the args at the time the resulting function is called rather than when partapply is called. For example (defun my-+ (a b) (let ((test-function (partapply #'+ a))) (setq a 0) (apply test-function b))) It's difficult to get it right, so your solution is probably what I would use. Symbolics has some code that tries to do something like the above right; their solution was to expand into a LET that binds all the free variables to their current values, and return a closure in that environment (they call this "snapshotting" the environment), but it has the problem that if the code modifies one of the snapshotted variables the change isn't seen outside the snapshot environment. (do ((i 1 (1+ i))) ((> i 10)) (snapshot-variables (setq i (1+ i)))) --- Barry Margolin Thinking Machines Corp. barmar@think.com seismo!think!barmar
kers@otter.HP.COM (Christopher Dollin) (01/14/88)
One problem with using lexical variables freely (as in compose in the basenote) is that although it may cons only a few words, the resulting closure may contain pointers to unused but arbitrarily large structures, thus permitting garbage to be retained way part its normal collection. I'm afraid I don't know enough about Lisp implementation to know if this is likely to be a problem: how is the environment usually represented? [In the case of Pop11, lexical closures are implemented as concealed partial applications, like lambda-lifting; hence no extra garbage is retained, but the closure is more expensive to compute. It conses store by something like fixed overhead + 2 * number of free variables where the fixed overhead is that for a simple procedure plus call to the part-applyed procedure, and each free variable contributes some store for the indirection headers and some for the instruction to push it as an argument. If I have messed up the description I apologise in advance to John Gibson and the rest of the Poplog team!] Regards, Kers. | "Why Lisp if you can talk Poperly?"