Note: this is aimed at exploring some of the ways to make this happen. You noted in your question some of these solutions (e.g., starting at -1), and I don't mean by including them here to suggest that you hadn't considered them. This answer is more about considering the role of a pre- and post-increment operators in value-oriented languages.
Note that (incf c)
not only performs a side effect (where order would matter), but also returns the new value of c
. That is, incf
is a pre-increment operator. That means that it's absolutely acceptable to write (+ x (incf c))
, as in:
(defun foo (lst)
(let ((c 0))
(mapcar #'(lambda (x)
(+ x (incf c)))
lst)))
(foo '(0 0 0))
;=> (1 2 3)
Now, those results aren't terrible, but they don't add the indices of the elements to the elements, because indices start at zero, not one. You could do a simple change and add the subtraction:
(defun foo (lst)
(let ((c 0))
(mapcar #'(lambda (x)
(+ x (1- (incf c)))) ; or (+ x -1 (incf c)), etc.
lst)))
(foo '(0 0 0))
;=> (0 1 2)
Even better than this, in my opinion, would be to use the first form, and just start c
at -1
. It's what I'd probably do, anyhow, and you noted in your question that this is an option:
(defun foo (lst)
(let ((c -1))
(mapcar #'(lambda (x)
(+ x (incf c)))
lst)))
(foo '(0 0 0))
;=> (0 1 2)
We might ask, though, whether there's a way to approximate a post-increment operator so that we didn't have to see (in our code) the subtraction? That is, we might view the above as what we have in some languages:
result.add( x + ++c )
and ask why we can't just do
result.add( x + c++ )
instead? This is simple enough:
(defmacro post-incf (place)
`(1- (incf ,place)))
(defun foo (lst)
(let ((c 0))
(mapcar #'(lambda (x)
(+ x (post-incf c)))
lst)))
(foo '(0 0 0))
;=> (0 1 2)
Actually, there's no flexibility in C-like language's pre- and post-increment operators, but incf
and decf
in Common Lisp afford a little bit more through the use of optional arguments, wherein a second, optional argument, can be used to specify by how much the value should change. It's not too hard to adjust post-incf
to handle such a second argument. A first attempt might be:
(defmacro post-incf (place &optional (delta-form 1))
`(- (incf ,place ,delta-form) ,delta-form))
That will work just fine for constant values such as 1
, but it will evaluate the delta-form
multiple times, and we don't want that (since it could have side effects, or be expensive to compute). We just need to be a little more careful to evaluate it only once, and thus:
(defmacro post-incf (place &optional (delta-form 1))
(let ((delta (gensym (symbol-name '#:delta-))))
`(let ((,delta ,delta-form))
(- (incf ,place ,delta) ,delta))))
(let ((a 0))
(list a ; => 0
(incf a 3) ; => 3
(post-incf a 2) ; => 3 (but x is now 5)
a)) ; => 5
;=> (0 3 3 5)