¿Es '(a. B) realmente una lista?

15

Estoy realmente confundido con la .notación. Es '(a . b)una lista?

(listp '(a . b))vuelve tpero cuando quiero saber su longitud (length '(a . b))da un error Wrong type argument: listp, b. Lo mismo es para otras funciones como nth,mapcaretc. todos dan el mismo error

¿Hay alguna función que pueda distinguir entre '(a b)y '(a . b)?


Contexto: Encontré este problema cuando quería implementar la versión recursiva de mapcar. Aquí está mi implementación

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

Lo uso para extraer todas las etiquetas específicas del html analizado. Ejemplo de htmlanalizar

;; 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>

Luego extraigo todo <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
fuente
1
No existe true-list-pen Elisp simplemente porque no se ha encontrado que sea lo suficientemente útil como para proporcionarlo. De hecho, no recuerdo la última vez que quise probar si una lista era correcta, así que tal vez si nos brinda un poco más de información sobre su caso de uso, podemos ayudarlo a resolver su problema de otra manera.
Stefan
@Stefan En resumen, quiero implementar mapcar recursivo, evalúo la función dada en elementos de una lista dada, luego en elementos de elementos de la lista, luego en elementos de elementos de elementos de la lista y así sucesivamente. Entonces necesito saber si un elemento es una lista verdadera o no.
tom
Es útil, por ejemplo, cuando analizo html libxml-parse-html-regiony deseo extraer todas las <td>etiquetas.
tom
¿Puede mostrarnos un ejemplo concreto en el que podría obtener una lista adecuada, o una lista incorrecta, u otra cosa, y donde necesita manejar los 3 casos de manera diferente? En la mayoría de los casos con los que he tenido que lidiar, los casos "adecuados" e "incorrectos" se pueden compartir hasta que lleguemos a la cola incorrecta real, por lo que nuevamente no necesita probar si es correcto o no: solo pruebe si es en su consplugar.
Stefan
1
libxml no solo devuelve listas de listas. Cada lista que representa un elemento XML tiene la forma (atributos de símbolo. Contenido). Por lo tanto, su código no debe aplicar mapcar de forma recursiva sobre todos los elementos de las listas, solo sobre cddrla lista (para omitir el nombre del elemento y los atributos). Una vez que haga eso, debería encontrar que todas las listas son correctas y su problema desaparecerá. También solucionará un error en su código donde puede confundir un tdatributo de un tdelemento.
Stefan

Respuestas:

22

Satisface listp, así que en ese sentido es una lista. listpsolo prueba si algo es una desventaja o nil(aka ()), por un lado, o algo más, por otro lado.

Una lista adecuada o una lista verdadera (o una lista que no es una lista punteada o una lista circular) es algo que es listpy también tiene nilcomo su último cdr. Es decir, una lista XSes adecuada si (cdr (last XS))es así nil(y así es como la distingue).

Otra forma de decir esto es que una lista adecuada tiene una lista adecuada como su cdr . Esta es la forma en que la Lista de tipos de datos (adecuada) se define en los idiomas escritos. Es una definición de tipo genérico y recursivo: la parte genérica dice que el primer argumento para el constructor de lista no vacía (a menudo llamado cons, por cierto) puede ser de cualquier tipo. La parte recursiva dice que su segundo argumento es una instancia de tipo (apropiado) Lista .

Sí, verifica si un dado listpes una lista adecuada usando (cdr (last XS))is nil. Para verificar si el cdr del bicho es en sí mismo una lista adecuada, debe continuar verificando su cdr, hasta el final, los últimos inconvenientes, para ver si es así nil. Puede definir un predicado para esto de la siguiente manera, si desea:

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

Aunque una lista circular no tiene fin, Emacs (comenzando con Emacs 24) es lo suficientemente inteligente como para verificar lastcorrectamente, por lo que este código funciona incluso para una lista circular (pero solo para Emacs 24.1 y posterior; para versiones anteriores obtienes una recursión "infinita" hasta que la pila se desborde).

Puede usar funciones como lengthsolo en listas apropiadas y otras secuencias. Ver también función safe-length.

Consulte el manual de Elisp, celdas de cons . De nodo .

En cuanto a la notación, (a b)es solo azúcar sintáctica para (a . (b . nil))- vea el manual de Elisp, notación de pares de puntos de nodo

Dibujó
fuente
¿Cuál es la mejor práctica para verificar la lista adecuada? Comprobar si (cdr (last XS))es niles crumblesome. ¿No hay una función como proper-list-p?
Tom
@tom: Eso, o algo equivalente, es necesario: debe verificar la última celda de contras. He agregado más sobre esto en la respuesta ahora.
Dibujó
@Drew Cambiaría el cuerpo de la función a (unless (atom x) (not (cdr (last x))))Para que puedas llamar (true-list-p "text")y nilno obtener un error.
Tom
@tom: Derecha; Gracias. En realidad, debe probarse primero para asegurarse de que sea una desventaja o nil(es decir, listp). (Además, pedía algo, yo no uso unlesso whenpor su valor de retorno que utilizo. and, orY, ifpor eso.)
dibujó