'(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
    )
  )

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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.