Czy „(a. B) to naprawdę lista?

15

Jestem naprawdę pomylony z .notacją. Jest '(a . b)lista?

(listp '(a . b))zwraca, tale gdy chcę wiedzieć, jego długość (length '(a . b))powoduje błąd Wrong type argument: listp, b. To samo dotyczy innych funkcji nth,mapcaritp., Wszystkie dają ten sam błąd

Czy jest jakaś funkcja, która mogę rozróżnić '(a b)i '(a . b)?


Kontekst: napotkałem ten problem, gdy chciałem zaimplementować wersję rekurencyjną mapcar. Oto moja implementacja

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

Używam tego do wyodrębnienia wszystkich określonych tagów z przeanalizowanego HTML. Przykład htmlanalizy

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

Następnie wyodrębniam wszystko <td>jako

(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
    )
  )
Tomek
źródło
1
Nie ma true-list-pw Elisp po prostu dlatego, że nie został uznany za przydatny wystarczy do jej świadczenia. Rzeczywiście, nie pamiętam, kiedy ostatni raz chciałem sprawdzić, czy lista jest odpowiednia, więc może jeśli podasz nam nieco więcej informacji na temat swojego przypadku użycia, możemy pomóc Ci rozwiązać problem w inny sposób.
Stefan
@Stefan Krótko mówiąc, chcę zaimplementować mapar rekurencyjny, oceniam podaną funkcję na elementach danej listy, następnie na elementach listy, a następnie na elementach elementów listy i tak dalej. Muszę więc wiedzieć, czy element jest prawdziwą listą, czy nie.
tom
Jest to przydatne na przykład, gdy analizuję HTML libxml-parse-html-regioni chcę wyodrębnić wszystkie <td>tagi.
tom
Czy możesz nam pokazać konkretny przykład, w którym możesz uzyskać odpowiednią listę, niewłaściwą listę lub coś innego i gdzie musisz inaczej rozpatrzyć 3 sprawy? W większości przypadków, z którymi miałem do czynienia, „właściwe” i „niewłaściwe” przypadki można udostępniać, dopóki nie dojdziemy do właściwego niewłaściwego ogona, więc nie musisz już sprawdzać, czy jest to właściwe, czy nie: po prostu sprawdź, czy to conspzamiast tego.
Stefan
1
libxml nie zwraca tylko list list. Każda lista reprezentująca element XML ma formę (atrybuty symbolu. Treść). Dlatego twój kod nie powinien stosować mapcar rekurencyjnie do wszystkich elementów list, tylko nad cddrlistą (aby pominąć nazwę elementu i atrybuty). Gdy to zrobisz, powinieneś stwierdzić, że wszystkie listy są prawidłowe, a Twój problem zniknie. Naprawi to również błąd w kodzie, w którym możesz pomylić tdatrybut tdelementu.
Stefan

Odpowiedzi:

22

Spełnia listp, więc w tym sensie jest to lista. listppo prostu sprawdza, czy coś jest wadą lub nil(aka ()) z jednej strony, czy coś innego z drugiej strony.

Właściwa lista lub prawdziwe listy (lub lista nie jest przerywana lista lub lista okrągły) jest czymś listp, a także posiada nilw swoim ostatnim CDR. Oznacza to, że lista XSjest poprawna, jeśli (cdr (last XS))jest nil(i tak ją rozróżniasz).

Innym sposobem na wyrażenie tego jest to, że właściwa lista ma właściwą listę jako cdr . W ten sposób lista typów danych (właściwa) jest definiowana w językach pisanych na maszynie. Jest to ogólna i rekurencyjna definicja typu: Część ogólna mówi, że pierwszy argument niepustego konstruktora listy (często nazywanego consBTW) może być dowolnego typu. Część rekurencyjna mówi, że jej drugim argumentem jest instancja typu (właściwa) Lista .

Tak, sprawdzasz, czy dana listplista (cdr (last XS))jest poprawna, używając nil. Aby sprawdzić, czy cdr samego crittera jest właściwą listą, musisz kontynuować sprawdzanie jego cdr, aż do końca - ostatnich wad, aby zobaczyć, czy tak jest nil. Możesz zdefiniować predykat dla tego w następujący sposób, jeśli chcesz:

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

Chociaż okrągła lista nie ma końca, Emacs (począwszy od Emacsa 24) jest wystarczająco inteligentny, aby sprawdzić lastpoprawnie, więc ten kod działa nawet w przypadku okrągłej listy (ale tylko w Emacsie 24.1 i nowszych; we wcześniejszych wersjach otrzymujesz „nieskończoną” rekurencję aż do przepełnienia stosu).

Możesz używać funkcji takich jak lengthtylko na odpowiednich listach i innych sekwencjach. Zobacz także funkcję safe-length.

Zobacz instrukcję Elisp, węzeł Cons Cells .

Jeśli chodzi o notację, (a b)to tylko cukier syntaktyczny (a . (b . nil))- patrz instrukcja Elisp, notacja pary kropkowanej

Rysował
źródło
Jaka jest najlepsza praktyka sprawdzania właściwej listy? Sprawdzanie, czy (cdr (last XS))jest nilto crumblesome. Czy nie ma takiej funkcji proper-list-p?
tom
@tom: To lub coś równoważnego jest konieczne - musisz sprawdzić ostatnią komórkę przeciw. Dodałem więcej na ten temat w odpowiedzi.
Drew
@Drew Chciałbym zmienić treść funkcji na (unless (atom x) (not (cdr (last x))))Tak, abyś mógł nawet zadzwonić (true-list-p "text")i nilnie dostać błędu.
tom
@tom: Right; dzięki. Właściwie należy to najpierw przetestować, aby upewnić się, że jest to minus lub nil(tj listp.). (Również FWIW, nie używać unlesslub whenich wartości zwracanej używam. and, orI ifza to.)
Drew