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�
--
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.
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.
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.
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
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�
--
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.
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�
--
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.
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.
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")
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
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
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�
--
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�
--
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.
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.
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