Estou realmente confuso com a .
notação. É '(a . b)
uma lista?
(listp '(a . b))
retorna, t
mas quando eu quero saber o seu comprimento (length '(a . b))
dá um erro Wrong type argument: listp, b
. O mesmo se aplica a outras funções, nth,mapcar
etc. todos eles dão o mesmo erro
Existe alguma função que eu possa distinguir entre '(a b)
e '(a . b)
?
Contexto: eu encontrei esse problema quando queria implementar a versão recursiva do mapcar
. Aqui está a minha implementação
(defun true-listp (object)
"Return non-`nil' if OBJECT is a true list."
(and (listp object) (null (cdr (last object)))))
(defun recursive-mapcar (func list)
"Evaluates func on elements of the list, then on elements of elements of the list and so forth."
(let ((output nil))
(flet ((comp (a b) nil)
(call-fun-and-save (x) (add-to-list 'output (funcall func x) t 'comp))
(recursion (l)
(mapcar
(lambda (x)
(call-fun-and-save x)
(if (and (true-listp x)) ;; HERE I use true-listp, testing for list or cons is not sufficient
(recursion x)))
l)))
(recursion list))
output))
Eu uso isso para extrair todas as tags específicas do html analisado. Exemplo de html
para analisar
;; buffer 'html'
<html>
<body>
<table style="width:100%">
<tr> <td>Jill</td> <td>Smith</td> <td>50</td> </tr>
<tr> <td>Eve</td> <td>Jackson</td> <td>94</td> </tr>
</table>
</body>
</html>
Então eu extraio tudo <td>
como
(with-current-buffer (get-buffer "html")
(let ((data (libxml-parse-html-region (point-max) (point-min))))
;; gat only <td> tags
(-non-nil
(recursive-mapcar
(lambda(x) (and (consp x) (equal 'td (car x)) x))
data))
data
)
)
true-list-p
no Elisp simplesmente porque não foi considerado útil o suficiente para fornecê-lo. Na verdade, não me lembro da última vez em que desejei testar se uma lista era adequada; talvez, se você nos der um pouco mais de informações sobre seu caso de uso, possamos ajudá-lo a resolver seu problema de outra maneira.libxml-parse-html-region
e quero extrair todas as<td>
tags.consp
vez disso.cddr
a lista (para ignorar o nome do elemento e os atributos). Depois de fazer isso, você deve descobrir que todas as listas estão corretas e seu problema desaparecerá. Ele também corrigirá um erro no seu código, onde você pode confundir umtd
atributo para umtd
elemento.Respostas:
Satisfaz
listp
, portanto, nesse sentido, é uma lista.listp
apenas testa se algo é um contras ounil
(aka()
), por um lado, ou algo mais, por outro lado.Uma lista adequada ou lista verdadeira (ou uma lista que não é uma lista pontilhada ou circular) é algo que é
listp
e também temnil
como seu último cdr. Ou seja, uma listaXS
é adequada se(cdr (last XS))
fornil
(e é assim que você a distingue).Outra maneira de colocar isso é que uma lista adequada tem uma lista adequada como seu cdr . É assim que a Lista de tipos de dados (adequada) é definida nos idiomas digitados. É uma definição de tipo genérico e recursivo: A parte genérica diz que o primeiro argumento para o construtor de lista não vazio (geralmente chamado de
cons
BTW) pode ser de qualquer tipo. A parte recursiva diz que seu segundo argumento é uma instância do tipo (apropriado) List .Sim, você verifica se um dado
listp
é uma lista adequada usando(cdr (last XS))
isnil
. Para verificar se o cdr do bichinho é uma lista adequada, você deve continuar verificando seu cdr até o fim - os últimos contras, para ver se énil
. Você pode definir um predicado para isso da seguinte maneira, se desejar:Embora uma lista circular não tenha fim, o Emacs (começando com o Emacs 24) é inteligente o suficiente para verificar
last
corretamente, portanto esse código funciona mesmo para uma lista circular (mas apenas para o Emacs 24.1 e posterior; nas versões anteriores, você obtém uma recursão "infinita" até o estouro da pilha).Você pode usar funções como
length
somente em listas apropriadas e outras seqüências. Veja também funçãosafe-length
.Consulte o manual Elisp, nó Cons Cells .
Quanto à notação,
(a b)
é apenas um açúcar sintático para(a . (b . nil))
- consulte o manual do Elisp, nó Notação de par pontilhadofonte
(cdr (last XS))
existenil
é fragmentário. Não existe uma função comoproper-list-p
?(unless (atom x) (not (cdr (last x))))
Assim você pode chamar(true-list-p "text")
enil
não obter um erro.nil
( ou seja,listp
). (Além disso, FWIW, eu não usounless
ouwhen
por seu valor de retorno que eu uso.and
,or
E,if
por isso.)