Home > Back-end >  Lisp Swap function
Lisp Swap function

Time:09-24

I am trying to write a functional program in Lisp that requires any two elements of a list to be shaped and return the new list. So far I have:

(defun swap (lst x y)
  (let ((temp (nth y lst)))
   (substitute (nth x lst) (nth y lst))
   (substitute temp (nth x lst)))

When I run: (swap '(1 2 3 4 5 e 6 7 8) 5 4) I get:

*** - EVAL/APPLY: too many arguments given to SWAP

CodePudding user response:

You should decide whether your swap will modify old list or not.

Destructive swap can be done with already mentioned rotatef or with pair of setf:

(defun swap1 (lst i j)
  (let ((temp (nth i lst)))
    (setf (nth i lst) (nth j lst))
    (setf (nth j lst) temp)
    lst))

Non-destructive swap has to work with copy of argument:

(defun swap2 (lst i j)
  (let* ((newlist (copy-tree lst))
         (temp (nth i newlist)))
    (setf (nth i newlist) (nth j newlist))
    (setf (nth j newlist) temp)
    newlist))

CodePudding user response:

remarks

Your code looks a bit like it was inspired by Scheme: you named the variable lst and introduced a local variable as-if with define but in a way that does not work in Common Lisp, where let is scoped. Moreover, you do not use the return value of SUBSTITUTE, which means that any intermediate result is discarded. The substitute function is purely functional and do not change its inputs with side-effects.

(defun swap (lst x y)
  (let (temp (nth y lst))) ;; temp is only visible inside the let
  (substitute (nth x lst)  (nth y lst)) ;; result is discarded
  (substitute temp (nth x lst))) ;; <- the only thing actually used in swap

implementing swap

There are two possible versions for this function, one that mutates the input list, one that doesn't. You also have to decide what is or isn't a valid input and what happens when an invalid input is given.

In both cases below, I assume that x is strictly lower than y, you can make the function more robust by swapping x and y in case this is not the case, or simply returning lst when x and y are equal.

Note also that in case x and/or y are beyond the limit of the list, the result is going to contain NIL values or signal an error, you could add more checks in the below functions to avoid those problems.

destructive version

Here, nswap is the destructive version, it descends into the list x times using nthcdr, to the sublist that starts at position x. From this sublist, it descends the remaining amount of cells to reach position y. The two lists xlist and ylist are lists that start at respectively position x and y. Then, I exchange their car using ROTATEF.

(defun nswap (list x y)
  (assert (< x y))
  (let* ((xlist (nthcdr x list))
         (ylist (nthcdr (- y x) xlist)))
    (prog1 list
      (rotatef (car xlist)
               (car ylist)))))

functional version

The purely functional swap could just call nswap on (copy-list lst). It is however also possible to share some cells with lst, since everything that follows the rest of ylist is left unmodified. That's how the function works, it computes xlist and ylist as before, but uses LDIFF to produce fresh lists (regions of the initial list). The different slices of the initial lists are concatenated with nconc (they are fresh lists, so the code has local side-effects but externally, the input list is unmodified). The (rest ylist) is the last argument to nconc and is not a copy.

(defun swap (list x y)
  (assert (< x y))
  (let* ((xlist (nthcdr x list))
         (ylist (nthcdr (- y x) xlist)))
    (nconc (ldiff list xlist)
           (list (first ylist))
           (ldiff (rest xlist) ylist)
           (list (first xlist))
           (rest ylist))))

For example, using :circle t for write let us see the shared end of list:

(let ((list '(a b c d e f g h i)))
  (write (list :original list
               :swapped (swap list 1 3))
         :circle t))

The following is printed, with #1= marking the tail and #1# showing that the identical same tail is used in the result:

(:ORIGINAL (A B C D . #1=(E F G H I))
 :SWAPPED (A D C B . #1#))
  • Related