2015-10-06 3 views
1

Этот код вычисляется в tсписок Emacs не список

(listp '(foo 0 . 0)) 

Эти код выдает ошибку: eval: Wrong type argument: listp, 0

(mapcar (lambda (x) x) '(foo 0 . 0)) 
(length '(foo 0 . 0)) 

Все три используют один и тот же «список», но mapcar и length ясно не думайте, что это список. Это связано с тем, что список заканчивается 0 . 0, а не 0 0, хотя я не знаю, почему это имеет значение для mapcar, но не listp.

Есть ли способ изменить выражение mapcar, чтобы принимать как обычные списки, такие как (foo 0 0), так и эти списки в стиле cons-style (foo 0 . 0)? В самом деле приложение, мой вход имеет оба типа списков (например, ("a" (b 0 . 0) (c 0 . 0)) и лямбда-рекурсивная функция, которая вызывает mapcar, если ее аргумент является списком.

(В случае, если предыдущий пункт не ясен, ответ «использовать (foo 0 0) вместо» является неправильным.)

Я подозреваю, что ответ что-то вроде

(defun my-func (x) 
    (if (consp x) 
    (if (not-actually-a-list-p x) 
     (delistify (mapcar #'myfunc (listify x))) 
    (mapcar #'myfunc input)) 
    ; process the individual atoms 
    )) 

Однако, я не знаю, что not-actually-a-list-p, listify и delistify ш быть.

+1

Я очень хочется сказать «использовать (Foo 0 0) вместо», но вы, конечно, есть свой разум :-) – coredump

+0

@coredump Мой входной является c-style-alist'. Это не под моим контролем. (Если бы это было под моим контролем, я бы просто изменил его, а не попытался найти его для проблем и исправить его.) –

ответ

4

Причина listp возвращается T это потому, что он только проверяет, является ли аргумент либо nil или минусы-клетки. A собственный список либо nil, либо тот, где все cdr удовлетворяет listp.

mapcar и length действительно должны перебирать список и подавиться неподходящих списков, потому что они не могут взять на себя cdr чего-то не быть против-клеток.

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

(defun mapcar-dot (f list) 
    (typecase list 
    (null nil) 
    (cons (cons (funcall f (car list)) 
       (mapcar-dot f (cdr list)))) 
    (t (funcall f list)))) 

Пример

(list (mapcar-dot #'1+ '(1 2 3 4)) 
     (mapcar-dot #'1+ '(1 2 3 . 4))) 
=> ((2 3 4 5) (2 3 4 . 5)) 

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

(defun mapcar-dot* (f list) 
    (loop for (a . b) on list 
     collect (funcall f a) 
     unless (listp b) 
      collect (funcall f b))) 
+0

Это то, что мне нужно (включая поддержание ненадлежащей структуры). (a. b) vs. (a b) всегда сбивает меня с толку. –

+0

'(1. 2)' - одна ячейка cons, содержащая 1 в ее _car_ и 2 в _cdr_. Возможно, он был создан с помощью '(cons 1 2)'. С другой стороны, '(1 2)' - две ячейки cons. Первый содержит 1 в _car_ и его _cdr_ указывает на вторую ячейку, которая содержит 2 в своих _car_ и 'nil' в _cdr_. Он может быть создан с помощью '(список 1 2)' или '(cons 1 (cons 2 ноль))'. НТН – duthen

2

listp возвращает непредставленные nil для ячейки минусов:

listp is a built-in function in ‘C source code’. 

(listp OBJECT) 

Return t if OBJECT is a list, that is, a cons cell or nil. 
Otherwise, return nil. 

listp не проверяет истинный список (то есть, с кордом из него последних минусов клетки nil).

mapcar и length требуют, чтобы аргумент списка был истинным списком.

Чтобы применить mapcar в список, который может быть не правильный список, вы можете сделать что-то вроде этого:

(defun my-map (fn conses) 
    "Like `mapcar', but for a dotted list also." 
    (let ((res ())) 
    (while (consp conses) 
     (push (funcall fn (car conses)) res) 
     (setq conses (cdr conses))) 
    (when conses (push (funcall fn conses) res)) 
    (nreverse res)))