tags:

views:

786

answers:

6

I've been attempting to write a Lisp macro that would perfom the equivalent of ++ in other programming languages for semantic reasons. I've attempted to do this in several different ways, but none of them seem to work, and all are accepted by the interpreter, so I don't know if I have the correct syntax or not. My idea of how this would be defined would be

(defmacro ++ (variable)
  (incf variable))

but this gives me a SIMPLE-TYPE-ERROR when trying to use it. What would make it work?

+11  A: 

Remember that a macro returns an expression to be evaluated. In order to do this, you have to backquote:

(defmacro ++ (variable)
   `(incf ,variable))
Kyle Cronin
A: 

This should do the trick, however I'm not a lisp guru.

(defmacro ++ (variable)
  `(setq ,variable (+ ,variable 1)))
Drew Olson
This won't quite work as expected in all situations. As you put it 'variable' gets evaluated twice which is not what the user expect if the expression has side effects.E.g. look how your macro expands this quite reasonable invocation:(++ (aref some-vector (++ some-index)))
HD
+5  A: 

Both of the previous answers work, but they give you a macro that you call as

(++ varname)

instead of varname++ or ++varname, which I suspect you want. I don't know if you can actually get the former, but for the latter, you can do a read macro. Since it's two characters, a dispatch macro is probably best. Untested, since I don't have a handy running lisp, but something like:

(defun plusplus-reader (stream subchar arg)
   (declare (ignore subchar arg))
   (list 'incf (read stream t nil t)))
(set-dispatch-macro-character #\+ #\+ #'plusplus-reader)

should make ++var actually read as (incf var).

+3  A: 

For pre-increment, there's already incf, but you can define your own with

(define-modify-macro my-incf () 1+)

For post-increment, you could use this (from fare-utils):

(defmacro define-values-post-modify-macro (name val-vars lambda-list function)
 "Multiple-values variant on define-modify macro, to yield pre-modification values"
 (let ((env (gensym "ENV")))
   `(defmacro ,name (,@val-vars ,@lambda-list &environment ,env)
      (multiple-value-bind (vars vals store-vars writer-form reader-form)
          (get-setf-expansion `(values ,,@val-vars) ,env)
       (let ((val-temps (mapcar #'(lambda (temp) (gensym (symbol-name temp)))
                                 ',val-vars)))
          `(let* (,@(mapcar #'list vars vals)
                  ,@store-vars)
             (multiple-value-bind ,val-temps ,reader-form
               (multiple-value-setq ,store-vars
                 (,',function ,@val-temps ,,@lambda-list))
               ,writer-form
               (values ,@val-temps))))))))

(defmacro define-post-modify-macro (name lambda-list function)
 "Variant on define-modify-macro, to yield pre-modification values"
 `(define-values-post-modify-macro ,name (,(gensym)) ,lambda-list ,function))

(define-post-modify-macro post-incf () 1+)
+2  A: 

Semantically, the prefix operators ++ and -- in a language like c++ or whatever are equivalent incf/decf in common lisp. If you realize this and, like your (incorrect) macro, are actually looking for a syntactic change then you've already been shown how to do it with backticks like `(incf ,x). You've even been shown how to make the reader hack around this to get something closer to non-lisp syntax. That's the rub though, as neither of these things is a good idea. In general, non idiomatic coding to make a language resemble another more closely just doesn't turn out to be such a good idea.

However, if are actually looking for the semantics, you've already got the prefix versions as noted but the postfix versions aren't going to be easy to match syntactically. You could do it with enough reader hackery, but it wouldn't be pretty.

If that's what you're looking for, I'd suggest a) stick with incf/decf names since they are idiomatic and work well and b) write post-incf, post-decf versions, e.g (defmacro post-incf (x) `(prog1 ,x (incf ,x)) kinds of things.

Personally, I don't see how this would be particularly useful but ymmv.

simon
+2  A: 

I would strongly advise against making an alias for incf. It would reduce readability for anyone else reading your code who have to ask themselves "what is this? how is it different from incf?"

If you want a simple post-increment, try this:

(defmacro post-inc (number &optional (delta 1))
  "Returns the current value of number, and afterwards increases it by delta (default 1)."
  (let ((value (gensym)))
    `(let ((,value ,number))
       (incf ,number ,delta)
       ,value)))
Svante