Lisp in One Day 2007 November に参加しました

昨日は、(株)数理システム主催のLisp in One Day 2007 Novemberに参加してきました。

講師は、高名な 黒田寿男 さんです。

GaucheNightの座談会で初めて黒田さんを知ったのですが、その明快な発言は鮮明に記憶に残っていたので迷わず今回のセミナーに応募したのでした ^^);

内容はLisp初心者向けのものですが、端々に黒田さんの経験、思想が語られていてとても有意義なセミナーでした。

メモ
  • ポールグラハムはよく Common Lisp(以下CL)がわかってない! CL で Schemeのようなコードを書いている
  • CLにはたくさんの関数があるので、Specは頻繁に目を通し、どんな関数があったかだけでも覚えること
  • Scheme と CL は別のもの !!
  • CLも自然言語と同じように、文法以外に使い回しというものがある
    • cond, if, when, unless の使い分けなど
  • (and (< a b) (< b c)) などと書くのは素人、プロなら (< a b c)

一通りの Lisp の説明が終わった後に出てくる例題が 非常に Lisp 的で素敵だったのでコードだけ書いておきます。

例題は、英語の文法(のサブセット)に沿ったランダムな英語を発生させるプログラムです。

まずは、素直に文法をコード化した例
(defun sentence ()
  (append (noun-phrase) (verb-phrase)))

(defun noun-phrase ()
  (append (Article) (Noun)))

(defun verb-phrase ()
  (append (Verb) (noun-phrase)))

(defun Article ()
  (one-of '(the a)))

(defun Noun ()
  (one-of '(man ball woman table)))

(defun Verb ()
  (one-of '(hit took saw liked)))

(defun one-of (set)
  (list (random-elt set)))

(defun random-elt (choices)
  (elt choices (random (length choices))))

(sentence) を実行すると、英文が作成されます。

このプログラムは素直で良いのですが、文法を追加し複雑な英文が作成できるようにするとコードもどんどん複雑化して行きます。

DSL

そこで、Domain Specific Language を使った例、 DSL というと最近では Ruby が有名ですが 実は Lisp が先祖(?)
しかも DSL にS式を使う事で簡単!

(defparameter *simple-grammar*
    '((sentence -> (noun-phrase verb-phrase)) 
      (noun-phrase -> (Article Noun)) 
      (verb-phrase -> (Verb noun-phrase)) 
      (Article -> the a) 
      (Noun -> man ball woman table) 
      (Verb -> hit took saw liked)))

(defvar *grammar* *simple-grammar*)

(defun rule-lhs (rule)
  (first rule))

(defun rule-rhs (rule)
  (rest (rest rule)))

(defun rewrites (category)
  (rule-rhs (assoc category *grammar*)))

(defun generate (phrase)
  (cond ((listp phrase)   
         (mappend #'generate phrase))
        ((rewrites phrase) 
         (generate (random-elt (rewrites phrase))))
        (t    
         (list phrase))))

(defun mappend (fn the-list)
  "Apply fn to each element of list and append the results."
  (if (null the-list)
      nil
    (append (funcall fn (first the-list))
            (mappend fn (rest the-list)))))

DSL版だと 定義を変えるだけで、簡単に文法を追加できます ^^)/

(defparameter *bigger-grammar*
    '((sentence -> (noun-phrase verb-phrase)) 
      (noun-phrase -> (Article Adj* Noun PP*) (Name) (Pronoun))  
      (verb-phrase -> (Verb noun-phrase PP*)) 
      (PP* -> () (PP PP*))  
      (Adj* -> () (Adj Adj*)) 
      (PP -> (Prep noun-phrase))  
      (Prep -> to in by with on) 
      (Adj -> big little blue green adiabatic)  
      (Article -> the a) 
      (Name -> Pat Kim Lee Terry Robin)  
      (Noun -> man ball woman table) 
      (Verb -> hit took saw liked)  
      (Pronoun -> he she it these those that))) 
(setq *grammar* *simple-grammar*)