kikkiiのブログ

ひきこもり

Common Lispで二分木・白黒木

昨日はHaskellで二分探索木の勉強をした。

kikkii.hatenablog.com

Common Lispでもそれと同じようなことをしている人を見つけた。

qiita.com

ありがたい。浅くしかコードを読んでいないが、コピー・アンド・ペーストして実行することで、溜飲を下げる。


(ql:quickload :optima)
(use-package :optima)

(defstruct (leaf (:constructor leaf)))
(defstruct (node (:constructor node (color left label right)))
  color left label right)

(defun rb-member (x tree)
  (match tree
    ((leaf) nil)
    ((node left label right)
     (cond ((< x label) (rb-member x left))
           ((> x label) (rb-member x right))
           (t           t)))))

(defun red (left label right)
  (node :red left label right))
(defun black (left label right)
  (node :black left label right))
(defpattern red (left label right)
  `(node (color :red) (left,left) (label ,label) (right ,right)))
(defpattern black (left label right)
  `(node (color :black) (left,left) (label ,label) (right ,right)))

(defun balance (tree)
  (match tree
    ((or (black (red (red a x b) y c) z d)
         (black (red a x (red b y c)) z d)
         (black a x (red (red b y c) z d))
         (black a x (red b y (red c z d))))
     (red (black a x b) y (black c z d)))
    (otherwise tree)))

(defun rb-insert (x tree)
  (labels ((ins (tree)
           (match tree
             ((leaf) (red (leaf) x (leaf)))
             ((node color left label right)
              (cond ((< x label)
                     (balance (node color (ins left) label right)))
                    ((> x label)
                     (balance (node color left label (ins right))))
                    (t tree))))))
    (match (ins tree)
      ((node left label right)
       (black left label right)))))

上が書かれたファイルをロードしてからREPLで下のようにやってみる。


CL-USER> (setq tree (leaf))
#S(LEAF)
CL-USER> (mapcar (lambda (xx) (setq tree (rb-insert xx tree))) '(8 6 4 1 7 3 5))
(#S(NODE :COLOR :BLACK :LEFT #S(LEAF) :LABEL 8 :RIGHT #S(LEAF))
 #S(NODE
    :COLOR :BLACK
    :LEFT #S(NODE :COLOR :RED :LEFT #S(LEAF) :LABEL 6 :RIGHT #S(LEAF))
    :LABEL 8
    :RIGHT #S(LEAF))
 #S(NODE
    :COLOR :BLACK
    :LEFT #S(NODE :COLOR :BLACK :LEFT #S(LEAF) :LABEL 4 :RIGHT #S(LEAF))
    :LABEL 6
    :RIGHT #S(NODE :COLOR :BLACK :LEFT #S(LEAF) :LABEL 8 :RIGHT #S(LEAF)))
 #S(NODE
    :COLOR :BLACK
    :LEFT #S(NODE
             :COLOR :BLACK
             :LEFT #S(NODE :COLOR :RED :LEFT #S(LEAF) :LABEL 1 :RIGHT #S(LEAF))
             :LABEL 4
             :RIGHT #S(LEAF))
    :LABEL 6
    :RIGHT #S(NODE :COLOR :BLACK :LEFT #S(LEAF) :LABEL 8 :RIGHT #S(LEAF)))
 #S(NODE
    :COLOR :BLACK
    :LEFT #S(NODE
             :COLOR :BLACK
             :LEFT #S(NODE :COLOR :RED :LEFT #S(LEAF) :LABEL 1 :RIGHT #S(LEAF))
             :LABEL 4
             :RIGHT #S(LEAF))
    :LABEL 6
    :RIGHT #S(NODE
              :COLOR :BLACK
              :LEFT #S(NODE
                       :COLOR :RED
                       :LEFT #S(LEAF)
                       :LABEL 7
                       :RIGHT #S(LEAF))
              :LABEL 8
              :RIGHT #S(LEAF)))
 #S(NODE
    :COLOR :BLACK
    :LEFT #S(NODE
             :COLOR :RED
             :LEFT #S(NODE
                      :COLOR :BLACK
                      :LEFT #S(LEAF)
                      :LABEL 1
                      :RIGHT #S(LEAF))
             :LABEL 3
             :RIGHT #S(NODE
                       :COLOR :BLACK
                       :LEFT #S(LEAF)
                       :LABEL 4
                       :RIGHT #S(LEAF)))
    :LABEL 6
    :RIGHT #S(NODE
              :COLOR :BLACK
              :LEFT #S(NODE
                       :COLOR :RED
                       :LEFT #S(LEAF)
                       :LABEL 7
                       :RIGHT #S(LEAF))
              :LABEL 8
              :RIGHT #S(LEAF)))
 #S(NODE
    :COLOR :BLACK
    :LEFT #S(NODE
             :COLOR :RED
             :LEFT #S(NODE
                      :COLOR :BLACK
                      :LEFT #S(LEAF)
                      :LABEL 1
                      :RIGHT #S(LEAF))
             :LABEL 3
             :RIGHT #S(NODE
                       :COLOR :BLACK
                       :LEFT #S(LEAF)
                       :LABEL 4
                       :RIGHT #S(NODE
                                 :COLOR :RED
                                 :LEFT #S(LEAF)
                                 :LABEL 5
                                 :RIGHT #S(LEAF))))
    :LABEL 6
    :RIGHT #S(NODE
              :COLOR :BLACK
              :LEFT #S(NODE
                       :COLOR :RED
                       :LEFT #S(LEAF)
                       :LABEL 7
                       :RIGHT #S(LEAF))
              :LABEL 8
              :RIGHT #S(LEAF))))

CL-USER> tree
#S(NODE
   :COLOR :BLACK
   :LEFT #S(NODE
            :COLOR :RED
            :LEFT #S(NODE
                     :COLOR :BLACK
                     :LEFT #S(LEAF)
                     :LABEL 1
                     :RIGHT #S(LEAF))
            :LABEL 3
            :RIGHT #S(NODE
                      :COLOR :BLACK
                      :LEFT #S(LEAF)
                      :LABEL 4
                      :RIGHT #S(NODE
                                :COLOR :RED
                                :LEFT #S(LEAF)
                                :LABEL 5
                                :RIGHT #S(LEAF))))
   :LABEL 6
   :RIGHT #S(NODE
             :COLOR :BLACK
             :LEFT #S(NODE :COLOR :RED :LEFT #S(LEAF) :LABEL 7 :RIGHT #S(LEAF))
             :LABEL 8
             :RIGHT #S(LEAF)))


CL-USER> (rb-member 8 tree)
T
CL-USER> (rb-member 100 tree)
NIL
CL-USER> (rb-member 1 tree)
T
CL-USER> (rb-member 10 tree)
NIL
CL-USER> (mapcar (lambda (xx) (rb-member xx tree)) '(1 2 3 4 5 6 7 8 9 10))
(T NIL T T T T T T NIL NIL)

Common Lispの方のはごちゃごちゃしていて、Haskellの方をやっていなかったら、すごく理解しにくかったと思う。わかっていても (mapcar (lambda (xx) (setq tree (rb-insert xx tree))) '(8 6 4 1 7 3 5)) の後のは目が滑って迷子になる。