Lisp tutorial for Pythonista.
Day #5 : LISP = LISt Processor




                                                         Ransui Iso
                          Strategic Technology Group, X-Listing Co, Ltd.
宿題は OK?
今日はリスト処理
 ちょっと深く潜る
リストの S 式表現と升・矢印表現

●    混乱したら図に立ち戻るとイイことがある
(defvar lst '(a (1 2) (3 . 4))


    lst


           A


                   1       2     3   4
練習問題

●   次の S 式表現のリストを升・矢印表現で書きなさい
((1 ((name "Cons Cell")
     (age 28)
     (likes (Python Lisp))))
 (2 ((name "Ham Spam") 
     (age 32)
     (likes ()))))


(defun fact(x)
  (if (= x 1) 1 (* x (fact (1­ x)))))
リスト操作と再帰

●       とても相性がイイ
         ●   何でもかんでも再帰ってのもアレゲだ
         ●   使い所の見極めが肝心

    –   例えばリストのトップレベルを反転する関数を考えてみる

        CL­USER> (my­reverse '(1 2 (3 4)))
        ((3 4) 2 1)
問題を単純化してみる

●   要素が2個の時
                      (cdr lst)
    lst


             1         2   (car (cdr lst))

          (car lst)


    lst                       演習問題:
                              上の情報を参考に、左のような
                              反転されたリストは cons 関数
             2         1      をつかってどうやって作成でき
                              るか?
任意の長さのリストの時は?

●   要素が2個の時はどうやったか?
     ●   リストの末尾の car 部分を使って新しい cons を作る
     ●   1 個前の cons セルの car 部分を car にもつ cons セル新しく作っ
           て上の cons セルにつなぐ

●   リストの末尾かどうかはどうやって判定する?
     ●   セルの cdr が nil かどうかを調べればいい
コードに書いてみる

●   cons セルを連結していく部分を書いてみる
(defun my­reverse (lst)
  (append (my­reverse (cdr lst)) (cons (car lst) nil)))
     ●   まだ不完全だよ!

     ●   append 関数:リストをつなぐ
          –   (append '(1 2) '(3 4)) → (1 2 3 4)
          –   (append '(1 2) '3) → (1 2 . 3)
          –   (append '(1 2) (cons 3 nil)) → (1 2 3)

              要するに、第一引数の最後の要素の cdr が第二引数を指すようになる
ケツに到達したら?

●   リストの末尾セルの car を使って cons セルを作る
(defun my­reverse (lst)
  (if (null (cdr lst))
      (cons (car lst) nil)
    (append (my­reverse (cdr lst)) 
            (cons (car lst) nil)))
                         分かりやすく cons とか冗長だけどこれでちゃんと動く

ループで書くとこんな感じ
(defun my­loop­reverse (lst)
  (let ((result nil))
    (dolist (x lst)
      (push x result))
    result))
                       問題が小さいから再帰のご利益が薄いけど、まぁ練習だ。
例題 :Maze Solver

●   迷路を解くよ
    0,0   1,0   2,0   3,0   ...
                                  問題の条件
    0,1         2,1

                                  1:迷路に閉路は無いものとして考える
    0,2
                                  2:スタート・ゴール地点は任意に与える
    0,3
                                  3:解は辿った座標のリストとする

    ...


                            4,5
迷路のモデルを考える
●   迷路をリストで表現する
          ●   ある座標に注目したときに移動できる座標のリストを作る
                                  (((1 1 NIL) ((1 2)))
    0,0   1,0   2,0   3,0   ...
                                   ((3 1 NIL) ((3 2)))
                                   ((5 1 NIL) ((5 2)))
    0,1         2,1
                                   ((1 2 NIL) ((1 3) (1 1)))
                                   ((3 2 NIL) ((3 3) (3 1)))
                                   ((5 2 NIL) ((5 3) (5 1)))
    0,2                            ((1 3 NIL) ((2 3) (1 2)))
                                   ((2 3 NIL) ((1 3) (3 3)))
                                   ((3 3 NIL) ((2 3) (3 4) (3 2)))
    0,3                            ((5 3 NIL) ((5 4) (5 2)))
                                   ((3 4 NIL) ((3 5) (4 4) (3 3)))
                                   ((4 4 NIL) ((3 4) (5 4)))
    ...                            ((5 4 NIL) ((4 4) (5 3)))
                                   ((1 5 NIL) ((1 6)))
                                   ((3 5 NIL) ((3 6) (3 4)))
                            4,5
                                   ((1 6 NIL) ((2 6) (1 5)))
                                   ((2 6 NIL) ((1 6) (3 6)))
                                   ((3 6 NIL) ((2 6) (4 6) (3 5)))
                                   ((4 6 NIL) ((3 6) (5 6)))
                                   ((5 6 NIL) ((4 6) (6 6)))
                                   ((6 6 NIL) ((5 6))))




1 エントリの構造:((x y mark) ((nx1 ny1) (nx2 ny2) ... ))
探索:まずはゴールの処理から
●   再帰は終了条件と終了時の戻り値が肝

    0,0   1,0   2,0   3,0   ...
                                              goal に辿り着いた
    0,1         2,1               移
                                  動                 戻り値
                                  先
    0,2                           座
                                  標
                                      現在の座標      goal の座標
    0,3
                                  移
                                  動
    ...
                                  先
                                  座
                            4,5   標
                                      現在の座標      先の戻り値



                                          以下続く
探索:行き止まりの時
●   ゴールじゃないけど戻らなきゃいけないときもある

    0,0   1,0   2,0   3,0   ...
                                       行き先が無い
    0,1         2,1               移
                                  動          戻り値
                                  先
    0,2                           座
                                  標
                                          nil
    0,3
                                  移
                                  動
    ...
                                  先
                                  座
                            4,5   標
                                          nil



                                      以下続く
探索:ストラテジ

–   現在位置に到達マークを付ける

–   if 現在位置 == ゴール:
     ●   return 現在位置

–   地図データより移動可能な隣接座標のリストを得る
     ●   if 移動可能隣接座標リスト == nil:
          –   return nil

     ●   for 移動先 in 移動可能隣接座標リスト :
          –   探索結果 ← 再帰: ( 移動先 , ゴール )
          –   if 探索結果 != nil:
                ●   return list( 現在位置 , 探索結果 )
          –   else:
                ●   return nil
移動可能位置をさがす関数

 ●   迷路のリスト表現は alist になっているので…
(defun get­movable­positions (maze­map current­position)
    (let ((result nil))
      (dolist (candidate­position 
                (cadr (assoc `(,@current­position t) maze­map :test #'equal)))
        (if (assoc `(,@candidate­position nil) maze­map :test #'equal)
            (push candidate­position result)))
      result))



 ●   「 ` 」:バッククォート
(defparameter lst '(hello world))
(defparameter x 10)
`(ham spam ,x ,lst ,@lst) → (ham spam 10 (hello world) hello world)

        ●   リストの中に要素を直接埋め込むことができる。
             –   「,」      :そのまま埋め込む
             –   「 ,@ 」   :リストを展開して埋め込む
             –
        ●   上手に使えば cons とか list 関数でリストを作成するよりも楽
プログラム全体

●   PasteBin 参照してね
     ●   http://pastebin.com/CamFYQf4

     ●   2次元配列の迷路からリスト形式のマップを作成する関数
          –   ループで一気に作ってます


●   発展問題
     ●   リスト形式の地図をつくりながら解くようにできるだろうか?
          –   最初に一気にリスト形式の地図をつくらない
          –   2次元配列の迷路を直接探索して解を見つけるにはどうすればいいか?

     ●   2次元配列形式の迷路を自動生成する関数を作れるか?
          –   生成された迷路は閉路の無いものであることを保証できるだろうか?
課題です!
動物当てゲーム Lisp Version
   問題そのものは OK だよね?
面倒な部分はコード差し上げます
●   PasteBin にあるです
     ●   http://pastebin.com/X8LgaWG2

●   ヒント
     ●   知識の木の構造を考えるとき
          –   まず最も単純な1段のみの構成を考える :
               ●   質問内容 , Yes の時の答え , No の時の答えをどうやってまとめるか

          –   それが入れ子になっても意味が通じるかを確認する

          –   末端が判定できるかを確認する

     ●   新規の知識を木に導入する方法を考える
          –   末端に到達した地点にいる状態のまま、新知識を木に導入する必要がある

          –   新知識はもともとの木に破壊的に追加して OK
               ●   setf  を使ってリストを強制的に書き換えちゃう

          –   このプログラムは戻り値の伝播を使う必要はない点に注意する

Lisp Tutorial for Pythonista : Day 5

  • 1.
    Lisp tutorial forPythonista. Day #5 : LISP = LISt Processor Ransui Iso Strategic Technology Group, X-Listing Co, Ltd.
  • 2.
  • 3.
  • 4.
    リストの S 式表現と升・矢印表現 ● 混乱したら図に立ち戻るとイイことがある (defvar lst '(a (1 2) (3 . 4)) lst A 1 2 3 4
  • 5.
    練習問題 ● 次の S 式表現のリストを升・矢印表現で書きなさい ((1 ((name "Cons Cell")      (age 28)      (likes (Python Lisp))))  (2 ((name "Ham Spam")       (age 32)      (likes ())))) (defun fact(x)   (if (= x 1) 1 (* x (fact (1­ x)))))
  • 6.
    リスト操作と再帰 ● とても相性がイイ ● 何でもかんでも再帰ってのもアレゲだ ● 使い所の見極めが肝心 – 例えばリストのトップレベルを反転する関数を考えてみる CL­USER> (my­reverse '(1 2 (3 4))) ((3 4) 2 1)
  • 7.
    問題を単純化してみる ● 要素が2個の時 (cdr lst) lst 1 2 (car (cdr lst)) (car lst) lst 演習問題: 上の情報を参考に、左のような 反転されたリストは cons 関数 2 1 をつかってどうやって作成でき るか?
  • 8.
    任意の長さのリストの時は? ● 要素が2個の時はどうやったか? ● リストの末尾の car 部分を使って新しい cons を作る ● 1 個前の cons セルの car 部分を car にもつ cons セル新しく作っ て上の cons セルにつなぐ ● リストの末尾かどうかはどうやって判定する? ● セルの cdr が nil かどうかを調べればいい
  • 9.
    コードに書いてみる ● cons セルを連結していく部分を書いてみる (defun my­reverse (lst)   (append (my­reverse (cdr lst)) (cons (car lst) nil))) ● まだ不完全だよ! ● append 関数:リストをつなぐ – (append '(1 2) '(3 4)) → (1 2 3 4) – (append '(1 2) '3) → (1 2 . 3) – (append '(1 2) (cons 3 nil)) → (1 2 3) 要するに、第一引数の最後の要素の cdr が第二引数を指すようになる
  • 10.
    ケツに到達したら? ● リストの末尾セルの car を使って cons セルを作る (defun my­reverse (lst)   (if (null (cdr lst))       (cons (car lst) nil)     (append (my­reverse (cdr lst))              (cons (car lst) nil))) 分かりやすく cons とか冗長だけどこれでちゃんと動く ループで書くとこんな感じ (defun my­loop­reverse (lst)   (let ((result nil))     (dolist (x lst)       (push x result))     result)) 問題が小さいから再帰のご利益が薄いけど、まぁ練習だ。
  • 11.
    例題 :Maze Solver ● 迷路を解くよ 0,0 1,0 2,0 3,0 ... 問題の条件 0,1 2,1 1:迷路に閉路は無いものとして考える 0,2 2:スタート・ゴール地点は任意に与える 0,3 3:解は辿った座標のリストとする ... 4,5
  • 12.
    迷路のモデルを考える ● 迷路をリストで表現する ● ある座標に注目したときに移動できる座標のリストを作る (((1 1 NIL) ((1 2))) 0,0 1,0 2,0 3,0 ...  ((3 1 NIL) ((3 2)))  ((5 1 NIL) ((5 2))) 0,1 2,1  ((1 2 NIL) ((1 3) (1 1)))  ((3 2 NIL) ((3 3) (3 1)))  ((5 2 NIL) ((5 3) (5 1))) 0,2  ((1 3 NIL) ((2 3) (1 2)))  ((2 3 NIL) ((1 3) (3 3)))  ((3 3 NIL) ((2 3) (3 4) (3 2))) 0,3  ((5 3 NIL) ((5 4) (5 2)))  ((3 4 NIL) ((3 5) (4 4) (3 3)))  ((4 4 NIL) ((3 4) (5 4))) ...  ((5 4 NIL) ((4 4) (5 3)))  ((1 5 NIL) ((1 6)))  ((3 5 NIL) ((3 6) (3 4))) 4,5  ((1 6 NIL) ((2 6) (1 5)))  ((2 6 NIL) ((1 6) (3 6)))  ((3 6 NIL) ((2 6) (4 6) (3 5)))  ((4 6 NIL) ((3 6) (5 6)))  ((5 6 NIL) ((4 6) (6 6)))  ((6 6 NIL) ((5 6)))) 1 エントリの構造:((x y mark) ((nx1 ny1) (nx2 ny2) ... ))
  • 13.
    探索:まずはゴールの処理から ● 再帰は終了条件と終了時の戻り値が肝 0,0 1,0 2,0 3,0 ... goal に辿り着いた 0,1 2,1 移 動 戻り値 先 0,2 座 標 現在の座標 goal の座標 0,3 移 動 ... 先 座 4,5 標 現在の座標 先の戻り値 以下続く
  • 14.
    探索:行き止まりの時 ● ゴールじゃないけど戻らなきゃいけないときもある 0,0 1,0 2,0 3,0 ... 行き先が無い 0,1 2,1 移 動 戻り値 先 0,2 座 標 nil 0,3 移 動 ... 先 座 4,5 標 nil 以下続く
  • 15.
    探索:ストラテジ – 現在位置に到達マークを付ける – if 現在位置 == ゴール: ● return 現在位置 – 地図データより移動可能な隣接座標のリストを得る ● if 移動可能隣接座標リスト == nil: – return nil ● for 移動先 in 移動可能隣接座標リスト : – 探索結果 ← 再帰: ( 移動先 , ゴール ) – if 探索結果 != nil: ● return list( 現在位置 , 探索結果 ) – else: ● return nil
  • 16.
    移動可能位置をさがす関数 ● 迷路のリスト表現は alist になっているので… (defun get­movable­positions (maze­map current­position)     (let ((result nil))       (dolist (candidate­position                  (cadr (assoc `(,@current­position t) maze­map :test #'equal)))         (if (assoc `(,@candidate­position nil) maze­map :test #'equal)             (push candidate­position result)))       result)) ● 「 ` 」:バッククォート (defparameter lst '(hello world)) (defparameter x 10) `(ham spam ,x ,lst ,@lst) → (ham spam 10 (hello world) hello world) ● リストの中に要素を直接埋め込むことができる。 – 「,」 :そのまま埋め込む – 「 ,@ 」 :リストを展開して埋め込む – ● 上手に使えば cons とか list 関数でリストを作成するよりも楽
  • 17.
    プログラム全体 ● PasteBin 参照してね ● http://pastebin.com/CamFYQf4 ● 2次元配列の迷路からリスト形式のマップを作成する関数 – ループで一気に作ってます ● 発展問題 ● リスト形式の地図をつくりながら解くようにできるだろうか? – 最初に一気にリスト形式の地図をつくらない – 2次元配列の迷路を直接探索して解を見つけるにはどうすればいいか? ● 2次元配列形式の迷路を自動生成する関数を作れるか? – 生成された迷路は閉路の無いものであることを保証できるだろうか?
  • 18.
  • 19.
    動物当てゲーム Lisp Version 問題そのものは OK だよね?
  • 20.
    面倒な部分はコード差し上げます ● PasteBin にあるです ● http://pastebin.com/X8LgaWG2 ● ヒント ● 知識の木の構造を考えるとき – まず最も単純な1段のみの構成を考える : ● 質問内容 , Yes の時の答え , No の時の答えをどうやってまとめるか – それが入れ子になっても意味が通じるかを確認する – 末端が判定できるかを確認する ● 新規の知識を木に導入する方法を考える – 末端に到達した地点にいる状態のまま、新知識を木に導入する必要がある – 新知識はもともとの木に破壊的に追加して OK ● setf  を使ってリストを強制的に書き換えちゃう – このプログラムは戻り値の伝播を使う必要はない点に注意する