From: =?ISO-8859-15?Q?Andr=E9_Thieme?=
Subject: problem setfing with nthcdr
Date: 
Message-ID: <d4k7k4$e1n$1@ulric.tng.de>
While this works:

CL-USER> (defparameter foo (list 'a 'b 'c 'd 'e 'f 'g))
FOO
CL-USER> foo
(A B C D E F G)
CL-USER> (setf (cddr foo) (cdddr foo))
(D E F G)
CL-USER> foo
(A B D E F G)  ; C is now removed from the list


this doesn't:

CL-USER> (defparameter foo (list 'a 'b 'c 'd 'e 'f 'g))
FOO
CL-USER> foo
(A B C D E F G)
CL-USER> (setf (nthcdr 2 foo) (nthcdr 3 foo))
; Evaluation aborted
EVAL: undefined function (SETF NTHCDR)
[Condition of type SYSTEM::SIMPLE-UNDEFINED-FUNCTION]

Why?


Andr�
--

From: Trent Buck
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <87k6mqmfbc.fsf@rocinante.twb.ath.cx>
Andr� Thieme <······························@justmail.de> writes:

> While this works:
> CL-USER> (setf (cddr foo) (cdddr foo))
> this doesn't:
> CL-USER> (setf (nthcdr 2 foo) (nthcdr 3 foo))
> ; Evaluation aborted
> EVAL: undefined function (SETF NTHCDR)
> [Condition of type SYSTEM::SIMPLE-UNDEFINED-FUNCTION]

SETF has not been trained to deal with NTHCDRs.  See DEFSETF in the hyperspec.
From: Kalle Olavi Niemitalo
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <87vf6a1346.fsf@Astalo.kon.iki.fi>
Trent Buck <·········@gmail.com> writes:

> SETF has not been trained to deal with NTHCDRs.
> See DEFSETF in the hyperspec.

Actually, to handle (incf (nthcdr 0 foo)), you'd need
DEFINE-SETF-EXPANDER rather than DEFSETF.  Implementations
have been posted at least twice in this newsgroup:

1998-10-21 <····················@engc.bu.edu>
2004-10-30 <···················@Astalo.kon.iki.fi> 

To satisfy point 13 of CLHS section 11.1.2.1.2 (assuming CLiki
issue DEFINE-SETF-METHOD:FIX), you'd also have to shadow NTHCDR.
From: Kalle Olavi Niemitalo
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <87pswh1n3y.fsf@Astalo.kon.iki.fi>
Kalle Olavi Niemitalo <···@iki.fi> writes:

> Actually, to handle (incf (nthcdr 0 foo)), you'd need
> DEFINE-SETF-EXPANDER rather than DEFSETF.

On second thought, (pop (nthcdr 0 foo)) is a better example.
From: Kenny Tilton
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <ZShbe.16622$mp6.2126061@twister.nyc.rr.com>
Andr� Thieme wrote:
> While this works:
> 
> CL-USER> (defparameter foo (list 'a 'b 'c 'd 'e 'f 'g))
> FOO
> CL-USER> foo
> (A B C D E F G)
> CL-USER> (setf (cddr foo) (cdddr foo))
> (D E F G)
> CL-USER> foo
> (A B D E F G)  ; C is now removed from the list
> 
> 
> this doesn't:
> 
> CL-USER> (defparameter foo (list 'a 'b 'c 'd 'e 'f 'g))
> FOO
> CL-USER> foo
> (A B C D E F G)
> CL-USER> (setf (nthcdr 2 foo) (nthcdr 3 foo))
> ; Evaluation aborted
> EVAL: undefined function (SETF NTHCDR)
> [Condition of type SYSTEM::SIMPLE-UNDEFINED-FUNCTION]
> 
> Why?

Others have answered this, but a larger point is that one of the really 
nice things about Lisp is that there is a very tight correlation between 
the runtime error message and the actual problem. Sounds obvious, but it 
  sure was not what I was used to coming from other languages.

btw, if you look in the hyperspec for NTHCDR you see it described as 
"function". If you could (SETF (NTHCDR ..etc it would be described as 
"accessor".

kt

-- 
Cells? Cello? Cells-Gtk?: http://www.common-lisp.net/project/cells/
Why Lisp? http://lisp.tech.coop/RtL%20Highlight%20Film

"Doctor, I wrestled with reality for forty years, and I am happy to 
state that I finally won out over it." -- Elwood P. Dowd
From: Pascal Bourguignon
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <87wtqqqlqd.fsf@thalassa.informatimago.com>
Andr� Thieme <······························@justmail.de> writes:
> CL-USER> (setf (nthcdr 2 foo) (nthcdr 3 foo))
> ; Evaluation aborted
> EVAL: undefined function (SETF NTHCDR)
> [Condition of type SYSTEM::SIMPLE-UNDEFINED-FUNCTION]
>
> Why?

Because NTHCDR is a function, not an accessor:

   Function NTHCDR

   Accessor CAR, CDR, CAAR, CADR, CDAR, CDDR, CAAAR, CAADR, CADAR, CADDR,
   CDAAR, CDADR, CDDAR, CDDDR, CAAAAR, CAAADR, CAADAR, CAADDR, CADAAR,
   CADADR, CADDAR, CADDDR, CDAAAR, CDAADR, CDADAR, CDADDR, CDDAAR,
   CDDADR, CDDDAR, CDDDDR

You could still write a DEFSETF for nthcdr, 
involving the (cdr (nthcdr (1- n))).


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Kitty like plastic.
Confuses for litter box.
Don't leave tarp around.
From: =?ISO-8859-15?Q?Andr=E9_Thieme?=
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <d4mfkg$a6l$1@ulric.tng.de>
Pascal Bourguignon schrieb:
> Andr� Thieme <······························@justmail.de> writes:
> 
>>CL-USER> (setf (nthcdr 2 foo) (nthcdr 3 foo))
>>; Evaluation aborted
>>EVAL: undefined function (SETF NTHCDR)
>>[Condition of type SYSTEM::SIMPLE-UNDEFINED-FUNCTION]
>>
>>Why?
> 
> 
> Because NTHCDR is a function, not an accessor:
> 
>    Function NTHCDR
> 
>    Accessor CAR, CDR, CAAR, CADR, CDAR, CDDR, CAAAR, CAADR, CADAR, CADDR,
>    CDAAR, CDADR, CDDAR, CDDDR, CAAAAR, CAAADR, CAADAR, CAADDR, CADAAR,
>    CADADR, CADDAR, CADDDR, CDAAAR, CDAADR, CDADAR, CDADDR, CDDAAR,
>    CDDADR, CDDDAR, CDDDDR
> 
> You could still write a DEFSETF for nthcdr, 
> involving the (cdr (nthcdr (1- n))).
> 
> 

If I wanted to write a delete-nth-element function, how would that look 
like? A friend of mine presented me today his version. It was a 4-liner 
with about ten conses and it ended with an eval. I am certain there is a 
better way to do that. He wanted:

(delete-nth-element 3 '(a b c d e f g)) => (A B C E F G)


Andr�
--
From: Matthias Buelow
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <3d834sF6lva5iU1@news.dfncis.de>
Andr� Thieme <······························@justmail.de> wrote:

>If I wanted to write a delete-nth-element function, how would that look 
>like? A friend of mine presented me today his version. It was a 4-liner 

Wow, you have friends that know Lisp?  ;-)

>with about ten conses and it ended with an eval. I am certain there is a 
>better way to do that. He wanted:
>
>(delete-nth-element 3 '(a b c d e f g)) => (A B C E F G)

(hmm, 'd is the 4th element, not the third :)

but apart from that:

    (defun delete-nth-element (n l)
      (remove-if #'(lambda (x) t) l :start (1- n) :end n))

or delete-if, for destructive update.

It's always a nice exercise to write such small functions yourself,
in a recursive way:

    (defun delete-nth-element (n l)
      (labels ((next (n l res)
		     (if (null l)
			 (reverse res)
			 (next (1- n) (cdr l)
			       (if (= n 0)
				   res
				   (cons (car l) res))))))
	(next (1- n) l '())))

A good compiler will flatten this into a loop.

mkb.
From: André Thieme
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <d4mlp0$fkh$1@ulric.tng.de>
Matthias Buelow schrieb:
> Andr� Thieme <······························@justmail.de> wrote:
> 
> 
>>If I wanted to write a delete-nth-element function, how would that look 
>>like? A friend of mine presented me today his version. It was a 4-liner 
> 
> 
> Wow, you have friends that know Lisp?  ;-)

Yes.. that's because I introduced it to them. In fact I am teaching CL 
to three of my friends.



>>with about ten conses and it ended with an eval. I am certain there is a 
>>better way to do that. He wanted:
>>
>>(delete-nth-element 3 '(a b c d e f g)) => (A B C E F G)
> 
> 
> (hmm, 'd is the 4th element, not the third :)

We let the index begin with 0, like: (nth 0 '(1 2 3)) => 1



>     (defun delete-nth-element (n l)
>       (remove-if #'(lambda (x) t) l :start (1- n) :end n))
> 
> or delete-if, for destructive update.

Thanks for this suggestion.
About the destructive updates: I read one should work with destructive 
functions as if they were not desctructive, because they are not forced 
to be always working destructive. It looks to me a bit strange that 
delete only /sometimes/ destructively deletes elements of a list.



> It's always a nice exercise to write such small functions yourself,

I could have written the function myself. But when I saw his four-liner 
I knew there had to be a better solution. At the same time I knew that I 
could not show him this better solution, so I asked here. In fact I 
forgot about the keyword parameters of remove.


Andr�
--
From: Matthias Buelow
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <3d8589F6lva5iU3@news.dfncis.de>
Andr� Thieme <······························@justmail.de> wrote:

>We let the index begin with 0, like: (nth 0 '(1 2 3)) => 1

Indeed.. my CL is a bit rusty, having done mostly Scheme lately.
Funny, then there should be `zeroth' or `zeroeth', and not `first'.
Ah, well.

mkb.
From: Pascal Bourguignon
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <8764y8onek.fsf@thalassa.informatimago.com>
Andr� Thieme <······························@justmail.de> writes:
> Matthias Buelow schrieb:
>>     (defun delete-nth-element (n l)
>>       (remove-if #'(lambda (x) t) l :start (1- n) :end n))
>> or delete-if, for destructive update.

I would avoid to mix delete and remove.

     (defun delete-nth-element (n l)
           (delete-if (lambda (x) t) l :start (1- n) :end n))

     (defun remove-nth-element (n l)
           (remove-if (lambda (x) t) l :start (1- n) :end n))

It's hard enough to remember which one is destructive...


> Thanks for this suggestion.
> About the destructive updates: I read one should work with destructive
> functions as if they were not desctructive, because they are not
> forced to be always working destructive. 

I would rather treat destructive functions as if they were
destructive, and non destructive function as if they were not
destructive.


> It looks to me a bit strange
> that delete only /sometimes/ destructively deletes elements of a list.

That's to allow implementing the destructive version using the non
destructive version:

(clhs remove ; ok remove is functional and delete is destructive, so:)

     (defun remove-nth-element (n l)
           (remove-if (lambda (x) t) l :start (1- n) :end n))

     (defun some-other-function (args...)
       ;; 40 lines of hairy functional code
       )

     (defun nsome-other-function (args...) 
            (some-other-function args...)
       ;; we'll implement other hairy 40 lines for the destructive case later.
       )


Moreover, sometime there's nothing to destroy. For example:

          (delete 1 '(a b c)) --> (a b c)


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

The world will now reboot.  don't bother saving your artefacts.
From: Denis Bueno
Subject: Re: tail call optimization
Date: 
Message-ID: <v8tis27hvkm.fsf@mornasule.stl.gtri.gatech.edu>
Bruno Haible <·····@clisp.org> writes:

>> It's always a nice exercise to write such small functions yourself,
>> in a recursive way:
>>
>>    (defun delete-nth-element (n l)
>>      (labels ((next (n l res)
>>		     (if (null l)
>>			 (reverse res)
>>			 (next (1- n) (cdr l)
>>			       (if (= n 0)
>>				   res
>>				   (cons (car l) res))))))
>>	(next (1- n) l '())))

>
> The conclusion is: Code that assumes tail recursion elimination
> makes it _impossible_ to _debug_ real big testcases.

But isn't that the beauty of recursion? Typical recursive functions are
defined in terms of a few base cases and a tail-recursive call. Thus the
size of the input doesn't matter, so long as the tests cover the base
case(s) and the recursive call.

After you've run those test cases, you can issue a (declaim (optimize
(debug 0))) and use your code on real data.

-Denis
(replace ·······@gmail.com" "dbueno")
From: Bruno Haible
Subject: Re: tail call optimization
Date: 
Message-ID: <d55tvd$7lt$1@laposte.ilog.fr>
Denis Bueno wrote:

>> The conclusion is: Code that assumes tail recursion elimination
>> makes it _impossible_ to _debug_ real big testcases.
>
> But isn't that the beauty of recursion? Typical recursive functions are
> defined in terms of a few base cases and a tail-recursive call. Thus the
> size of the input doesn't matter, so long as the tests cover the base
> case(s) and the recursive call.

Testing is not that easy: In real-life code, you can easily have 100%
source code coverage by the testsuite, and still have bugs in the code.

> After you've run those test cases, you can issue a (declaim (optimize
> (debug 0))) and use your code on real data.

Honestly, as a developer, do you find that acceptable to compile your
program twice before running tests: once with debugging, for the
smaller tests, and once without debugging, for the larger ones?
I guess that most developers will, in this situation, just do the
smaller tests. And thus fall into the pitfall of delivering code that
was never tested on real input data. As the Ariane 5 crash showed [1],
such mistakes can cost hundreds of millions of dollars.

               Bruno


[1] http://www.siam.org/siamnews/general/ariane.htm
From: Bruno Haible
Subject: Re: tail call optimization
Date: 
Message-ID: <d55t61$70c$1@laposte.ilog.fr>
Ulrich Hobelmann wrote:

>> Write it iteratively instead, and debuggability and testability are
>> reconciled.
>
> The trouble is, not everything can nicely be written with loops. 
> It might end up really awkward and you have to use trampolines and 
> stuff, just like in Java or C.

In many cases where you can't write a loop directly, data structures known
as "queue" (first-in-first-out list) and "stack" (last-in-first-out list)
will help. Also, you can hide the awkward stuff inside macros.

                Bruno
From: Pascal Bourguignon
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <87is29nmcs.fsf@thalassa.informatimago.com>
Andr� Thieme <······························@justmail.de> writes:
> If I wanted to write a delete-nth-element function, how would that
> look like? A friend of mine presented me today his version. It was a
> 4-liner with about ten conses and it ended with an eval. I am certain
> there is a better way to do that. He wanted:
>
> (delete-nth-element 3 '(a b c d e f g)) => (A B C E F G)

Define "better"?

(defun delete-nth-element (n list)
    (delete-if (constantly t) list :start n :end (1+ n)))

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Small brave carnivores
Kill pine cones and mosquitoes
Fear vacuum cleaner
From: =?ISO-8859-15?Q?Andr=E9_Thieme?=
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <d4ml0v$f35$1@ulric.tng.de>
Pascal Bourguignon schrieb:
> Andr� Thieme <······························@justmail.de> writes:
> 
>>If I wanted to write a delete-nth-element function, how would that
>>look like? A friend of mine presented me today his version. It was a
>>4-liner with about ten conses and it ended with an eval. I am certain
>>there is a better way to do that. He wanted:
>>
>>(delete-nth-element 3 '(a b c d e f g)) => (A B C E F G)
> 
> 
> Define "better"?

I can show you his solution tomorrow. You will probably not understand 
it (as long you are not willing to invest some time into it). Your 
solution is definately better ;-)


> 
> (defun delete-nth-element (n list)
>     (delete-if (constantly t) list :start n :end (1+ n)))

Really nice, I did not know about constantly and I also forgot about the 
keyword parameters of the remove/delete functions.

Would this solution also be okay?:
(defun delete-nth-element (n list)
   (delete (nth n list) list :start n :end (1+ n)))

Or do you see some disadvantages over delete-if?


Andr�
--
From: Pascal Bourguignon
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <87acnkoo9d.fsf@thalassa.informatimago.com>
Andr� Thieme <······························@justmail.de> writes:
> Pascal Bourguignon schrieb:
>> (defun delete-nth-element (n list)
>>     (delete-if (constantly t) list :start n :end (1+ n)))
>
> Really nice, I did not know about constantly and I also forgot about
> the keyword parameters of the remove/delete functions.
>
> Would this solution also be okay?:
> (defun delete-nth-element (n list)
>    (delete (nth n list) list :start n :end (1+ n)))
>
> Or do you see some disadvantages over delete-if?

It's good enough, it's still O(n), but the  list is walked twice, so
the constant factor is greater, and when N is big, it'll (detectably)
be slower.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

The world will now reboot.  don't bother saving your artefacts.
From: =?ISO-8859-15?Q?Andr=E9_Thieme?=
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <d4p5ss$l58$1@ulric.tng.de>
Andr� Thieme schrieb:

> I can show you his solution tomorrow. You will probably not understand 
> it (as long you are not willing to invest some time into it).

Okay, here is the delete-nth-element of the friend who just started 
learning Lisp:

(defun delete-nth-element (eintrag liste)
   (dotimes (i (1- eintrag))
     (setf liste (cons 'cdr (cons liste nil))))
   (setf liste (cons 'setf (cons liste (cons (cons 'cdr (cons liste 
nil)) nil))))
   (eval liste))

I must say that he just started with Lisp and I think his solution is 
not bad for that. He did not write more than 20 functions yet, so, this 
is okay while obviously not lispish.


Andr�
--
From: Pascal Bourguignon
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <87ll73lohu.fsf@thalassa.informatimago.com>
Andr� Thieme <······························@justmail.de> writes:

> Andr� Thieme schrieb:
>
>> I can show you his solution tomorrow. You will probably not
>> understand it (as long you are not willing to invest some time into
>> it).
>
> Okay, here is the delete-nth-element of the friend who just started
> learning Lisp:
>
> (defun delete-nth-element (eintrag liste)
>    (dotimes (i (1- eintrag))
>      (setf liste (cons 'cdr (cons liste nil))))
>    (setf liste (cons 'setf (cons liste (cons (cons 'cdr (cons liste
> nil)) nil))))
>    (eval liste))
>
> I must say that he just started with Lisp and I think his solution is
> not bad for that. He did not write more than 20 functions yet, so,
> this is okay while obviously not lispish.

This is silly.  Try this:


(defun make-maker (expression)
   (if (consp expression)
     (list (quote cons)
           (list (quote quote) (car expression))
           (make-maker (cdr expression)))
      expression))

(defparameter expr '(defun delete-nth-element (eintrag liste)
    (dotimes (i (1- eintrag))
      (setf liste (cons 'cdr (cons liste nil))))
    (setf liste (cons 'setf (cons liste (cons (cons 'cdr (cons liste nil)) nil))))
    (eval liste)))

(dotimes (i 10 expr) (setf expr (make-maker expr)))

(let ((value (eval expr)))
  (dotimes (i 10 value) (setf value (eval value))))



Or:

(defun sum (a b)
    (if (zerop b)
      (list a)
      (cons 1 (sum a (1- b)))))

(eval (print `(let ((a 42)) (+ ,@(sum 'a 10)))))

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Nobody can fix the economy.  Nobody can be trusted with their finger
on the button.  Nobody's perfect.  VOTE FOR NOBODY.
From: Matthias Buelow
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <3daptcF6tcrhiU1@news.dfncis.de>
Andr� Thieme wrote:

> (defun delete-nth-element (eintrag liste)
>   (dotimes (i (1- eintrag))
>     (setf liste (cons 'cdr (cons liste nil))))
>   (setf liste (cons 'setf (cons liste (cons (cons 'cdr (cons liste nil))
> nil))))
>   (eval liste))

It's nice to see that your friend has understood the correlation between
code and data in Lisp... ;-)

mkb.
From: Adam Warner
Subject: Re: problem setfing with nthcdr
Date: 
Message-ID: <pan.2005.04.26.02.41.38.465299@consulting.net.nz>
On Tue, 26 Apr 2005 04:10:41 +0200, André Thieme wrote:
> While this works:
> 
> CL-USER> (defparameter foo (list 'a 'b 'c 'd 'e 'f 'g))
> FOO
> CL-USER> foo
> (A B C D E F G)
> CL-USER> (setf (cddr foo) (cdddr foo))
> (D E F G)
> CL-USER> foo
> (A B D E F G)  ; C is now removed from the list
> 
> 
> this doesn't:
> 
> CL-USER> (defparameter foo (list 'a 'b 'c 'd 'e 'f 'g))
> FOO
> CL-USER> foo
> (A B C D E F G)
> CL-USER> (setf (nthcdr 2 foo) (nthcdr 3 foo))
> ; Evaluation aborted
> EVAL: undefined function (SETF NTHCDR)
> [Condition of type SYSTEM::SIMPLE-UNDEFINED-FUNCTION]
> 
> Why?

CDDR is an accessor. NTHCDR isn't (i.e. the SETF behaviour isn't specified
by ANSI Common Lisp so you are invoking undefined behaviour).

<http://www.lispworks.com/documentation/HyperSpec/Body/f_car_c.htm>

Syntax:
cddr x => object
(setf (cddr x) new-object)

<http://www.lispworks.com/documentation/HyperSpec/Body/f_nthcdr.htm>

Syntax:
nthcdr n list => tail

Regards,
Adam