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.

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 on other sites
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 on other sites
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 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 on other sites
By the way, it's spelled Lisp :)

Share on other sites
Quote:
 Parsing a list and replacing elements is actually pretty easy

Yes. Use SUBSTITUTE for sequences and SUBST for trees.

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 on other sites
Quote:
 Original post by BrobanxAs an aside, is it possible to have a lambda function be recursive? If so, how?

No, it isn't.

Quote:
 Original post by BrobanxAlso...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 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 on other sites
right, I should perhaps read what i write.. before I post :)

1. 1
2. 2
3. 3
frob
12
4. 4
5. 5

• 13
• 14
• 65
• 14
• 15
• Forum Statistics

• Total Topics
632130
• Total Posts
3004286

×