'(A. B) è davvero un elenco?


15

Sono davvero confuso con la .notazione. È '(a . b)un elenco?

(listp '(a . b))ritorna tma quando voglio sapere la sua lunghezza (length '(a . b))dà un errore Wrong type argument: listp, b. Lo stesso vale per altre funzioni, nth,mapcarecc. Danno tutte lo stesso errore

C'è qualche funzione che posso distinguere tra '(a b)e '(a . b)?


Contesto: ho riscontrato questo problema quando volevo implementare la versione ricorsiva di mapcar. Ecco la mia implementazione

(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 per estrarre tutti i tag specifici da HTML analizzato. Esempio di htmlanalisi

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

Quindi estraggo tutto <td>come

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

1
Non c'è true-list-pin Elisp semplicemente perché non è stato trovato abbastanza utile per fornirlo. In effetti, non ricordo l'ultima volta che ho voluto verificare se un elenco fosse corretto, quindi forse se ci dai un po 'più di informazioni sul tuo caso d'uso, possiamo aiutarti a risolvere il tuo problema in un altro modo.
Stefan

@Stefan In breve, voglio implementare mapcar ricorsivo, valuto la funzione data su elementi di un determinato elenco, quindi su elementi di elementi dell'elenco, quindi su elementi di elementi di elementi dell'elenco e così via. Quindi ho bisogno di sapere se un elemento è un vero elenco o no.
Tom,

È utile ad esempio quando analizzo HTML con libxml-parse-html-regione voglio estrarre tutti i <td>tag.
Tom,

Potete mostrarci un esempio concreto in cui è possibile ottenere un elenco corretto o un elenco improprio o qualcos'altro e dove è necessario gestire i 3 casi in modo diverso? Nella maggior parte dei casi con cui ho avuto a che fare, i casi "corretti" e "impropri" possono essere condivisi fino a quando non si arriva alla coda impropria effettiva, quindi non è necessario testare nuovamente se è corretto o meno: prova solo se è conspinvece.
Stefan

1
libxml non restituisce solo elenchi di elenchi. Ogni elenco che rappresenta un elemento XML ha il modulo (attributi del simbolo. Contenuto). Quindi il tuo codice non dovrebbe applicare mapcar in modo ricorsivo su tutti gli elementi degli elenchi, ma solo su cddrquello dell'elenco (per saltare il nome dell'elemento e gli attributi). Una volta fatto ciò, dovresti scoprire che tutti gli elenchi sono corretti e il tuo problema scomparirà. Risolverà anche un bug nel tuo codice in cui potresti confondere un tdattributo per un tdelemento.
Stefan

Risposte:


22

Soddisfa listp, quindi in questo senso è un elenco. listpverifica solo se qualcosa è un contro o nil(aka ()), da un lato, o qualcos'altro, dall'altro.

Una lista corretta o vero e proprio elenco (o una lista che non è un elenco con punto oppure una lista circolare) è qualcosa che è listpe ha anche nilla sua ultima cdr. Cioè, un elenco XSè corretto se (cdr (last XS))è nil(ed è così che lo si distingue).

Un altro modo per dirlo è che un elenco corretto ha un elenco corretto come suo cdr . Questo è il modo in cui l' elenco dei tipi di dati (proprio) è definito nelle lingue digitate. È una definizione di tipo generico e ricorsivo: la parte generica afferma che il primo argomento del costruttore di elenchi non vuoti (spesso chiamato consBTW) può essere di qualsiasi tipo. La parte ricorsiva afferma che il suo secondo argomento è un'istanza di tipo (proprio) Elenco .

Sì, controlli se un dato listpè un elenco corretto usando (cdr (last XS))is nil. Al fine di verificare se il cdr della creatura è di per sé un elenco corretto, è necessario continuare a controllare il suo cdr, fino alla fine - gli ultimi contro, per vedere se lo è nil. È possibile definire un predicato per questo come segue, se si desidera:

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

Sebbene un elenco circolare non abbia fine, Emacs (che inizia con Emacs 24) è abbastanza intelligente da controllare lastcorrettamente, quindi questo codice funziona anche per un elenco circolare (ma solo per Emacs 24.1 e successivi; per le versioni precedenti si ottiene una ricorsione "infinita" fino a quando lo stack non trabocca).

È possibile utilizzare funzioni come lengthsolo su elenchi appropriati e altre sequenze. Vedi anche funzione safe-length.

Vedere il manuale Elisp, nodo Contro celle .

Per quanto riguarda la notazione, (a b)è solo zucchero sintattico per (a . (b . nil))- vedere il manuale di Elisp, nodo Notazione di coppia punteggiata


Qual è la migliore pratica per verificare l'elenco corretto? Verificando (cdr (last XS))è nilè crumblesome. Non esiste una funzione simile proper-list-p?
Tom,

@tom: Questo, o qualcosa di equivalente, è necessario - devi controllare l'ultima cella di contro. Ho aggiunto altro su questo nella risposta ora.
Tratto

@Drew Vorrei cambiare il corpo della funzione in (unless (atom x) (not (cdr (last x))))modo da poter persino chiamare (true-list-p "text")e nilnon ottenere un errore.
Tom,

@tom: giusto; grazie. In realtà, dovrebbe essere testato prima per assicurarsi che sia un contro o nil(cioè, listp). (Inoltre, FWIW, io non uso unlesso whenper il loro valore di ritorno che uso. and, orE ifper quello.)
Drew
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.