Mapcar и ассоц.

Я хотел бы сделать:

(mapcar #'assoc '(a s) '((a . b) (c . d) (s . f)))

и вернуть его

((A . B) (S . F))

Что кажется вполне разумным, учитывая, что (assoc 'a '((a . b) (c . d) (s . f))) возвращает (A . B), а (assoc 's '((a . b) (c . d) (s . f))) возвращает (S . F). Но увы не работает:

*** - ASSOC: A is not a list
The following restarts are available:
ABORT          :R1      Abort main loop

Какие-нибудь мысли?


person rhombidodecahedron    schedule 29.01.2011    source источник


Ответы (2)


При использовании с двумя списками mapcar применяет функцию к спискам попарно (а с тремя списками — по тройному и т. д.). Так

(mapcar #'assoc '(a s) '((a . b) (c . d) (s . f)))

такой же как

( (assoc 'a (a . b)) (assoc 's (c . d)) )

(при использовании со списками разной длины mapcar использует размер наименьшего списка). Чтобы получить то, что вы хотите, вы должны сделать:

(mapcar (lambda (x) (assoc x '((a . b) (c . d) (s . f)))) '(a s))
person sepp2k    schedule 29.01.2011

Нам нужен другой уровень списка. Вторым аргументом должен быть список ассоциированных списков.

CL-USER >  (mapcar #'assoc '(a s) '(((a . b) (c . d) (s . f))))

((A . B))

Но второй аргумент состоит только из одного элемента. Теперь мы можем использовать трюк и сделать круговой список:

CL-USER > (mapcar #'assoc '(a s) '#1=(((A . B) (C . D) (S . F)) . #1#))

((A . B) (S . F))

Если мы создадим циклический список для второго аргумента, тогда он сработает.

Как функция:

(defun circular (list)
  (if (null list)
      list
    (setf (cdr (last list)) list)))

CL-USER > (mapcar #'assoc '(a s) (circular '(((a . b) (c . d) (s . f)))))

((A . B) (S . F))
person Rainer Joswig    schedule 30.01.2011