此代码的评估结果为t
(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样式列表(foo 0 . 0)
?在实际的应用程序中,我的输入有两种类型的列表(例如,("a" (b 0 . 0) (c 0 . 0))
,lambda是一个递归函数,如果它的参数是一个列表,则调用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
应该是什么。
答案 0 :(得分:4)
listp
返回T
的原因是因为它只检查参数是nil
还是cons-cell。 正确列表是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)))
答案 1 :(得分:2)
listp
为cons小区返回非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
不检查真正的列表(即,使用cdr的最后一个cons小区nil
)。
mapcar
和length
要求list参数为真实列表。
要将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)))