'(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、それを提供するために有用な十分であることが判明していなかったというだけの理由elispの中で。実際、リストが適切かどうかを最後にテストしたかったのを思い出せないので、ユースケースについてもう少し情報を提供していただければ、別の方法で問題を解決するのに役立ちます。
ステファン

@Stefan要するに、再帰的なmapcarを実装したいのです。指定されたリストの要素、リストの要素の要素、リストの要素の要素の要素などに対して、指定された関数を評価します。したがって、要素が真のリストであるかどうかを知る必要があります。
トム

たとえば、htmlを解析してlibxml-parse-html-regionすべての<td>タグを抽出したい場合に便利です。
トム

適切なリスト、不適切なリスト、または他の何かを取得する可能性があり、3つのケースを異なる方法で処理する必要がある具体的な例を示してください。私が対処しなければならなかったほとんどの場合、「適切な」および「不適切な」ケースは、実際の不適切なテールに到達するまで共有できるため、適切かどうかを再度テストする必要はありません。それはだconsp代わりに。
ステファン

1
libxmlはリストのリストを返すだけではありません。XML要素を表す各リストには、フォーム(シンボル属性。コンテンツ)があります。したがって、コードはリストのすべての要素にmapcarを再帰的に適用するのではなく、リストの要素にのみ適用する必要がありますcddr(要素名と属性をスキップするため)。これを行うと、すべてのリストが適切であり、問​​題が解消されることがわかります。またtdtd要素の属性を混同する可能性のあるコードのバグも修正します。
ステファン

回答:


22

を満たすlistpので、その意味ではリストです。 listp何かが短所かnil(別名())であるか、または他の何かであるかをテストするだけです。

適切なリストまたは真リスト(ドットリストや循環リストではありませんまたはリスト)があるものですlistpし、また持っているnilその最後のcdrとして。つまり、リストXSはifであれば適切(cdr (last XS))ですnil(そして、それがあなたがそれを区別する方法です)。

別の言い方をすれば、適切なリストにはcdrとして適切なリストがあるということです。これは、データ型(適切な)リストが型付き言語で定義される方法です。これは一般的かつ再帰的な型定義です。一般的な部分では、空でないリストコンストラクター(多くの場合cons、BTW と呼ばれる)への最初の引数は任意の型にできます。再帰部分は、2番目の引数が(適切な)List型のインスタンスであることを示しています。

はい、あなたが与えられたのかどうかを確認し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適切なリストや他のシーケンスでのみなどの機能を使用できます。functionも参照してくださいsafe-length

ElispマニュアルのノードCons Cellsを参照してください。

表記に関して(a b)は、単なる構文上の糖です(a . (b . nil))-Elispのマニュアル、ノードの点線ペア表記を参照してください


適切なリストを確認するためのベストプラクティスは何ですか?かどうかを確認するの(cdr (last XS))nil大変です。のような関数はありませんproper-list-pか?
トム

@tom:それ、または同等のものが必要です-最後のコンスセルを確認する必要があります。これについては、回答の詳細を追加しました。
ドリュー

@Drew関数の本体をに変更するので、(unless (atom x) (not (cdr (last x))))呼び出してエラー(true-list-p "text")を取得することさえできnilます。
トム

@tom:そうです; THX。実際には、最初にテストして、短所またはnil(つまりlistp)であることを確認する必要があります。(また、FWIW、私が使用していないunlessか、whenその戻り値のために私が使用しています。andor、およびifそのため。)
ドリュー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.