10

destructuring-bind の「未使用の値」に対して、erlang で _ のようなものを配置する方法はありますか?

たとえば、次のようなものがあります。

(destructuring-bind ((_SNIPPET
                               (_TITLE . title)
                               (_DESCRIPTION . description)
                               _RESOURCE-ID (_VIDEO-ID . video-id)))) entry
                (declare (ignore 
                          _SNIPPET _TITLE _DESCRIPTION _RESOURCE-ID _VIDEO-ID))
                (list video-id title description)))

未使用の値ごとに特定の変数を配置せず、次のように記述すると便利です。

 (destructuring-bind ((_
                         (_ . title)
                         (_ . description)
                         (_ (_ . video-id)))) entry
                    (list video-id title description)))

標準の destructuring-bind またはその他の標準のマクロでこのような動作を取得する方法はありますか? または、ML のようなパターン マッチング ライブラリを使用する必要があります。

4

2 に答える 2

10

では不可能DESTRUCTURING-BINDです(変数を複数回使用することはできません。コンパイラによっては文句を言うでしょう)。、、、などの変数を列挙できますが、_1それぞれ_2を無視する必要があります。

LOOP出来る:

CL-USER 23 > (loop for ((a b nil c) nil d) in '(((1 2 3 4) 5 6)
                                                ((1 2 3 4) 5 6))
                   collect (list a b c d))
((1 2 4 6) (1 2 4 6))

NILワイルドカード変数として使用されます。

LOOPマクロを再利用できます。

(defmacro match-bind (pattern object &body body)
  `(loop with ,pattern = ,object
         while nil
         finally (return (progn ,@body))))

CL-USER 37 > (match-bind ((a b nil c) nil d)
                 '((1 2 3 4) 5 6)
               (list a b c d))
(1 2 4 6)

LET-MATCH一部のライブラリの一部を使用できます。例: https://github.com/schani/clickr/blob/master/let-match.lisp おそらくもっと凝ったバージョンがあるでしょう。

于 2014-08-23T17:45:41.200 に答える
7

このために言語に組み込まれているものは何もありません。 Rainer Joswig's answerは、ループがある程度の破壊を行うことができることを指摘していますが、それほど多くのことを行うわけではありません。この回答の以前のバージョンでは、破壊ラムダ リストをトラバースし、_ で始まるすべてのシンボルのリストを収集し、それらの変数を無視する宣言をフォームに追加することを提案しました。より安全なバージョンは、それぞれを新しい変数に置き換え (変数が繰り返されないようにするため)、それらをすべて無視します。したがって、次のようなもの

(destructuring-bind (_a (_b c)) object
  c)

に展開します

(destructuring-bind (#:g1 (#:g2 c)) object
  (declare (ignore #:g1 #:g2))
  c)

このアプローチは、「 3.4.4.1.1 ラムダ リストによるデータ指向の破壊」で説明されている「データ指向」のみを使用している場合に問題なく機能します。ただし、「 3.4.4.1.2 ラムダ リストによるラムダ リスト指向の破壊」で説明されている「ラムダ リスト指向」アプローチを使用している場合は、&optional、&key などのラムダ リスト キーワードを使用できます。それらの一部の変数を置き換えてはならないため、はるかに複雑です。たとえば、

&optional (_x '_default-x)

後者はパターンではないため、_x何かに置き換えても問題ないかもしれませんが、そうではありません。_default-xしかし、Lisp ではコードはデータなので、destructuring-lambda-list をマップし、パターンである場所だけを置き換えるマクロを書くことができます。これは、まさにそれを行うやや毛むくじゃらのコードです。これは、関数と分解ラムダ リストを取り、引数の型 (全体、必須、オプションなど) と共に、ラムダ リスト内の各パターン変数に対して関数を呼び出します。

(defun map-dll (fn list)
  (let ((result '())
        (orig list)
        (keywords '(&allow-other-keys &aux &body
                    &key &optional &rest &whole)))
    (labels ((save (x)
               (push x result))
             (handle (type parameter)
               (etypecase parameter
                 (list (map-dll fn parameter))
                 (symbol (funcall fn type parameter)))))
      (macrolet ((parse-keyword ((&rest symbols) &body body)
                   `(progn
                      (when (and (not (atom list))
                                 (member (first list) ',symbols))
                        (save (pop list))
                        ,@body)))
                 (doparameters ((var) &body body)
                   `(do () ((or (atom list) (member (first list) keywords)))
                      (save (let ((,var (pop list)))
                              ,@body)))))
        (parse-keyword (&whole)
          (save (handle :whole (pop list))))
        (doparameters (required)
         (handle :required required))
        (parse-keyword (&optional)
         (doparameters (opt)
          (if (symbolp opt)
              (handle :optional opt)
              (list* (handle :optional (first opt)) (rest opt)))))
        (when (and (atom list) (not (null list))) ; turn (... . REST) 
          (setq list (list '&rest list)))         ; into (... &rest REST)
        (parse-keyword (&rest &body)
         (save (handle :rest (pop list))))
        (parse-keyword (&key)
         (doparameters (key)
          (if (symbolp key)
              (handle :key key)
              (destructuring-bind (keyspec . more) key
                (if (symbolp keyspec)
                    (list* (handle :key keyspec) more)
                    (destructuring-bind (keyword var) keyspec
                      (list* (list keyword (handle :key var)) more)))))))
        (parse-keyword (&allow-other-keys))
        (parse-keyword (&aux)
         (doparameters (aux) aux))
        (unless (null list)
          (error "Bad destructuring lambda list: ~A." orig))
        (nreverse result)))))

これを使用すると、_ で始まる各パターン変数を本体で無視される新しい変数に置き換える destructuring-bind* を簡単に作成できます。

(defmacro destructuring-bind* (lambda-list object &body body)
  (let* ((ignores '())
         (lambda-list (map-dll (lambda (type var)
                                 (declare (ignore type))
                                 (if (and (> (length (symbol-name var)) 0)
                                          (char= #\_ (char (symbol-name var) 0)))
                                     (let ((var (gensym)))
                                       (push var ignores)
                                       var)
                                     var))
                               lambda-list)))
    `(destructuring-bind ,lambda-list ,object
       (declare (ignore ,@(nreverse ignores)))
       ,@body)))

次に、それが生成する展開を見てみましょう。

(macroexpand-1
 '(destructuring-bind* (&whole (a _ . b)
                        c _ d
                        &optional e (f '_f)
                        &key g _h
                        &aux (_i '_j))
   object
   (list a b c d e f g)))
;=>                            
(DESTRUCTURING-BIND
    (&WHOLE (A #:G1041 &REST B) C #:G1042 D
     &OPTIONAL E (F '_F)
     &KEY G #:G1043
     &AUX (_I '_J))
    OBJECT
  (DECLARE (IGNORE #:G1041 #:G1042 #:G1043))
  (LIST A B C D E F G))

置き換えてはいけない場所 (init フォーム、aux 変数など) は置き換えていませんが、必要な場所は処理しています。あなたの例でもこの作品を見ることができます:

(macroexpand-1
 '(destructuring-bind* ((_ (_ . title)
                         (_ . description)
                         _
                         (_ . video-id)))
   entry
   (list video-id title description)))
;=>
(DESTRUCTURING-BIND ((#:G1044 (#:G1045 &REST TITLE)
                              (#:G1046 &REST DESCRIPTION)
                              #:G1047
                              (#:G1048 &REST VIDEO-ID)))
    ENTRY
  (DECLARE (IGNORE #:G1044 #:G1045 #:G1046 #:G1047 #:G1048))
  (LIST VIDEO-ID TITLE DESCRIPTION))
于 2014-08-23T17:48:12.410 に答える