Не может взять АВТОМОБИЛЬ 2 (Common Lisp) - PullRequest
0 голосов
/ 04 апреля 2020

Я новичок в Лиспе и пытаюсь добраться туда, где я могу перебрать список различной глубины с помощью рекурсии CAR / CDR. Я видел похожие вопросы, но ни один из них не касался той проблемы, с которой я столкнулся, или того, как ее решить. В этом примере я пытаюсь возвести в квадрат каждое число в списке:

(defun square-list (x)
  (cond ((and (atom x) (not (equal x nil)))
         (cons (* x x) (or (square-list (car x)) (square-list (cdr x)))))
        (t (or (square-list (car x)) (square-list (cdr x))))))

Из прочтения у меня сложилось впечатление, что и CAR, и CDR атома будут либо нулем, либо другим атомом. Что я тут не так делаю? Спасибо за ваше время.

РЕДАКТИРОВАТЬ: Я решил основную c проблему возведения в квадрат числа произвольной глубины (например, '(1 ((2) 3) (4 5))) , но я все еще получаю сообщение об ошибке, когда я добавляю письмо в микс. Вот новый код:

(defun square-list (x)
          (cond ((null x) nil) 
                ((and (atom x) (numberp x)) (* x x))
                (t (cons (square-list (car x)) (square-list (cdr x))))))

Ошибка все та же: «Невозможно взять АВТОМОБИЛЬ А» со списком »(a ((2) 3) (4 5)).

1 Ответ

1 голос
/ 04 апреля 2020

Если вам нужно рекурсивно работать со списком, вам следует подумать о двух вещах: случай завершения, например, что происходит, когда список пуст, и рекурсивный случай, например, что происходит, когда список имеет по крайней мере один элемент.

В вашем случае, когда список пуст, ни один номер не должен быть в квадрате, поэтому верните пустой список. В противном случае вы должны построить (т.е. cons) новый список, первый элемент которого является квадратом первого элемента списка (т.е. (car x)), а остальная часть списка получается путем возведения в квадрат остальной части списка ( т.е. (cdr x)).

Вот возможное решение вашей проблемы:

CL-USER>  (defun square-list (x)
            (if (null x)
                nil
                (cons (* (car x) (car x)) (square-list (cdr x)))))

SQUARE-LIST
CL-USER> (square-list '(1 2 3 4 5))
(1 4 9 16 25)

Кстати, вы можете взять только списки car и cdr, не из атомов (это приведет к ошибке времени выполнения).

CL-USER> (car (list 1 2 3 4 5))
1
CL-USER> (cdr (list 1 2 3 4 5))
(2 3 4 5)
CL-USER> (car 1)
The value 1 is not of the expected type LIST.
[Condition of type TYPE-ERROR]

Если вместо этого вам нужно поместить все элементы дерева (т.е. список, содержащий произвольно вложенный подсписок), то вам нужно чтобы изменить функцию для управления тремя разными случаями, а не двумя:

  1. , если список пуст, возвращает пустой список,
  2. , если первый элемент списка сам по себе является список, затем примените к нему рекурсивно square-list и «против» результата, полученного с применением square-list к остальной части списка,
  3. в противном случае, верните новый список, возведя в квадрат первый элемент списка (который теперь мы знаем, что это, безусловно, атом) и «с onsing »результат с результатом возведения в квадрат остальной части списка.

Например:

CL-USER> (defun square-list (x)
           (cond ((null x) nil)
                 ((listp (car x)) (cons (square-list (car x))
                                        (square-list (cdr x))))
                 (t (cons (* (car x) (car x)) (square-list (cdr x))))))
SQUARE-LIST
CL-USER> (square-list '(1 ((2 3) (4))))
(1 ((4 9) (16)))

В качестве последних замечаний, обратите внимание, что вышеуказанная функция не является хвостовой рекурсивной. Самый простой способ сделать хвостовую рекурсию первым - использовать классическую стратегию «накопителя».

CL-USER> (defun square-list (x &optional accumulator)
           (if (null x)
               (reverse accumulator)
               (square-list (cdr x) (cons (* (car x) (car x)) accumulator))))
SQUARE-LIST
CL-USER> (square-list '(1 2 3 4 5))
(1 4 9 16 25)
...