プロパティリストに関数をマッピングしますか?


17

Q:プロパティリスト全体で関数をマップする慣用的な方法は何ですか?

さまざまなマッピング関数(mapcarおよびファミリ)は、リストなどのシーケンスに関数をマッピングします。プロパティリストを扱うとき、つまりリストに含まれる各プロパティ(最初の要素から始まる他のすべての要素)をマップしようとするとき、これらの関数をどのように使用しますか?マッピング関数は、個々の要素としてではなく、要素のペアでリストにアクセスする必要があるように思えます。

おもちゃの例として、プロパティリストを取得してすべてのプロパティ値を収集する方法を教えてください。代わりに関連付けリストである場合、それは非常に簡単です。

(mapcar #'cadr '((:prop1 a) (:prop2 b) (:prop3 c))) ;=> (a b c)

ループを使用してこれを実行できると確信していますが、少し面倒で、もっと慣用的な方法があるのではないかと思っています。


プロパティ値のみにマップするか(それがどのように聞こえるか、そしてmapcarリストの例に何をするか)、またはプロパティシンボルとプロパティ値のペアにマップするかどうかを明確にしてください。後者の方がより一般的です(より一般的には有用です)。
ドリュー

@ドリュー:私は一般的なケースにもっと興味があります。この例は、私が考えることができる最も簡単なものでした。プロパティ/値のペアをマッピングする方法を知りたいです。答えが「ループ」である場合、そのようになりますが、よりエレガントなソリューションがあるかどうか疑問に思っています。
ダン

あなたの例で持っているのはプロパティリストではありません。プロパティリストには偶数の要素があり、奇数の要素はプロパティ名、偶数の要素はプロパティ値です。あなたが持っているものは、おそらく、ツリー(リストのリスト)と呼ばれるでしょう。
wvxvw

回答:


8

さまざまなloop繰り返しの回答が得られるでしょう。私の知る限り、これを行うための慣用的な方法はありません。プロパティ値のみを蓄積することは非常に一般的ではないと思います(プロパティに関連付けない)。

これを行う簡単な方法の1つを次に示します。

(defun prop-values (plist)
  "..."
  (let ((pl    (cdr plist))
        (vals  ()))
    (while pl
      (push (car pl) vals)
      (setq pl  (cddr pl)))
    (nreverse vals)))

より一般的な場合、plistにバイナリ関数をマップする場合:

(defun map-plist (fn plist)
  "..."
  (let ((pl    plist)
        (vals  ()))
    (while pl
      (push (funcall fn (car pl) (cadr pl)) vals)
      (setq pl (cddr pl)))
    (nreverse vals)))

(setq foo '(a 1 b 2 c 3 d 4 e 5))

(map-plist #'cons foo) ; => ((a . 1) (b . 2) (c . 3) (d . 4) (e . 5))

13

これはおそらく状況に依存します。一般に、複数の値を複数の名前と結び付ける必要がある場合はハッシュテーブルを使用しますが、プロパティリストを使用する必要がある場合はを使用しますcl-loop。以下に例を示します。

(cl-loop for (key value) on '(:prop1 a :prop2 b :prop3 c) by 'cddr
         collect value)
;;; (a b c)

そして、あなたの例で示すデータ構造がある場合:

(cl-loop for (key value) in '((:prop1 a) (:prop2 b) (:prop3 c))
         collect value)
;;; (a b c)

5

私にとっての答えは、plistをalistに変えることです。これは同等のデータ構造で、深さが半分で操作が簡単です。


3
まあ、それは一種の答え(および良いアドバイス)ですが、コメントの多くです。
ドリュー

4

Emacs 25になる現在のGit HEADのEmacsでは、次のことができます。つまり、新しいseq-partition関数を使用してplistを簡単にalistに変換し、 standardを使用してalistエントリを処理します mapcar

(let* ((my-plist (list :a 1 :b 2 :c 3 :more (list 4 5 6)))
       (my-alist (seq-partition my-plist 2))
       (my-reverse-alist (mapcar (lambda (entry)
                                   (let ((prop (car entry))
                                         (val  (cadr entry)))
                                     (list val prop)))
                                 my-alist)))
  (message "my-plist: %s\nmy-alist: %s\nmy-reverse-alist: %s"
           my-plist my-alist my-reverse-alist))
;; my-plist: (:a 1 :b 2 :c 3 :more (4 5 6))
;; my-alist: ((:a 1) (:b 2) (:c 3) (:more (4 5 6)))
;; my-reverse-alist: ((1 :a) (2 :b) (3 :c) ((4 5 6) :more))

seq-partition使用してドキュメントをご覧くださいC-h f seq-partition RET


1

1つのアイデアは、リストの奇数の値からのみ変換を使用-map-indexeddashて適用することです。

(-non-nil (-map-indexed (lambda (index item) (when (oddp index) item))
  '(a x b y c z))) ; => (x y z)
(-non-nil (--map-indexed (when (oddp it-index) it) '(a x b y c z))) ; => (x y z)

別のアイデアは、ht<-plistfrom を使用してplistをハッシュテーブルに変換することhtです。

(ht-values (ht<-plist '(a x b y c z) 'equal)) ; (z y x)

ハッシュテーブルはアイテムの順序を保持しないことに注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.