From: ········@gmail.com
Subject: specific remove
Date: 
Message-ID: <1127238663.466482.207990@g47g2000cwa.googlegroups.com>
Is there an easy way to remove a single element from a list when there
are duplicates of that element?

For example, if I have (1 2 1 3), is it possible to remove just the
second '1' and end up with (1 2 3)?

From: Pascal Bourguignon
Subject: Re: specific remove
Date: 
Message-ID: <87psr3sizx.fsf@thalassa.informatimago.com>
········@gmail.com writes:

> Is there an easy way to remove a single element from a list when there
> are duplicates of that element?
>
> For example, if I have (1 2 1 3), is it possible to remove just the
> second '1' and end up with (1 2 3)?

Your specification is not really precise.  
Do you mean the following?

(defun positions (item list &key (test (function eql)) (key (function identity)))
   (loop for i from 0 for x in list
         when (funcall test item (funcall key x)) collect i))

(defun remove-nth-occurence-of-item (n item list
                                     &key (test (function eql)) 
                                          (key (function identity)))
   (let ((p (nth n (positions item list :test test :key key))))
      (if p
         (remove-if (constantly t) list :start p :end (1+ p))
         list)))

(remove-nth-occurence-of-item 1 1 '(1 2 1 3))


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GCS d? s++:++ a+ C+++ UL++++ P--- L+++ E+++ W++ N+++ o-- K- w--- 
O- M++ V PS PE++ Y++ PGP t+ 5+ X++ R !tv b+++ DI++++ D++ 
G e+++ h+ r-- z? 
------END GEEK CODE BLOCK------
From: ········@gmail.com
Subject: Re: specific remove
Date: 
Message-ID: <1127241585.433109.259990@g44g2000cwa.googlegroups.com>
Sorry for not being clear.  I tried to make my example simple and lost
what I was trying to convey.

Basically I need to treat it as an array and remove the nth element.

Something like (my-remove 3 '(a b c d e))
and get (a b c e).  The catch is I don't care about duplicates like the
normal remove does.

That is (my-remove 3 '(a a a a b))
should give me (a a a b)
From: Tayssir John Gabbour
Subject: Re: specific remove
Date: 
Message-ID: <1127244542.851535.36840@g14g2000cwa.googlegroups.com>
········@gmail.com wrote:
> Sorry for not being clear.  I tried to make my example simple and lost
> what I was trying to convey.
>
> Basically I need to treat it as an array and remove the nth element.
>
> Something like (my-remove 3 '(a b c d e))
> and get (a b c e).  The catch is I don't care about duplicates like the
> normal remove does.
>
> That is (my-remove 3 '(a a a a b))
> should give me (a a a b)

Something like the following?

(defun my-remove (nth list)
  (remove-if (constantly t)
             list
             :start nth :count 1))

No doubt there's something more eloquent, but it escapes me at the
moment...


Tayssir
From: drewc
Subject: Re: specific remove
Date: 
Message-ID: <AGZXe.529601$s54.83369@pd7tw2no>
Tayssir John Gabbour wrote:
> Something like the following?
> 
> (defun my-remove (nth list)
>   (remove-if (constantly t)
>              list
>              :start nth :count 1))
> 
> No doubt there's something more eloquent, but it escapes me at the
> moment...

That's a nice one! Of course remove-if would have this functionality. 
I've got to stop going to LOOP first, and start reading the hyperspec :)


drewc


-- 
Drew Crampsie
drewc at tech dot coop
"Never mind the bollocks -- here's the sexp's tools."
	-- Karl A. Krueger on comp.lang.lisp
From: Timofei Shatrov
Subject: Re: specific remove
Date: 
Message-ID: <433064d2.26381132@news.readfreenews.net>
On 20 Sep 2005 11:39:45 -0700, ········@gmail.com tried to confuse
everyone with this message:

>Sorry for not being clear.  I tried to make my example simple and lost
>what I was trying to convey.
>
>Basically I need to treat it as an array and remove the nth element.
>
>Something like (my-remove 3 '(a b c d e))
>and get (a b c e).  The catch is I don't care about duplicates like the
>normal remove does.
>
>That is (my-remove 3 '(a a a a b))
>should give me (a a a b)
>

Something like
(defun my-remove (n list)
 (cons (butlast list (- (length list) n 1)) (nthcdr (1+ n) list)))


-- 
|a\o/r|,-------------.,---------- Timofei Shatrov aka Grue ------------.
| m"a ||FC AMKAR PERM|| mail: grue at mail.ru  http://grue3.tripod.com |
|  k  ||  PWNZ J00   || Kingdom of Loathing: Grue3 lvl 18 Seal Clubber |
`-----'`-------------'`-------------------------------------------[4*72]
From: Peter Seibel
Subject: Re: specific remove
Date: 
Message-ID: <m2r7bjz33b.fsf@gigamonkeys.com>
····@mail.ru (Timofei Shatrov) writes:

> On 20 Sep 2005 11:39:45 -0700, ········@gmail.com tried to confuse
> everyone with this message:
>
>>Sorry for not being clear.  I tried to make my example simple and lost
>>what I was trying to convey.
>>
>>Basically I need to treat it as an array and remove the nth element.
>>
>>Something like (my-remove 3 '(a b c d e))
>>and get (a b c e).  The catch is I don't care about duplicates like the
>>normal remove does.
>>
>>That is (my-remove 3 '(a a a a b))
>>should give me (a a a b)
>>
>
> Something like
> (defun my-remove (n list)
>  (cons (butlast list (- (length list) n 1)) (nthcdr (1+ n) list)))

One should never pass up an opportunity to use LDIFF:

  (defun my-remove (n list)
    (let ((tail (nthcdr n list)))
      (nconc (ldiff list tail) (rest tail))))

Note this is non-destructive (a.k.a. non-recycling) despite the use of
NCONC since LDIFF returns (as it must) new list structure. It does, of
course, return a list that may share structure with the original list.

-Peter

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: drewc
Subject: Re: specific remove
Date: 
Message-ID: <TDZXe.524808$5V4.92300@pd7tw3no>
········@gmail.com wrote:
> Sorry for not being clear.  I tried to make my example simple and lost
> what I was trying to convey.
> 
> Basically I need to treat it as an array and remove the nth element.
> 
> Something like (my-remove 3 '(a b c d e))
> and get (a b c e).  The catch is I don't care about duplicates like the
> normal remove does.

This should do what you want (assuming you have actually specified what 
you want)

1) using loop :

CL> (defun remove-nth (number list)
       (append (loop for n upto (1- number)
		    for i in list
		    collect i)
	      (nthcdr (1+ number) list)))
REMOVE-NTH

Or a destructive version using rplacd :

CL> (defun delete-nth (number list)
       (prog2
	  (rplacd (nthcdr (1- number) list)
		  (nthcdr (1+ number) list))
	  list))
DELETE-NTH


Or a recursive translation :

CL> (defun r-remove-nth (number list &optional new-list (count 0))
       (if (< count number)
	  (r-remove-nth number (cdr list)
			(cons (car list) new-list)
			(1+ count))
	  (append (reverse new-list) (cdr list))))


And the results :

CL> (remove-nth 3 (list 0 1 2 3 4 5))
(0 1 2 4 5)
CL> (delete-nth 3 (list 0 1 2 3 4 5))
(0 1 2 4 5)
CL> (r-remove-nth 3 (list 0 1 2 3 4 5))
(0 1 2 4 5)
CL>

HTH.


-- 
Drew Crampsie
drewc at tech dot coop
"Never mind the bollocks -- here's the sexp's tools."
	-- Karl A. Krueger on comp.lang.lisp
From: drewc
Subject: Re: specific remove
Date: 
Message-ID: <z6_Xe.236647$Hk.46381@pd7tw1no>
drewc wrote:
> ········@gmail.com wrote:
> 
>> Sorry for not being clear.  I tried to make my example simple and lost
>> what I was trying to convey.
>>
>> Basically I need to treat it as an array and remove the nth element.
>>
>> Something like (my-remove 3 '(a b c d e))
>> and get (a b c e).  The catch is I don't care about duplicates like the
>> normal remove does.

And, FWIW, this last version is faster and conses less than all the 
non-destuctive versions in this thread so far (at least on SBCL x86 linux) :

CL> (defun l-remove-nth (nth list)
       (loop for x upfrom 0
	    for i in list
	    with new-list
	    do (unless (eql x nth)
		 (setf new-list (cons i new-list)))
	    finally (return (nreverse new-list))))

Can you tell i love loop?  :)

drewc

> 
> 
> This should do what you want (assuming you have actually specified what 
> you want)
> 
> 1) using loop :
> 
> CL> (defun remove-nth (number list)
>       (append (loop for n upto (1- number)
>             for i in list
>             collect i)
>           (nthcdr (1+ number) list)))
> REMOVE-NTH
> 
> Or a destructive version using rplacd :
> 
> CL> (defun delete-nth (number list)
>       (prog2
>       (rplacd (nthcdr (1- number) list)
>           (nthcdr (1+ number) list))
>       list))
> DELETE-NTH
> 
> 
> Or a recursive translation :
> 
> CL> (defun r-remove-nth (number list &optional new-list (count 0))
>       (if (< count number)
>       (r-remove-nth number (cdr list)
>             (cons (car list) new-list)
>             (1+ count))
>       (append (reverse new-list) (cdr list))))
> 
> 
> And the results :
> 
> CL> (remove-nth 3 (list 0 1 2 3 4 5))
> (0 1 2 4 5)
> CL> (delete-nth 3 (list 0 1 2 3 4 5))
> (0 1 2 4 5)
> CL> (r-remove-nth 3 (list 0 1 2 3 4 5))
> (0 1 2 4 5)
> CL>
> 
> HTH.
> 
> 


-- 
Drew Crampsie
drewc at tech dot coop
"Never mind the bollocks -- here's the sexp's tools."
	-- Karl A. Krueger on comp.lang.lisp
From: justinhj
Subject: Re: specific remove
Date: 
Message-ID: <1127249331.978811.311900@g44g2000cwa.googlegroups.com>
Is your last loop one faster than your destructive version using
rplacd?

Justin
From: drewc
Subject: Re: specific remove
Date: 
Message-ID: <HS_Xe.529861$s54.245645@pd7tw2no>
justinhj wrote:
> Is your last loop one faster than your destructive version using
> rplacd?

Not by a long shot. I get between 8 and 12 seconds for the 
loop/recursive versions, and just above 3 for the rplacd (i'm simply 
doing about 10 million iterations of the test provided by the OP).

drewc






-- 
Drew Crampsie
drewc at tech dot coop
"Never mind the bollocks -- here's the sexp's tools."
	-- Karl A. Krueger on comp.lang.lisp
From: justinhj
Subject: Re: specific remove
Date: 
Message-ID: <1127253325.685671.245020@g47g2000cwa.googlegroups.com>
I guess that's what you'd expect.

I came up with this non-destructive version, which I quite like.

(defun remove-nth(n lst)
  (if (> n 0)
      (cons (car lst)
            (remove-nth (1- n) (cdr lst)))
      (cdr lst)))
From: justinhj
Subject: Re: specific remove
Date: 
Message-ID: <1127255952.301041.156530@g47g2000cwa.googlegroups.com>
Although I haven't thought of an elegant way to detect that input n is
greater than the length of the list. The implemetation adds (length -
n) nils to the end of the list if it's not long enough.
From: Thomas A. Russ
Subject: Re: specific remove
Date: 
Message-ID: <ymihdcf49b2.fsf@sevak.isi.edu>
"justinhj" <········@gmail.com> writes:

> 
> Although I haven't thought of an elegant way to detect that input n is
> greater than the length of the list. The implemetation adds (length -
> n) nils to the end of the list if it's not long enough.

Just test the list argument for being NIL.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: justinhj
Subject: Re: specific remove
Date: 
Message-ID: <1127309198.923670.53530@g49g2000cwa.googlegroups.com>
This is what came to mind but it seems a shame to take the cdr of list
twice

(defun remove-nth(n lst)
  (if (eql (cdr lst) nil)
      nil
      (if (> n 0)
          (cons (car lst)
                (remove-nth (1- n) (cdr lst)))
          (cdr lst))))
From: drewc
Subject: Re: specific remove
Date: 
Message-ID: <Iw0Ye.530228$s54.476953@pd7tw2no>
justinhj wrote:
> Although I haven't thought of an elegant way to detect that input n is
> greater than the length of the list. The implemetation adds (length -
> n) nils to the end of the list if it's not long enough.
> 

Ah good point! the second test should take care of that :

(defun remove-nth (nth list &optional new-list)
       (cond ((eql nth 0)
	     (nconc (nreverse new-list) (cdr list)))
	    ((eql nil list)
	     (nreverse new-list))
	    (t
	     (remove-nth (1- nth)
                          (cdr list) (cons (car list) new-list)))))

and there you go. I hope the OP is still with us :)



-- 
Drew Crampsie
drewc at tech dot coop
"Never mind the bollocks -- here's the sexp's tools."
	-- Karl A. Krueger on comp.lang.lisp
From: Emilio Lopes
Subject: Re: specific remove
Date: 
Message-ID: <qhk6h8kiqb.fsf@tiscali.de>
justinhj  writes:

> Although I haven't thought of an elegant way to detect that input n
> is greater than the length of the list. The implemetation adds
> (length -
> n) nils to the end of the list if it's not long enough.

~% sbcl
This is SBCL 0.8.16, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* (defun remove-nth (n lst)
  (if (or (= n 0) (null lst))
      (cdr lst)
    (cons (car lst) (remove-nth (1- n) (cdr lst)))))

REMOVE-NTH
* (remove-nth 0 (list 0 1 2 3 4 5))

(1 2 3 4 5)
* (remove-nth 3 (list 0 1 2 3 4 5))

(0 1 2 4 5)
* (remove-nth 8 (list 0 1 2 3 4 5))

(0 1 2 3 4 5)
* 
From: justinhj
Subject: Re: specific remove
Date: 
Message-ID: <1127520094.547169.139500@f14g2000cwb.googlegroups.com>
Yeah that's what I was grasping for ;-)
From: drewc
Subject: Re: specific remove
Date: 
Message-ID: <Hp0Ye.530220$s54.352909@pd7tw2no>
justinhj wrote:
> I guess that's what you'd expect.
> 
> I came up with this non-destructive version, which I quite like.
> 
> (defun remove-nth(n lst)
>   (if (> n 0)
>       (cons (car lst)
>             (remove-nth (1- n) (cdr lst)))
>       (cdr lst)))

Much nicer than mine, which was a poor translation of a hacked loop 
(hadn't had my coffee yet) :)

Here is a tail recursive version :

(defun remove-nth (nth list &optional new-list)
       (cond ((eql nth 0)
	     (nconc (nreverse new-list) (cdr list)))
	    (t
	     (remove-nth (1- nth) (cdr list)
			 (cons (car list) new-list)))))

which runs close to the speed of my LOOP abuse, and conses a lot less.

This is one on my favourite "features" of common lisp .. that i can 
throw something together that barely works, and come back in the 
afternoon to fine tune it. LOOP is a fine example of this. I often hack 
up quick solution using loop, and later come back and replace it with a 
recursive solution, when the problem is more clear to me. Or start with 
alists and  move to CLOS.

While the above is certainly more idiomatic, I had to think more than 5 
minutes about it , and there are other problems to be solved!



-- 
Drew Crampsie
drewc at tech dot coop
"Never mind the bollocks -- here's the sexp's tools."
	-- Karl A. Krueger on comp.lang.lisp
From: justinhj
Subject: Re: specific remove
Date: 
Message-ID: <1127309353.436377.102920@z14g2000cwz.googlegroups.com>
What I like about Lisp is that even when your code is working it's fun
to go back and make it shorter, more elegant or more generic. 

Justin
From: Damien Diederen
Subject: Re: specific remove
Date: 
Message-ID: <87zmq4q2n6.fsf@keem.bcc>
Hello,

drewc <·····@rift.com> writes:
> drewc wrote:
>> ········@gmail.com wrote:
>> 
>>> Sorry for not being clear.  I tried to make my example simple and lost
>>> what I was trying to convey.
>>>
>>> Basically I need to treat it as an array and remove the nth element.
>>>
>>> Something like (my-remove 3 '(a b c d e))
>>> and get (a b c e).  The catch is I don't care about duplicates like the
>>> normal remove does.
>
> And, FWIW, this last version is faster and conses less than all the 
> non-destuctive versions in this thread so far (at least on SBCL x86 linux) :
>
> CL> (defun l-remove-nth (nth list)
>       (loop for x upfrom 0
> 	    for i in list
> 	    with new-list
> 	    do (unless (eql x nth)
> 		 (setf new-list (cons i new-list)))
> 	    finally (return (nreverse new-list))))
>
> Can you tell i love loop?  :)

What about:

,----
| (defun remove-nth (nth list)
|   (loop :for index :from 0
|         :for item :in list
|         :unless (= index nth)
|           :collect item))
`----

> drewc

Cu,
Damien.

-- 
http://foobox.net/~dash/

I can resist everything except temptation.
                --Oscar Wilde