Hi,
I am just trying to solve Ex 3.5 in Graham's ANSI Common Lisp book. I am
reading it on my own and not as part of a university course. The task is:
define a function pos+, that takes a list as param and returns a list
that adds the position of each element of the list to the element's value.
Thus:
(pos+ '(7 5 1 4))
returns:
(7 6 3 7)
This is easy to solve using recursion, but the function shall also be
defined using iteration and using mapcar.
My solutions are as follows, but they horribly and senselessly use
side-effects and thus look very inelegant. Is there a better way to do
it? (I mean, using just very primitive means - this exercise is in the
very beginning of the book...)
Thanks!
Chris
mapcar:
(defun pos+1 (lst)
(setf i 0)
(mapcar #'(lambda (x) (let ((new (+ x i)))
(progn (setf i (+ i 1))
new)))
lst))
iteration:
(defun pos+ (lst)
(setf acc NIL)
(setf i 0)
(dolist (obj lst)
; i know, instead of append, i could do a cons and reverse afterwards...
(progn (setf acc (append acc (list (+ obj i))))
(setf i (+ i 1))))
acc)
In article <············@online.de>, Christian Hofer wrote:
> Hi,
>
> I am just trying to solve Ex 3.5 in Graham's ANSI Common Lisp book. I am
> reading it on my own and not as part of a university course. The task is:
>
> define a function pos+, that takes a list as param and returns a list
> that adds the position of each element of the list to the element's value.
> Thus:
> (pos+ '(7 5 1 4))
> returns:
> (7 6 3 7)
>
> This is easy to solve using recursion, but the function shall also be
> defined using iteration and using mapcar.
>
> My solutions are as follows, but they horribly and senselessly use
> side-effects and thus look very inelegant. Is there a better way to do
> it? (I mean, using just very primitive means - this exercise is in the
> very beginning of the book...)
>
> Thanks!
>
> Chris
>
> mapcar:
>
> (defun pos+1 (lst)
> (setf i 0)
> (mapcar #'(lambda (x) (let ((new (+ x i)))
> (progn (setf i (+ i 1))
> new)))
> lst))
It might be better to introduce i with let, and increment it with INCF.
(defun mapcar-pos+ (list)
(let ((i -1))
(mapcar #'(lambda (elt) (+ elt (incf i)))
list)))
> iteration:
>
> (defun pos+ (lst)
> (setf acc NIL)
> (setf i 0)
> (dolist (obj lst)
> ; i know, instead of append, i could do a cons and reverse afterwards...
> (progn (setf acc (append acc (list (+ obj i))))
> (setf i (+ i 1))))
> acc)
I'd prefer LOOP here:
(defun loop-pos+ (list)
(loop for i from 0
for elt in list
collect (+ elt i)))
...but it's also easy with DO:
(defun do-pos+ (orig-list)
(do ((i 0 (1+ i))
(list orig-list (cdr list))
(new-list nil (cons (+ (car list) i) new-list)))
((endp list) (nreverse new-list))))
I learned Common Lisp from "ANSI Common Lisp", but it hasn't aged well
in my memory, and I never use it for reference. I found these comments
on "ANSI Common Lisp" to be very helpful:
http://www.cs.northwestern.edu/academics/courses/325/readings/graham/graham-notes.html
PAIP is also very good for learning. It also is useful as a reference
(the chapter on optimization is excellent) and it doesn't seem to
suffer from as many quirks.
Zach
Thank you very much for your answers, they were giving me some important
insights.
Zachary Beane wrote:
> It might be better to introduce i with let, and increment it with INCF.
*snip*
Ok, I misused setf, that is true. I was not sure, if I could manipulate
variables that I had defined with let.
I generally prefer to start loops from 0 with setting a variable to 0
and not to -1, that is why my solution was a bit more complicated in
this respect. (Actually, I prefer the Smalltalk way to start counting
with 1 instead of 0, like I have learned it from my earliest childhood.)
> ...but it's also easy with DO:
*snip*
Now I better understand the power of "do"!
> http://www.cs.northwestern.edu/academics/courses/325/readings/graham/graham-notes.html
Interesting link, thanks.
Chris
Christian Hofer <·······@gmx.de> writes:
> Hi,
>
> I am just trying to solve Ex 3.5 in Graham's ANSI Common Lisp book. I
> am reading it on my own and not as part of a university course. The
> task is:
>
> define a function pos+, that takes a list as param and returns a list
> that adds the position of each element of the list to the element's
> value.
> Thus:
> (pos+ '(7 5 1 4))
> returns:
> (7 6 3 7)
>
> This is easy to solve using recursion, but the function shall also be
> defined using iteration and using mapcar.
>
> My solutions are as follows, but they horribly and senselessly use
> side-effects and thus look very inelegant. Is there a better way to do
> it? (I mean, using just very primitive means - this exercise is in the
> very beginning of the book...)
>
> Thanks!
>
> Chris
>
> mapcar:
>
> (defun pos+1 (lst)
> (setf i 0)
> (mapcar #'(lambda (x) (let ((new (+ x i)))
> (progn (setf i (+ i 1))
> new)))
> lst))
>
You're indeed right that some of your side effects are unneeded.
(defun pos+1 (lst &aux (pos -1))
(mapcar #'(lambda (val) (+ val (incf pos))) lst))
Most people don't like using &aux; I think for a short function like
this it's OK.
> iteration:
>
> (defun pos+ (lst)
> (setf acc NIL)
> (setf i 0)
> (dolist (obj lst)
> ; i know, instead of append, i could do a cons and reverse afterwards...
> (progn (setf acc (append acc (list (+ obj i))))
> (setf i (+ i 1))))
> acc)
(defun pos+ (lst)
(do* ((pos 0 (1+ pos))
(lst-cdr lst (cdr lst-cdr))
(lst-car #1=(car lst-cdr) #1#)
result)
((null lst-cdr) (nreverse result))
(push (+ pos lst-car) result)))
my preferred way would use LOOP:
(defun pos+ (lst)
(loop for val in lst
for pos upfrom 0
collect (+ val pos)))
Hey, I am really enthusiatic about how much help I get on this list for
my stupid newbie questions. Thank you all very much! I think I can learn
Lisp really fast that way.
Jock Cooper wrote:
> You're indeed right that some of your side effects are unneeded.
>
> (defun pos+1 (lst &aux (pos -1))
> (mapcar #'(lambda (val) (+ val (incf pos))) lst))
>
> Most people don't like using &aux; I think for a short function like
> this it's OK.
Here and in the following you use advanced concepts that I have not
learned yet. But it is really interesting to see the different ways that
it can be done and I will come back to your code, when I understand its
syntax.
Chris
"Christian Hofer" <·······@gmx.de> wrote in message
·················@online.de...
> My solutions are as follows, but they horribly and senselessly use
> side-effects and thus look very inelegant. Is there a better way to do
> it? (I mean, using just very primitive means - this exercise is in the
> very beginning of the book...)
Well, side effects do not necessarily mean bad code. And if a function
does not do things like modifying variables that it doesn't own, then
you can consider that it doesn't have (important) side effects...
> mapcar:
>
> (defun pos+1 (lst)
> (setf i 0)
> (mapcar #'(lambda (x) (let ((new (+ x i)))
> (progn (setf i (+ i 1))
> new)))
> lst))
Just a few remarks:
- do not use SETF on a variable that doesn't exist yet.
- use INCF to increment a value.
- it's more meaningful to use LIST than LST as a variable name.
(defun pos+ (list)
(let ((i 0))
(mapcar #'(lambda (x)
(prog1 (+ x i) (incf i)))
list)))
> iteration:
>
> (defun pos+ (lst)
> (setf acc NIL)
> (setf i 0)
> (dolist (obj lst)
> ; i know, instead of append, i could do a cons and reverse afterwards...
> (progn (setf acc (append acc (list (+ obj i))))
> (setf i (+ i 1))))
> acc)
That's right, using CONS and NREVERSE is clearer (it's a standard idiom
for accumulating a list in a DO like form, with LOOP you would use COLLECT)
and more effective (APPEND has to traverse the whole list on each call).
--
Matthieu Villeneuve
Matthieu Villeneuve:
| - it's more meaningful to use LIST than LST as a variable name.
Lispniks know the name of the pain that tempts programmers to name
a list lst.
--
"Hurry if you still want to see something. Everything is vanishing."
-- Paul C�zanne (1839-1906)
Matthieu Villeneuve wrote:
> Well, side effects do not necessarily mean bad code. And if a function
> does not do things like modifying variables that it doesn't own, then
> you can consider that it doesn't have (important) side effects...
You are probably right. I just find it easier to read the code, if I do
not have to think about when the "let" is evaluated. But for such small
tasks, that does not really matter.
> - it's more meaningful to use LIST than LST as a variable name.
I thought it was clearer to see, what is a variable and what is a
function, when using separate names. But maybe that is just my
beginner's difficulty with interpreting the foreign looking code.
Chris