From: Mirko on 5 Apr 2010 11:27 I have a utility that may need to do some unit conversions. I would like to specify the conversion rules as an association list: ((a do-something) (b do-something-else) ... etc) Now, I could specify `do-something' as #'(lambda (arg) (* arg 5)) and so on. All the lambda's have the same format #'(lambda (arg) (...)), only the ellipsis changing from item to item Specifying the lambdas works, but makes the a-list hard to read. Instead I would like it to contain the bodies of the lambda's. ((a (* arg 5)) (b (/ arg 12)) ... etc) So, the question is how to compile those bodies (I am not sure `compile' is the right term - I just want to get an `executable' lambda out of these). Right now, I am compiling via macros: (defmacro build (sym) `(lambda (arg) ,(second (assoc sym +table+))) and then (funcall (build a) arg) will execute my function. But I am wondering if I am misusing the macros here, and if there is something more direct Thanks, Mirko
From: Tamas K Papp on 5 Apr 2010 12:24 On Mon, 05 Apr 2010 08:27:52 -0700, Mirko wrote: > I have a utility that may need to do some unit conversions. I would > like to specify the conversion rules as an association list: > > ((a do-something) (b do-something-else) ... etc) > > Now, I could specify `do-something' as #'(lambda (arg) > (* arg 5)) > > and so on. All the lambda's have the same format #'(lambda (arg) > (...)), only the ellipsis changing from item to item > > Specifying the lambdas works, but makes the a-list hard to read. Instead > I would like it to contain the bodies of the lambda's. > > ((a (* arg 5)) (b (/ arg 12)) ... etc) > > So, the question is how to compile those bodies (I am not sure `compile' > is the right term - I just want to get an `executable' lambda out of > these). > > Right now, I am compiling via macros: > > (defmacro build (sym) > `(lambda (arg) > ,(second (assoc sym +table+))) > > and then > > (funcall (build a) arg) > > will execute my function. > > But I am wondering if I am misusing the macros here, and if there is > something more direct I think that this is OK, but I would do it in a slightly different way. You want convenient syntax for two things: defining new conversions, and performing them by name. I would just use a macro for the first and a generic function for the second, eg: (defgeneric do-conversion (name arg) (:documentation "Perform the named conversion on ARG.")) (defmacro define-conversion (name &body body) `(defmethod do-conversion ((name (eql ',name)) arg) ,@body)) (define-conversion m->km (/ arg 1000)) (define-conversion km->m (* arg 1000)) ;; example: (do-conversion 'm->km 1000) Best, Tamas
From: joswig on 5 Apr 2010 12:49 On 5 Apr., 18:24, Tamas K Papp <tkp...(a)gmail.com> wrote: > On Mon, 05 Apr 2010 08:27:52 -0700, Mirko wrote: > > I have a utility that may need to do some unit conversions. I would > > like to specify the conversion rules as an association list: > > > ((a do-something) (b do-something-else) ... etc) > > > Now, I could specify `do-something' as #'(lambda (arg) > > (* arg 5)) > > > and so on. All the lambda's have the same format #'(lambda (arg) > > (...)), only the ellipsis changing from item to item > > > Specifying the lambdas works, but makes the a-list hard to read. Instead > > I would like it to contain the bodies of the lambda's. > > > ((a (* arg 5)) (b (/ arg 12)) ... etc) > > > So, the question is how to compile those bodies (I am not sure `compile' > > is the right term - I just want to get an `executable' lambda out of > > these). > > > Right now, I am compiling via macros: > > > (defmacro build (sym) > > `(lambda (arg) > > ,(second (assoc sym +table+))) > > > and then > > > (funcall (build a) arg) > > > will execute my function. > > > But I am wondering if I am misusing the macros here, and if there is > > something more direct > > I think that this is OK, but I would do it in a slightly different > way. You want convenient syntax for two things: defining new > conversions, and performing them by name. I would just use a macro > for the first and a generic function for the second, eg: > > (defgeneric do-conversion (name arg) > (:documentation "Perform the named conversion on ARG.")) > > (defmacro define-conversion (name &body body) > `(defmethod do-conversion ((name (eql ',name)) arg) > ,@body)) > > (define-conversion m->km (/ arg 1000)) > (define-conversion km->m (* arg 1000)) > > ;; example: > (do-conversion 'm->km 1000) > > Best, > > Tamas How about this one without generic functions. Dispatch is done by symbol lookup if necessary. (defparameter *table* '((m->km (/ arg 1000)) (km->m (* arg 1000)))) (defmacro defconv (var conversions) (when (symbolp conversions) (setf conversions (symbol-value conversions))) `(progn ,@(loop for (name calculation) in conversions collect `(defun ,name (,var) ,calculation)))) (defconv arg *table*) (m->km 4) (funcall 'm->km 4) (defun convert (how what) (funcall how what)) (convert 'm->km 4)
From: Pascal J. Bourguignon on 5 Apr 2010 13:41 Mirko <mirko.vukovic(a)gmail.com> writes: > I have a utility that may need to do some unit conversions. I would > like to specify the conversion rules as an association list: > > ((a do-something) (b do-something-else) ... etc) > > Now, I could specify `do-something' as > #'(lambda (arg) > (* arg 5)) > > and so on. All the lambda's have the same format #'(lambda (arg) > (...)), only the ellipsis changing from item to item > > Specifying the lambdas works, but makes the a-list hard to read. > Instead I would like it to contain the bodies of the lambda's. > > ((a (* arg 5)) (b (/ arg 12)) ... etc) > > So, the question is how to compile those bodies (I am not sure > `compile' is the right term - I just want to get an `executable' > lambda out of these). > > Right now, I am compiling via macros: > > (defmacro build (sym) > `(lambda (arg) > ,(second (assoc sym +table+))) > > and then > > (funcall (build a) arg) > > will execute my function. > > But I am wondering if I am misusing the macros here, and if there is > something more direct This looks ok. Indeed, if your problem is to make a form more readable, using syntactic abstraction may be useful. Now the question is whether you need to do that at run-time or at compilation-time. If you need to do it at compilation-time, ie. if all your expressions are known at compilation-time, like are literally written in your program source, then you may indeed use a macro to wrap them up in a function. On the other hand, if you build this a-list at run-time, then you don't need a macro, you can do it with a function. The fact hat you use +table+ ie. a constant would let me think that this is indeed a compilation time matter. Then you don't have to wait run-time to build the function, you can do it at compilation time: (defun generate-function (arg-name body) `(lambda (,arg-name) ,@body)) (defun generate-function-alist (alist) (cons 'list (mapcar (lambda (entry) (list 'cons (list 'quote (car entry)) (generate-function 'arg (cdr entry)))) alist))) (defmacro function-alist (alist) (generate-function-alist alist)) (defconstant +table+ (function-alist ((a (+ arg 1)) (b (* arg 4))))) +table+ --> ((A . #<FUNCTION :LAMBDA (ARG) (+ ARG 1)>) (B . #<FUNCTION :LAMBDA (ARG) (* ARG 4)>)) so now you can use directly: (funcall (or (cdr (assoc key +table+)) (lambda (arg) (declare (ignore arg)) (error "No function keyed ~S" key))) arg) Of course, if you know your alist only at run-time you would just use instead: (defun make-function-alist (alist) (mapcar (lambda (entry) (cons (car entry) (compile nil `(lambda (arg) ,@(cdr entry))))) alist)) eg.: (defparameter *table* nil) (setf *table* (make-function-alist (acons 'a (read) (acons 'b (read) *table*)))) (+ arg 3) (/ arg 3) ; read by the above form --> ((A . #<COMPILED-FUNCTION NIL>) (B . #<COMPILED-FUNCTION NIL>)) If you don't mind compiling the functions, you may use: (coerce `(lambda (arg) ,@(cdr entry)) 'function) instead, to get whatever the implementation deems convenient. -- __Pascal Bourguignon__
From: Thomas A. Russ on 5 Apr 2010 12:23
Mirko <mirko.vukovic(a)gmail.com> writes: > I have a utility that may need to do some unit conversions. I would > like to specify the conversion rules as an association list: > > ((a do-something) (b do-something-else) ... etc) > > Now, I could specify `do-something' as > #'(lambda (arg) > (* arg 5)) > > and so on. All the lambda's have the same format #'(lambda (arg) > (...)), only the ellipsis changing from item to item > > Specifying the lambdas works, but makes the a-list hard to read. > Instead I would like it to contain the bodies of the lambda's. > > ((a (* arg 5)) (b (/ arg 12)) ... etc) > > So, the question is how to compile those bodies (I am not sure > `compile' is the right term - I just want to get an `executable' > lambda out of these). > > Right now, I am compiling via macros: > > (defmacro build (sym) > `(lambda (arg) > ,(second (assoc sym +table+))) I don't think you need a lambda expression here. I would use a function instead: (defun build (sym) `(lambda (arg) ,(second (assoc sym +table+)))) That way you don't have to specify the symbol argument as a literal constant in your code. You can evaluate it, pass it in using a parameter to a function that needs it, etc. > and then > > (funcall (build a) arg) (funcall (build 'a) arg) > will execute my function. If you want to compile this, you can get a compiled varsion like this: (compile nil (build 'a)) > But I am wondering if I am misusing the macros here, and if there is > something more direct Well, what I would do is use the simple format as the input and storage format in your code. Then for actual use, I would first do an initialization step where you just go through and replace the expressions with their compiled functions. I would also use a true association list like ((a . (* arg 5)) (b . (/ arg 12)) ... etc) So, for example: (defun initialize () (loop for item in *table* do (setf (cdr item) (compile nil (build (cdr item)))))) And then to use it: (defun convert (key arg) (funcall (cdr (assoc key *table*)) arg)) So this doesn't require any macros at all. If you wanted to use a macro in all of this, I would use it to build the conversion table instead. That way you can do away with the need to explicitly call an initialization function in the first place: (defvar *table* nil) (defmacro conversion-table (&rest key-body-pairs) `(setf *table* ,(loop for (key . body) in key-body-pairs collect (cons key (compile nil `(lambda (arg) ,body)))))) And then you would have (conversion-table (a . (* arg 5)) (b . (/ arg 12)) ...) and you would just use the CONVERT function from above. Note: If you don't like the dot-notation ALISTS, you can always not use the dot and substitute SECOND for CDR and LIST for CONS in the above treatement. The only thing using the CONS/dot notation does is save one cons cell per entry. -- Thomas A. Russ, USC/Information Sciences Institute |