Jump to content
  • Advertisement
Sign in to follow this  
Brobanx

LISP: Some help writing a macro.

This topic is 4769 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've created a macro called 'deffunc' that is supposed to add pre- and post-condition options to 'defun'. It works quite well, but it is flawed because of some potential symbol-name overlapping. (defmacro deffunc (name args code &key (pre t) (post t)) `(defun ,name ,args (if (not ,pre) (format t "Pre-condition on ~S failed!" ,(symbol-name name)) (let ((result ,code)) (if (not ,post) (format t "Post-condition on ~S failed!" ,(symbol-name name)) result)) ))) (deffunc testing (a) (* a 2) :pre (numberp a) :post (eql result (* a 2))) My only issue here is that I use the symbol "result" to equal the result of the function call, the :post predicate can access the value "result". However, I would rather use the keyword ":result" instead, but because keywords are self-evaluating, I can't just replace "result" for ":result" in my macro. I think I would need to somehow take the value of :post, and replace ":result" with another symbol, but I have no idea how to search through a list (:post), and replace symbols inside of it. Any ideas?

Share this post


Link to post
Share on other sites
Advertisement
I've considered the idea of the user writing a lambda function as the post-condition, and sending :result as an argument to that lambda function, but that seems too awkward, I wanna keep this as straight-forward as possible for the end-user of the macro.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Searching through code and replacing bits of it isn't really a lispy thing to do. If I were writing this macro, I'd just let the user specify what the result should be called:

(defmacro deffunc (name args expr &key (pre t) (post t) (result 'result))
`(defun ,name ,args
(unless ,pre
(format *error-output* "~&Pre-condition on ~S failed!~%" ',name))
(let ((,result ,expr))
(unless ,post
(format *error-output* "~&Post-condition on ~S failed!~%" ',name))
,result)))

(deffunc testing (a)
(* a 2)
:pre (numberp a)
:result testing-result
:post (eql testing-result (* a 2)))


Since function names aren't necessarily symbols, calling SYMBOL-NAME on them isn't really the right thing either, so I've eliminated that (and sent the messages to non-*standard-output*; *debug-io* and *trace-output* might also be reasonable choices for the output streams).

This would also require some thinking for use with multiple return values.. for that case, I'd probably make it something like this:

(defmacro deffunc (name args expr &key (pre t) (post t) (result 'result))
(when (symbolp result) (setf result (list result)))
`(defun ,name ,args
(unless ,pre
(format *error-output* "~&Pre-condition on ~S failed!~%" ',name))
(multiple-value-bind ,result ,expr
(unless ,post
(format *error-output* "~&Post-condition on ~S failed!~%" ',name))
(values ,@result))))

(deffunc ensured-gethash (key table)
(gethash key table)
:pre (hash-table-p table)
:result (value was-present)
:post was-present)

(ensured-gethash 5 (make-hash-table))
Post-condition on ENSURED-GETHASH failed!

If RESULT is a symbol, only the primary value is bound (and returned), otherwise all the names in the list given are bound, made available to the postcondition test, and returned.

Share this post


Link to post
Share on other sites
Thanks anon, I liked how you handled that.


Quote:
However, I would rather use the keyword ":result" instead, but because keywords are self-evaluating, I can't just replace "result" for ":result" in my macro.


I really don't understand this. Why would you want to use a keyword to do something that keywords cannot do (be bound to values).

Quote:
I think I would need to somehow take the value of :post, and replace ":result" with another symbol, but I have no idea how to search through a list (:post), and replace symbols inside of it. Any ideas?


The value of :post is a constant: the symbol :post . The value of :result is a constant: the symbol :result

You simply shouldn't use keywords in expressions, they aren't meant to do that. Your initial solution is pretty good, the anon's is even better.

Parsing a list and replacing elements is actually pretty easy:


(defun tree-replace (tree element new-element &key (eq 'eq))
(if (consp tree)
(cons (tree-replace (car tree) element new-element :eq eq)
(tree-replace (cdr tree) element new-element :eq eq))
(if (funcall eq element tree) new-element tree)))


Thereafter you'll probably want to use gensym in the macro to ensure that the symbol used to store the results of evaluating the code and used to replace :result is unique and doesn't conflict with other symbols in the :post condition. (the gensym function creates a unique symbol)

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Parsing a list and replacing elements is actually pretty easy


Yes. Use SUBSTITUTE for sequences and SUBST for trees.

Share this post


Link to post
Share on other sites
Well, thanks for all the help. It turns out I eventually found a way to write a macro which takes an arbitrary expression and replaces ':result' with 'result', but I changed my deffunc macro to just let the user choose the name of his result, like anon. suggested.



(defmacro replace-expression (from to expr)
(defun f (l)
(let (r)
(dolist (x l r)
(if (listp x) (setq r (append r (list (f x))))
(if (eq from x) (setq r (append r (list `,to)))
(setq r (append r (list x))))))))
`(f ',expr))



As an aside, is it possible to have a lambda function be recursive? If so, how?

Also...
The only problem with the above macro is that it defines a function f which is available globally, I want the function to be seen by ONLY the macro, how do I do this?

Sorry I'm a bit a new to lisp and trying my best to understand macros.

[Edited by - Brobanx on July 25, 2005 5:47:44 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Brobanx


As an aside, is it possible to have a lambda function be recursive? If so, how?


No, it isn't.

Quote:
Original post by Brobanx
Also...
The only problem with the above macro is that it defines a function f which is available globally, I want the function to be seen by ONLY the macro, how do I do this?


check out flet and lables. Both are used to produce local functions, flets can be recursive labels can not.

Share this post


Link to post
Share on other sites
Acutally it's the other way around. labels can be recursive, flets cannot. The following code is correct.



(defmacro replace-expression (from to expr)
(labels ((f (l)
(let (r)
(dolist (x l r)
(if (listp x) (setq r (append r (list (f x))))
(if (eq from x) (setq r (append r (list to)))
(setq r (append r (list x)))))))))
(f `',expr)))



Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!