Lisp Exercises Involving List Manipulation

1.1k Views Asked by At

I trying to complete this exercise;

Write a Lisp function that takes as input a list of elements, such as (A B C)

, and returns a list in which the position of each element follows it, such as (A 1 B 2 C 3)

I'm trying to do it with two functions, however its not working correctly, I'm just getting the same list. Here is my code:

(defun insert (index var userList)
    (if (or (eql userList nil) (eql index 1))
            (cons var userList)
            (cons (car userList) (insert (- index 1) var (cdr userList)))))


(defun insertIndex (userList)
    (setq len (length userList))
    (loop for x from 1 to len
        do (insert x x userList)))

The insert functions seems to work fine on its own, but it seems like it doesn't do anything with loop. I'm new lisp and any help would be appreciated, thanks in advance.

2

There are 2 best solutions below

0
On BEST ANSWER

Positions in Lisp start with 0. In insertIndex the variable len is not defined. The LOOP does not return any useful value.

If you want to solve it with recursion, the solution is much simpler.

You need to test for the end condition. If the list is empty, return the empty list.

Otherwise create a new list with the FIRST element, the current position and the result of calling the function on the rest of the list and the position increased by one.

(LIST* 1 2 '(3 4)) is shorter for (cons 1 (cons 2 '(3 4))).

Here is the example with a local function. To create a top-level function with DEFUN is now your task. You only need to rewrite the code a bit. LABELS introduces a potentially recursive local function.

(labels ((pos-list (list pos)
           (if (null list)
               '()
               (list* (first list)
                      pos
                      (pos-list (rest list) (1+ pos))))))
  (pos-list '(a b c d e f) 0))
0
On

The main problem with your insertIndex function is that the do clause of loop is for side-effects only, it doesn't change the return value of the loop. (And your insert is side-effect free.) The right loop clause to add elements to a list return value is collect. (There are also append and nconc to join multiple lists.)

This is a working function:

(defun insert-index (list)
  (loop for elt in list and i from 1
    collect elt
    collect i))

Your whole expectations about the behaviour of the insert and insertIndex functions seem to be flawed. You need to get a clearer mental model about which functions are side-effecting, which are not, and whether you need side-effects or not to solve some particular problem.

Also, you shouldn't call setq on an undefined variable in Common Lisp. You need to first use let to introduce a new local variable.

Minor points: CamelCase is very unidiomatic in Lisp. The idiomatic way to seperate words in identifiers is to use dashes, like I did in my code example. And you don't need to do (eql something nil), there's the special null function to check if something's nil, e.g. (null something).