From: Leslie P. Polzer
Subject: Nice function to shift around elements in a list?
Date: 
Message-ID: <521ec25a-c466-470b-b098-45c02818163a@a37g2000pre.googlegroups.com>
I'm looking for a nice way to achieve movement of list elements by a
certain offset:

(defparameter list (list 'a 'b 'c 'd 'e))

(move 'c list -1) => (list 'a 'c 'b 'd 'e)
(move 'c list +2) => (list 'a 'b 'd 'e 'c)
(move 'a list -1) => (list 'a 'b 'c 'd 'e)
(move 'e list +5) => (list 'a 'b 'c 'd 'e)

Thanks in advance for any elegant solution. :)

  Leslie

From: ·······@eurogaran.com
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <bbf9e576-93ab-498a-9792-704bead65d14@a12g2000pro.googlegroups.com>
On Dec 16, 1:14 pm, "Leslie P. Polzer" <·············@gmx.net> wrote:
> I'm looking for a nice way to achieve movement of list elements by a
> certain offset:
>
> (defparameter list (list 'a 'b 'c 'd 'e))
>
> (move 'c list -1) => (list 'a 'c 'b 'd 'e)
> (move 'c list +2) => (list 'a 'b 'd 'e 'c)
> (move 'a list -1) => (list 'a 'b 'c 'd 'e)
> (move 'e list +5) => (list 'a 'b 'c 'd 'e)
>
> Thanks in advance for any elegant solution. :)
>
>   Leslie

The most elegant solution is to use the data representation that is
most adequate to the task at hand.

Have you thought about using vectors instead of lists?
From: Leslie P. Polzer
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <64b2ac3e-2fb9-4e63-8905-58f43ccb4bcd@a26g2000prf.googlegroups.com>
> The most elegant solution is to use the data representation that is
> most adequate to the task at hand.
>
> Have you thought about using vectors instead of lists?

What difference would that make to solve the problem at hand?

In fact one might well come up with a version that works for
all sequences.
From: budden
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <c711488b-840f-4efc-bfcd-95c42a84b77d@p2g2000prn.googlegroups.com>
Looks a bit like this:
http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/b00bf5a637d5dfdf?hl=en#

your $4 hour cl freelancer
From: Leslie P. Polzer
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <89fb4709-0993-48db-b86d-8df9b7a5693b@n33g2000pri.googlegroups.com>
On Dec 16, 3:14 pm, budden <···········@mail.ru> wrote:
> Looks a bit like this:http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/b0...

I'm not sure how well that would map to this one.
From: Alexander Lehmann
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <ead3b60e-1ee4-44b2-b1d9-1488f8cdc6e5@y1g2000pra.googlegroups.com>
On 16 Dez., 13:14, "Leslie P. Polzer" <·············@gmx.net> wrote:
> I'm looking for a nice way to achieve movement of list elements by a
> certain offset:
>
> (defparameter list (list 'a 'b 'c 'd 'e))
>
> (move 'c list -1) => (list 'a 'c 'b 'd 'e)
> (move 'c list +2) => (list 'a 'b 'd 'e 'c)
> (move 'a list -1) => (list 'a 'b 'c 'd 'e)
> (move 'e list +5) => (list 'a 'b 'c 'd 'e)
>
> Thanks in advance for any elegant solution. :)
>
>   Leslie

(defun move (sym lst offset)
  (let* ((pos (position sym lst))
         (new-pos (mod (+ pos offset) (length lst)))
         (stripped-lst (remove sym lst :count 1)))
    (append (subseq stripped-lst 0 new-pos)
            (cons sym (subseq stripped-lst new-pos)))))

I don't think that this is the most elegant solution, yet it seems to
work ;-)
From: Leslie P. Polzer
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <9a250a4b-a583-4e90-9307-2ac90519ef06@i20g2000prf.googlegroups.com>
On Dec 16, 3:21 pm, Alexander Lehmann <········@in.tum.de> wrote:

> (defun move (sym lst offset)
>   (let* ((pos (position sym lst))
>          (new-pos (mod (+ pos offset) (length lst)))
>          (stripped-lst (remove sym lst :count 1)))
>     (append (subseq stripped-lst 0 new-pos)
>             (cons sym (subseq stripped-lst new-pos)))))
>
> I don't think that this is the most elegant solution, yet it seems to
> work ;-)

It does, almost:

 * (move 'a (list 'a 'b 'c) -1)

=> (B C A)
From: Thomas A. Russ
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <ymitz93aokq.fsf@blackcat.isi.edu>
"Leslie P. Polzer" <·············@gmx.net> writes:

> It does, almost:
> 
>  * (move 'a (list 'a 'b 'c) -1)
> 
> => (B C A)

I think it was assumed you wanted wrap-around.  Looking at the initial
examples, however, it seems you don't want that.  So, interpreting the
specification you had in mind (but never directly expressed), I assume
that you never move anything past the ends of the list.

So a minor tweak to Alexander's code to calculate NEW-POS differently
gives you what you want.

(defun move (sym lst offset)
  (let* ((pos (position sym lst))
         (len (length lst))
         (new-pos (max 0 (min (+ pos offset) (1- len))))
         (stripped-lst (remove sym lst :count 1)))
    (append (subseq stripped-lst 0 new-pos)
            (cons sym (subseq stripped-lst new-pos)))))


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: William James
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <gi97n8019cl@enews4.newsguy.com>
Thomas A. Russ wrote:

> "Leslie P. Polzer" <·············@gmx.net> writes:
> 
> > It does, almost:
> > 
> >  * (move 'a (list 'a 'b 'c) -1)
> > 
> > => (B C A)
> 
> I think it was assumed you wanted wrap-around.  Looking at the initial
> examples, however, it seems you don't want that.  So, interpreting the
> specification you had in mind (but never directly expressed), I assume
> that you never move anything past the ends of the list.
> 
> So a minor tweak to Alexander's code to calculate NEW-POS differently
> gives you what you want.
> 
> (defun move (sym lst offset)
>   (let* ((pos (position sym lst))
>          (len (length lst))
>          (new-pos (max 0 (min (+ pos offset) (1- len))))
>          (stripped-lst (remove sym lst :count 1)))
>     (append (subseq stripped-lst 0 new-pos)
>             (cons sym (subseq stripped-lst new-pos)))))

Ruby:

# Destructive (changes original list).
def move el, list, offset
  from = list.index(el)
  to = from + offset
  0 <= to and to < list.size and list.delete(el) and
    list.insert(to, el)
end
From: Madhu
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <m3r647602y.fsf@moon.robolove.meer.net>
* (Thomas A. Russ) <···············@blackcat.isi.edu> :
Wrote on 16 Dec 2008 11:48:37 -0800:

| "Leslie P. Polzer" <·············@gmx.net> writes:
|
|> It does, almost:
|> 
|>  * (move 'a (list 'a 'b 'c) -1)
|> 
|> => (B C A)
|
| I think it was assumed you wanted wrap-around.  Looking at the initial
| examples, however, it seems you don't want that.  So, interpreting the
| specification you had in mind (but never directly expressed), I assume
| that you never move anything past the ends of the list.

[Sorry I did not notice this post before sending the replying saying
 moving off the head of the list was incorrect behaviour]

I think the problem was not correctly specified, which is why I also
request the OP to supply best attempt before asking for alternatives as
it would help clarify the behaviour sought.

I assume the OP wants the wraparound behaviour at the list too.

| So a minor tweak to Alexander's code to calculate NEW-POS differently
| gives you what you want.

I think this approach to wraparound is not so good. Effectively OFFSET
would be working on a circular list.

What would you want fo if you have a list of 3 elements and OFFSET is
-200?

IMO, the designer of the MOVE routine ought to bound (OFFSET + position)
to lie WITHIN the list, and sanitize the value before calling the
routine.

If you have an INITIAL-OFFSET which moves you off the list, you can
compute 

OFFSET =  POSITION - (MOD (+ INITIAL-OFFSET POSITION) (LENGTH LIST))

i.e.

    (setq offset
	  (let ((position (position elem list))
		(length (length list)))
	    (- position (mod (+ offset position) length))))

Then you could use the pass this OFFSET to the posted MOVE routine.
[This appears to be cleaner to my eyes.  Does this work ?]

--
Madhu
From: Leslie P. Polzer
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <70234d24-0d23-4689-8983-0f7426c3876b@w24g2000prd.googlegroups.com>
> What would you want fo if you have a list of 3 elements and OFFSET is
> -200?
>
> IMO, the designer of the MOVE routine ought to bound (OFFSET + position)
> to lie WITHIN the list, and sanitize the value before calling the
> routine.

Yes, that's an interesting consideration. I didn't worry about it
because I expect to call it with only either -1 or +1.

Personally I would not expect the caller to get this right, though.

I would insert a sanitizer at the beginning of the fun.


> Then you could use the pass this OFFSET to the posted MOVE routine.
> [This appears to be cleaner to my eyes.  Does this work ?]

I haven't checked it but it should be easy to put this through
a modulo and get the correct value.

  Leslie
From: Madhu
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <m3myev5ztd.fsf@moon.robolove.meer.net>
[Some corrections, until I get into the habit of proofreading before
sending...]

* Madhu <··············@moon.robolove.meer.net> :
Wrote on Wed, 17 Dec 2008 07:21:17 +0530:

| IMO, the designer of the MOVE routine ought to bound (OFFSET + position)
| to lie WITHIN the list, and sanitize the value before calling the
| routine.
|
| If you have an INITIAL-OFFSET which moves you off the list, you can
| compute 
|
| OFFSET =  POSITION - (MOD (+ INITIAL-OFFSET POSITION) (LENGTH LIST))
^^^

OFFSET =  (MOD (+ INITIAL-OFFSET POSITION) (LENGTH LIST)) - POSITION

| i.e.
|
|     (setq offset
| 	  (let ((position (position elem list))
| 		(length (length list)))
| 	    (- position (mod (+ offset position) length))))
^^^
            (- (mod (+ offset position) length) position)

--
Madhu
From: Madhu
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <m31vw86vqy.fsf@moon.robolove.meer.net>
* "Leslie P. Polzer" 
Wrote on Tue, 16 Dec 2008 04:14:08 -0800 (PST):

| I'm looking for a nice way to achieve movement of list elements by a
| certain offset:
|
| (defparameter list (list 'a 'b 'c 'd 'e))
|
| (move 'c list -1) => (list 'a 'c 'b 'd 'e)
| (move 'c list +2) => (list 'a 'b 'd 'e 'c)
| (move 'a list -1) => (list 'a 'b 'c 'd 'e)
| (move 'e list +5) => (list 'a 'b 'c 'd 'e)
|
| Thanks in advance for any elegant solution. :)

In future please post your best attempt so far when asking for
alternatives.

AFAIC Lisp's elegance in dealing with lists is in using lisps' list
manipulation functions.

(defun move (elem list offset)
  (let* ((cons (member elem list))
         (head (ldiff list cons)))
    (if (plusp offset)
        (let ((tail (nthcdr (1+ offset) cons)))
          (nconc head (cdr (ldiff cons tail)) (list elem) tail))
        (let ((tail (nthcdr (+ (length head) offset) head)))
          (append (ldiff head tail) (list elem) tail (cdr cons))))))

--
Madhu
From: Leslie P. Polzer
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <57e4bd29-3d50-4646-a6ba-c1e17085b485@n33g2000pri.googlegroups.com>
On Dec 16, 3:27 pm, Madhu <·······@meer.net> wrote:

> AFAIC Lisp's elegance in dealing with lists is in using lisps' list
> manipulation functions.
>
> (defun move (elem list offset)
>   (let* ((cons (member elem list))
>          (head (ldiff list cons)))
>     (if (plusp offset)
>         (let ((tail (nthcdr (1+ offset) cons)))
>           (nconc head (cdr (ldiff cons tail)) (list elem) tail))
>         (let ((tail (nthcdr (+ (length head) offset) head)))
>           (append (ldiff head tail) (list elem) tail (cdr cons))))))

This doesn't work for me either when moving beyond the list head
(tail seems to work, though):

(move 'a (list 'a 'b 'c) -2)
The value -2 is not of type UNSIGNED-BYTE.
From: Madhu
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <m3wsdz61sk.fsf@moon.robolove.meer.net>
* "Leslie P. Polzer"
Wrote on Tue, 16 Dec 2008 09:49:20 -0800 (PST):

| On Dec 16, 3:27 pm, Madhu <·······@meer.net> wrote:
|
|> AFAIC Lisp's elegance in dealing with lists is in using lisps' list
|> manipulation functions.
|>
|> (defun move (elem list offset)
|>   (let* ((cons (member elem list))
|>          (head (ldiff list cons)))
|>     (if (plusp offset)
|>         (let ((tail (nthcdr (1+ offset) cons)))
|>           (nconc head (cdr (ldiff cons tail)) (list elem) tail))

^^^^^ WTF are these non-whitespace characters that google introduces in
transit?

|>         (let ((tail (nthcdr (+ (length head) offset) head)))
|>           (append (ldiff head tail) (list elem) tail (cdr cons))))))
|
| This doesn't work for me either when moving beyond the list head
| (tail seems to work, though):

Becasuse moving beyound the list head makes no sense?
What meaning could it possibly have for a proper list? 

| (move 'a (list 'a 'b 'c) -2)
| The value -2 is not of type UNSIGNED-BYTE.

The case is errenous input, and the program correctly signals an error.

--
Madhu
From: Szymon 'tichy'
Subject: Re: Nice function to shift around elements in a list?
Date: 
Message-ID: <gibj4n$o7$1@nemesis.news.neostrada.pl>
(defparameter *list* (copy-list '(< b c d * e f g >)))

(loop for symbol in '(< * >) do
      (loop for i from -8 upto 8 do
           (format t "~&~2D: ~A" i (move symbol *list* i)))
      (terpri) (terpri))

-8: (< B C D * E F G >)
-7: (< B C D * E F G >)
-6: (< B C D * E F G >)
-5: (< B C D * E F G >)
-4: (< B C D * E F G >)
-3: (< B C D * E F G >)
-2: (< B C D * E F G >)
-1: (< B C D * E F G >)
  0: (< B C D * E F G >)
  1: (B < C D * E F G >)
  2: (B C < D * E F G >)
  3: (B C D < * E F G >)
  4: (B C D * < E F G >)
  5: (B C D * E < F G >)
  6: (B C D * E F < G >)
  7: (B C D * E F G < >)
  8: (B C D * E F G > <)

-8: (* < B C D E F G >)
-7: (* < B C D E F G >)
-6: (* < B C D E F G >)
-5: (* < B C D E F G >)
-4: (* < B C D E F G >)
-3: (< * B C D E F G >)
-2: (< B * C D E F G >)
-1: (< B C * D E F G >)
  0: (< B C D * E F G >)
  1: (< B C D E * F G >)
  2: (< B C D E F * G >)
  3: (< B C D E F G * >)
  4: (< B C D E F G > *)
  5: (< B C D E F G > *)
  6: (< B C D E F G > *)
  7: (< B C D E F G > *)
  8: (< B C D E F G > *)

-8: (> < B C D * E F G)
-7: (< > B C D * E F G)
-6: (< B > C D * E F G)
-5: (< B C > D * E F G)
-4: (< B C D > * E F G)
-3: (< B C D * > E F G)
-2: (< B C D * E > F G)
-1: (< B C D * E F > G)
  0: (< B C D * E F G >)
  1: (< B C D * E F G >)
  2: (< B C D * E F G >)
  3: (< B C D * E F G >)
  4: (< B C D * E F G >)
  5: (< B C D * E F G >)
  6: (< B C D * E F G >)
  7: (< B C D * E F G >)
  8: (< B C D * E F G >)

;; elegant part

(defun move (item list n &key (key #'identity) (test #'eql))
   (%destrucive-move item (copy-list list) n key test))

;; gross ;)

(defun %ncdr (n list)
   (loop with tail = list
      repeat n
      for next = (cdr tail)
      do (if next (setq tail next) (loop-finish))
      finally (return tail)))

(defun %destrucive-move (item list n key test &aux (orig list) (last list))
   (unless list (return-from %destrucive-move))
   (cond ((plusp n)
          (if (funcall test item (funcall key (car list)))
              (let* ((first orig)
                     (temp (%ncdr n orig))
                     (temp1 (cdr temp)))
                (setq orig (cdr orig))
                (rplacd temp first)
                (rplacd first temp1))
              (loop
                 (when (or (null list)
                           (funcall test item (funcall key (car list))))
                   (unless (cdr list) (return))
                   (let* ((item-cons list)
                          (temp (%ncdr n list))
                          (cdrtemp (cdr temp)))
                     (rplacd last (cdr list))
                     (rplacd temp item-cons)
                     (rplacd item-cons cdrtemp))
                   (return))
                 (unless (eq list orig)
                   (setq last (cdr last)))
                 (setq list (cdr list)))))
         ((minusp n)
          (let ((counter -1) (n (abs n)) (place list))
            (loop
               (when (> counter n) (setq place (cdr place)))
               (when (or (null list)
                         (funcall test item (funcall key (car list))))
                 (unless (> counter -1) (return))
                 (let ((item-cons list))
                   (rplacd last (cdr item-cons))
                   (rplacd item-cons (cdr place))
                   (rplacd place item-cons))
                 (when (< counter n)
                   (rotatef (car orig) (cadr orig)))
                 (return))
               (unless (eq list orig)
                 (setq last (cdr last)))
               (setq list (cdr list))
               (incf counter)))))
   orig)

.