Onlisp

18章「分配」

18.1リストに対する分配
  • ok
18.2他の構造
> (dbind (a b c) #(1 2 3)
        (list a b c))
(1 2 3)
> (dbind (a (b c) d) '( 1 #(2 3) 4)
        (list a b c d))
(1 2 3 4)
> (dbind (a (b . c) &rest d) '(1 "fribble" 2 3 4)
        (list a b c d))
(1 #\f "ribble" (2 3 4))
  • 少し難しくなってきたので、自分で実装してみよう。
  • 以下のようになればいい。
> (destruc '(a b c) 'seq #'atom)
((A (ELT SEQ 0)) (B (ELT SEQ 1)) (C (ELT SEQ 2)))

> (destruc '(a (b . c) &rest d) 'seq)
((A (ELT SEQ 0))
 ((#:G2 (ELT SEQ 1)) (B (ELT #:G2 0)) (C (SUBSEQ #:G2 1)))
 (D (SUBSEQ SEQ 2)))

> (dbind-ex (destruc '(a (b . c) &rest d) 'seq) '(body))
(LET ((A (ELT SEQ 0))
      (#:G4 (ELT SEQ 1))
      (D (SUBSEQ SEQ 2)))
  (LET ((B (ELT #:G4 0))
        (C (SUBSEQ #:G4 1)))
    (PROGN BODY)))
  • 結果
(defun destruc2 (bind seq &optional (n 0) restp)
  (when bind
    (when (atom bind)
      (setq bind (list bind) restp t))
    (let ((sym (car bind)))
      (remove t
       `( ,(if (atom sym)
               (if (member sym '(&rest &body))
                   (setq n (1- n) restp t) ;skipping this time
                   `(,sym (,(if restp 'subseq 'elt) ,seq ,n)))
               (let ((new-seq (gensym)))
                 `((,new-seq (elt ,seq ,n))
                   ,@(destruc2 sym new-seq))))
           ,@(destruc2 (cdr bind) seq (1+ n) restp))))))

(defun dbind-ex2 (binds body)
  (if binds
      (let (here-binds under-binds)
        (dolist (bind binds) 
          (if (atom (car bind))
              (setq here-binds (cons bind here-binds))
              (setq here-binds (cons (car bind) here-binds)
                    under-binds (nconc under-binds (cdr bind)))))
        `(let ,(nreverse here-binds)
           ,(dbind-ex2 under-binds body)))
      `(progn ,@body)))
  • できた。子一時間かかってしまった。
> (destruc2 '(a b c) 'seq)
  ((A (ELT SEQ 0)) (B (ELT SEQ 1)) (C (ELT SEQ 2)))

> (destruc2 '(a (b . c) &rest d) 'seq)
  ((A (ELT SEQ 0))
   ((#:G849 (ELT SEQ 1)) (B (ELT #:G849 0)) (C (SUBSEQ #:G849 1)))
   (D (SUBSEQ SEQ 2)))

> (dbind-ex2 (destruc2 '(a (b . c) &rest d) 'seq) '(body))
  (LET ((A (ELT SEQ 0)) (#:G850 (ELT SEQ 1)) (D (SUBSEQ SEQ 2)))
    (LET ((B (ELT #:G850 0)) (C (SUBSEQ #:G850 1)))
      (PROGN BODY)))
  • うまくいっているようだ。
> (defmacro dbind2 (pat seq &body body)
    (let ((gseq (gensym)))
      `(let ((,gseq ,seq))
         ,(dbind-ex2 (destruc2 pat gseq) body))))

> (dbind2 (a b c) #(1 2 3)
    (list a b c))
; (1 2 3)

> (dbind2 (a (b c) d) '( 1 #(2 3) 4)
    (list a b c d))
; (1 2 3 4)

> (dbind2 (a (b . c) &rest d) '(1 "fribble" 2 3 4)
    (list a b c d))
; (1 #\f "ribble" (2 3 4))
  • ok!
  • ちなみに、このままだと
> (dbind2 (a b c) (list 1 2))
>>Error
  • となってしまうが、safe-eltとsafe-subseqというマクロを作ってやれば回避できる。
(defmacro safe-elt (sequence index)
  `(if (> (length ,sequence) ,index)
       (elt ,sequence ,index)))

(defmacro safe-elt2 (sequence index)
  `(if (> (length ,sequence) ,index)
       (elt ,sequence ,index)
       (when (typep ,sequence 'string)
         #\Nul)))

(defmacro safe-subseq (sequence start &optional end)
  `(let ((ret (safe-subseq2 ,sequence ,start ,end)))
     (unless (or (null ret) (equalp ret #()) (equal ret ""))
       ret)))

(defmacro safe-subseq2 (sequence start &optional end)
  (with-gensyms (seq len)
    `(let* ((,seq ,sequence)
            (,len (length ,seq)))
       (subseq ,seq
               (min ,start ,len)
               ,(if end `(min ,len ,end))))))
  • destruc2のeltとsubseqを置き換えてつかう。
  • safe-eltとsafe-subseqはマッチしない変数にはすべてnilを束縛する。
  • safe-elt2とsafe-subseq2はマッチしない変数にはその場のタイプに対応したnull-valueを束縛する。
;destruc2のeltとsubseqを置き換える。

;safe-eltとsafe-subseqを使った場合
> (dbind2 (a (b c d) (e f . g) h) '(0 "ab" "cd") (list a b c d e f g h))
  (0 #\a #\b NIL #\c #\d NIL NIL)

;safe-elt2とsafe-subseq2を使った場合
> (dbind2 (a (b c d) (e f . g) h) '(0 "ab" "cd") (list a b c d e f g h))
  (0 #\a #\b #\Nul #\c #\d "" NIL) ;対応したNull-value #\Nulや""などが束縛される。
18.3 参照
  • 上の参照を取るバージョン。
    • ok
18.4 マッチング
  • 最後の高速なマッチングオペレーターについては、すごいんだが、コードが複雑すぎでちょっと理解できていない。
    • あとで復習する。
;この式が
(if-match (?x (1 . ?y) . ?x) '((a b) #(1 2 3) a b)
  (values ?x ?y))

;こうなる…
(LET ((?Y '#:G924) (?X '#:G925))
  (LABELS ((#:G927 ()
             NIL))
    (LET ((#:G926 '((A B) #(1 2 3) A B)))
      (IF (AND (TYPEP #:G926 'SEQUENCE) (> (LENGTH #:G926) 1))
          (LET ((#:G931 (ELT #:G926 0)))
            (IF (OR (GENSYM? ?X) (EQUAL ?X #:G931))
                (LET ((?X #:G931))
                  (LET ((#:G928 (ELT #:G926 1)))
                    (IF (AND (TYPEP #:G928 'SEQUENCE) (> (LENGTH #:G928) 0))
                        (IF (EQUAL 1 (ELT #:G928 0))
                            (LET ((#:G930 (SUBSEQ #:G928 1)))
                              (IF (OR (GENSYM? ?Y) (EQUAL ?Y #:G930))
                                  (LET ((?Y #:G930))
                                    (LET ((#:G929 (SUBSEQ #:G926 2)))
                                      (IF (OR (GENSYM? ?X) (EQUAL ?X #:G929))
                                          (LET ((?X #:G929))
                                            (VALUES ?X ?Y))
                                          (#:G927))))
                                  (#:G927)))
                            (#:G927))
                        (#:G927))))
                (#:G927)))
          (#:G927)))))