This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

pos+ function from Paul Graham's "ANSI Common Lisp"

13 posts · 10927 views

Hi, I've tried to solve an exercise 5 from page 57
that is:
"Suppose the function pos+ takes a list and returns a list of each element plus it's position:
> (pos+ '(7 5 1 4))
(7 6 3 7)
Define this function using (a) recursion, (b) iteration, (c) mapcar."

(a) and (b) are straightforward, but (c) I solved following way:
(defun pos+ (l)

  (let ((i -1)) (mapcar #'(lambda (x) (incf i) (+ x i) ) 
           l )))
I don't like this code, can anybody suggest more elegant solution?

Moreover, I don't understand why
   (let ((i -1)) (mapcar #'(lambda (x) (+ x i) (incf i) ) 
           l )))
doesn't work. Please help because I'm stuck with learning.

Re: pos+ function from Paul Graham's "ANSI Common Lisp"

So
 (let ((i -1)) (mapcar #'(lambda (x) (+ x i) (incf i) )
           l )))
should be
 (let ((i 0)) (mapcar #'(lambda (x) (+ x i) (incf i) )
           l )))
Alternatives are:
 (mapcar (let ((i 0)) (lambda (x) (+ x i) (incf i) ))
      l ))
or
(mapcar #'+ list (loop for k in list 
			       for i from 0 collect i))
I don't think either of them are more elegant, and your solution is most likely to be efficient.

Re: pos+ function from Paul Graham's "ANSI Common Lisp"

Hi,
unfortunately none of your solutions works properly except the last one.
I mean the print the list of successive numbers starting from one ending on the length of the list.

The last one I treat as iterative solution.

Re: pos+ function from Paul Graham's "ANSI Common Lisp"

The problem with 'implausibleusername' functions is the order of the forms, since the functions return the last evaluated form the functions will return the value of (incf i), you can either wrap a PROG1 form around these, like:
(let ((i 0)) (mapcar #'(lambda (x) (prog1 (+ x i)(incf i))) l))
or have the forms order switched like:
(let ((i -1)) (mapcar #'(lambda (x) (incf i)(+ x i)) l))
Note that the variable i must have different starting values in each approach because the variable is increased before the sum in the last example while it is increased after the sum in first one.

EDIT:
You can even chain the variable increase with the sum:
(let ((i -1)) (mapcar #'(lambda (x) (+ x (incf i))) l))
CL-USER> (setf *boss* (make-instance 'smart-person))
NIL
CL-USER>

Re: pos+ function from Paul Graham's "ANSI Common Lisp"

Great thanks for your comment. Now I understand better.

Re: pos+ function from Paul Graham's "ANSI Common Lisp"

AlexPaes wrote:The problem with 'implausibleusername' functions is the order of the forms, since the functions return the last evaluated form the functions will return the value of (incf i), you can either wrap a PROG1 form around these, like:
That'll teach me not to use '(1 1 1) as a test case.

Re: pos+ function from Paul Graham's "ANSI Common Lisp"

Ajschylos wrote:
(defun pos+ (l)
  (let ((i -1)) (mapcar #'(lambda (x) (incf i) (+ x i) ) 
           l )))
I don't like this code, can anybody suggest more elegant solution?
If you're going to use MAPCAR, you might as well write a functional solution.
That means no INCF, at least not in the toplevel function. Instead, write another function that returns a list of integers to be added to the elements of your list, and map #'+ onto the two lists.
> (defun range0 (max)
    (loop for x below max
          collect x))
RANGE0
> (range0 10)
(0 1 2 3 4 5 6 7 8 9)
> (defun pos+ (list)
    (mapcar #'+ list (range0 (length list))))
POS+
> (pos+ (list 1 5 2 9 12))
(1 6 4 12 16)
--Dan B.

Re: pos+ function from Paul Graham's "ANSI Common Lisp"

Thank you, that's really piece of beautiful code.
A.

FP

Ajschylos wrote:that's really piece of beautiful code.
That's functional programming :P You can write code like that for lots of applications. It tends to be a little slower and more memory-intensive than imperative code, but once you get used to FP, hard programs can actually be both fun and easy.
--Dan B.

Re: FP

danb wrote:That's functional programming :P You can write code like that for lots of applications. It tends to be a little slower and more memory-intensive than imperative code, but once you get used to FP, hard programs can actually be both fun and easy.
Note that using a package like SERIES you can write the code functionally, and yet have in many cases efficiency equal to iterative solution, because the system will optimize many common cases, in particular using delayed evaluation to avoid having to keep intermediary mapping results in memory. For example in this code equivalent to the above:
(defun pos+ (list)
  (collect (map-fn 'number #'+ (scan list) (scan-range :below (length list)))))
the SCAN-RANGE function doesn't create all numbers at once, but only as they are needed. Not that this really matters for this particular example, but for larger functions it can.

Re: pos+ function from Paul Graham's "ANSI Common Lisp"

Hi 'Ramarren', thanks a lot for your comment.

It's quite new for me that there exist so useful packages in CL.

I am working on some hard problem intersecting fields like Number Theory, Algebra and Combinatorics. I have chosen LISP because of its power of expression (which is still difficult for me to understand and use) and compact, elegant formulas which remind me mathematical formulas. Most important for me is to understand algorithms after some period of time needed for preparing theoretical background which means long months of delays.

The SERIES package seems to be exceptionally valuable for me, could you please reference some other packages that can help in my work?

A.

Re: pos+ function from Paul Graham's "ANSI Common Lisp"

Most Common Lisp packages are gathered in CLiki and perhaps cl-user. Also, the Maxima Computer Algebra System is written in CL and open source, and it might be useful for your described problem domain. Although I read that its code is old, obviously complex, and hence not very readable for someone not well versed in Lisp.