Чи справді '(a. B) список?


15

Я справді плутаю .позначення. Чи '(a . b)є список?

(listp '(a . b))повертається, tале коли я хочу знати, його довжина (length '(a . b))видає помилку Wrong type argument: listp, b. Те саме стосується інших функцій, як nth,mapcarі т. Д. Всі вони дають однакову помилку

Чи є якась функція, яку я можу розрізняти '(a b)і '(a . b)?


Контекст: Я зіткнувся з цією проблемою, коли хотів реалізувати рекурсивну версію mapcar. Ось моя реалізація

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

Я використовую це для вилучення всіх конкретних тегів з розбору HTML. Приклад htmlрозбору

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

Тоді я витягую все <td>як

(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
В true-list-pЕліспі немає просто тому, що його не знайшли достатньо корисним для його надання. Дійсно, я не можу пригадати, коли я востаннє хотів перевірити, чи є список належним, тому, можливо, якщо ви дасте нам трохи більше інформації про ваше використання, ми можемо допомогти вам вирішити вашу проблему іншим способом.
Стефан

@ Stefan Коротше кажучи, я хочу реалізувати рекурсивну карту, я оцінюю задану функцію на елементах заданого списку, потім на елементах елементів списку, потім на елементах елементів елементів списку тощо. Тому мені потрібно знати, чи є елемент справжнім списком чи ні.
Tom

Це корисно, наприклад, коли я розбираю html libxml-parse-html-regionі хочу витягнути всі <td>теги.
Tom

Чи можете ви показати нам конкретний приклад, де ви можете отримати або належний список, або неналежний список, або щось інше, і де вам потрібно по-різному обробити 3 справи? У більшості випадків мені доводилося стикатися, "належні" та "неправильні" випадки можна ділитися, поки ми не дістанемося до фактичного неправильного хвоста, тож вам знову не потрібно перевіряти, чи правильно це чи ні: просто перевірити, чи це conspзамість цього.
Стефан

1
libxml не просто повертає списки списків списків. Кожен список, що представляє XML-елемент, має форму (атрибути символів. Вміст). Тож ваш код не повинен застосовувати mapcar рекурсивно над усіма елементами списків, лише над cddrсписком (щоб пропустити ім'я елемента та атрибути). Як тільки ви це зробите, ви повинні встановити, що всі списки є правильними і ваша проблема зникне. Він також виправить помилку у вашому коді, де ви можете заплутати tdатрибут для tdелемента.
Стефан

Відповіді:


22

Це задовольняє listp, тому в цьому сенсі це перелік. listpпросто перевіряє, чи є щось проти чи nil(ака ()), з одного боку, чи щось інше, з іншого.

Правильний список або істинний список (або список , яка не пунктирний список або циклічний список) є те , що , listpа також має nilв своїх останніх корду. Тобто, список XSє правильним, якщо (cdr (last XS))є nil(і саме так ви його відрізняєте).

Ще один спосіб це зробити - це те, що в правильному списку є власний список як його cdr . Це спосіб , тип даних (власне) Список визначається в типізованих мовах. Це загальне та рекурсивне визначення типу: Загальна частина говорить, що перший аргумент конструктору не порожнього списку (часто його називають consBTW) може бути будь-якого типу. Рекурсивна частина говорить про те, що другий її аргумент - це екземпляр типу (належного) списку .

Так, ви перевіряєте даність чи listpце правильний список , використовуючи (cdr (last XS))це nil. Для того, щоб перевірити, чи належним є список cdr у відповідному тексті, потрібно продовжити перевірку його cdr, до кінця - останні мінуси, щоб побачити, чи є він nil. Ви можете визначити присудок для цього наступним чином, якщо хочете:

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

Хоча круговий список не має кінця, Emacs (починаючи з Emacs 24) досить розумний, щоб перевірити lastправильно, тому цей код працює навіть для кругового списку (але лише для Emacs 24.1 і пізніших версій; для більш ранніх версій ви отримуєте "нескінченну" рекурсію до переповнення стека).

Ви можете використовувати такі функції, як lengthлише у правильних списках та інших послідовностях. Дивіться також функцію safe-length.

Дивіться посібник Elisp, вузол мінусів комірок .

Що стосується позначення, (a b)це просто синтаксичний цукор для (a . (b . nil))- дивіться посібник Elisp, вузол пунктирної нотатки про пару


Яка найкраща практика для перевірки відповідності списку? Перевірка того, чи (cdr (last XS))є nil, крихтим. Хіба немає такої функції, як proper-list-p?
tom

@tom: Це, або щось еквівалентне, потрібно - ви повинні перевірити останню клітинку мінусів. Про це я вже додав у відповіді зараз.
Дрю

@Drew Я змінив би тіло функції на (unless (atom x) (not (cdr (last x))))Так, ви навіть можете зателефонувати (true-list-p "text")і отримати nilпомилку.
tom

@tom: Правильно; Дякую. Насправді його слід перевірити спочатку, щоб переконатися, що це мінуси або nil(тобто listp). (Крім того , FWIW, я не використовую unlessабо whenдля значення, що повертається я використовую. and, orІ ifдля цього.)
Дрю
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.