'(A. B) é realmente uma lista?

15

Estou realmente confuso com a .notação. É '(a . b)uma lista?

(listp '(a . b))retorna, tmas 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,mapcaretc. 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 htmlpara 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
    )
  )
tom
fonte
11
Não existe true-list-pno 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.
Stefan
@ Stefan Em resumo, eu quero implementar o mapcar recursivo, avalio determinada função nos elementos de uma determinada lista, depois nos elementos dos elementos da lista, depois nos elementos dos elementos dos elementos da lista e assim por diante. Então, eu preciso saber se um elemento é uma lista verdadeira ou não.
Tom
É útil, por exemplo, quando analiso html libxml-parse-html-regione quero extrair todas as <td>tags.
Tom
Você pode nos mostrar um exemplo concreto de onde você pode obter uma lista adequada, ou uma lista imprópria ou qualquer outra coisa, e onde precisa lidar com os três casos de maneira diferente? Na maioria dos casos com os quais tive que lidar, os casos "adequados" e "impróprios" podem ser compartilhados até chegarmos à cauda imprópria real, para que você novamente não precise testar se é adequado ou não: apenas teste se é em conspvez disso.
Stefan
11
libxml não apenas retorna listas de listas. Cada lista que representa um elemento XML possui o formulário (atributos do símbolo. Conteúdo). Portanto, seu código não deve aplicar o mapcar recursivamente sobre todos os elementos das listas, apenas sobre cddra 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 um tdatributo para um tdelemento.
Stefan

Respostas:

22

Satisfaz listp, portanto, nesse sentido, é uma lista. listpapenas testa se algo é um contras ou nil(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 é listpe também tem nilcomo seu último cdr. Ou seja, uma lista XSé adequada se (cdr (last XS))for nil(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 consBTW) 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))is nil. 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:

(defun true-listp (object)
  "Return non-`nil' if OBJECT is a true list."
  (and (listp object)  (null (cdr (last object)))))

Embora uma lista circular não tenha fim, o Emacs (começando com o Emacs 24) é inteligente o suficiente para verificar lastcorretamente, 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 lengthsomente em listas apropriadas e outras seqüências. Veja também função safe-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 pontilhado

Desenhou
fonte
Qual é a melhor prática para verificar a lista adequada? Verificar se (cdr (last XS))existe nilé fragmentário. Não existe uma função como proper-list-p?
tom
Tom: Isso, ou algo equivalente, é necessário - você deve verificar a última célula de contras. Eu adicionei mais sobre isso na resposta agora.
Tirou
@ Drew Eu mudaria o corpo da função para (unless (atom x) (not (cdr (last x))))Assim você pode chamar (true-list-p "text")e nilnão obter um erro.
Tom
@ tom: Certo; valeu. Na verdade, ele deve ser testado primeiro para garantir que é um contras ou nil( ou seja, listp). (Além disso, FWIW, eu não uso unlessou whenpor seu valor de retorno que eu uso. and, orE, ifpor isso.)
de Drew