[ 目次, 前節, 次節, 索引 ]

4.3.2 非決定性プログラムの例



4.3.3節ではamb評価器の実装を記述する. しかしまずそれをどう使うか, 例をいくつか示す. 非決定性プログラムの長所は, 探索の仕方の細部を押し隠すことが出来, われわれのプログラムをより高い 抽象レベルで表現出来ることである.
論理パズル
次の(Dinesman 1968からとった)パズルは, たくさんある単純な論理パズルの代表である:
Baker, Cooper, Fletcher, MillerとSmithは五階建てアパートの異る階に住んでいる. Bakerは最上階に住むのではない. Cooperは最下階に住むのではない. Fletcherは最上階にも最下階にも住むのではない. MillerはCooperより上の階に住んでいる. SmithはFletcherの隣の階に住むのではない. FletcherはCooperの隣の階に住むのではない. それぞれはどの階に住んでいるか.

   すべての可能性を数え上げ, 与えられた制限を課すことで直截的な方法により誰がどの階に住むか決めることが出来る:~48


(define (multiple-dwelling)
  (let ((baker (amb 1 2 3 4 5))
        (cooper (amb 1 2 3 4 5))
        (fletcher (amb 1 2 3 4 5))
        (miller (amb 1 2 3 4 5))
        (smith (amb 1 2 3 4 5)))
    (require
     (distinct? (list baker cooper fletcher miller smith)))
    (require (not (= baker 5)))
    (require (not (= cooper 1)))
    (require (not (= fletcher 5)))
    (require (not (= fletcher 1)))
    (require (> miller cooper))
    (require (not (= (abs (- smith fletcher)) 1)))
    (require (not (= (abs (- fletcher cooper)) 1)))
    (list (list 'baker baker)
          (list 'cooper cooper)
          (list 'fletcher fletcher)
          (list 'miller miller)
          (list 'smith smith))))

   式(multiple-dwelling)を評価すると, 結果

((baker 3) (cooper 2) (fletcher 4) (miller 5) (smith 1))
が出来る. この単純な手続きは働くが非常に遅い. 問題4.39と4.40は改良の可能性を論じる.

問題 4.38


多住居手続きを修正し, SmithとFletcherが相隣る階に住むのでないとの制限を除去する. この修正パズルはに何組の解が存在するか.

問題 4.39


多住居手続きの制限の順序は解に影響するだろうか. 解を見出す時間に影響するだろうか. 影響すると考えるなら, 与えられたプログラムから制限を並べ変えて得られた, より速いプログラムを示せ. 影響しないと考えるなら, 論点を示せ.

問題 4.40


多住居の問題で, 人の階への割当ての組は, 階の割当てが相異るという要求をする前と後では, いくつか. 人の階へのすべての可能な割当てを生成し, それからそれを消すのをバックトラックに委せるのは非常に非効率である. 例えば制限の殆んどは人と階の変数の一つか二つにしか依存しない. 従って人に対して階を選択する前にその制限を課すことが出来る. 前段の制限で既に除外されたのではない可能性だけの生成を基にして, この問題を解く, 遥かに効率的な非決定性手続きを書いて示せ. (ヒント: let式の入れ子が必要である.)

問題 4.41


多住居パズルを解く通常のSchemeプログラムを書け.

問題 4.42


(Phillips 1934にある)次の「嘘つき」パズルを解け:
五人の女子生徒が試験を受けている. 彼らの両親は結果に対し過度の関心を持っている--- と彼らは考えている. そこで彼らは自宅へ試験について手紙を書くのに, 誰もが一つの正しい情報と一つの嘘の情報を書こうと約束した. 次は彼らの手紙の関係する部分である:

• Betty: 「Kittyは試験が二番で私は三番でした.」

• Ethel: 「私がトップと聞いてうれしいでしょう. Joanが二番でした.」

• Joan: 「私は三番でした. 可哀想なEthelはビリでした.」

• Kitty: 「私は二番になりました. Maryは四番でしかありませんでした.」

• Mary: 「私は四番でした. トップの座はBettyがとりました.」

五人の女子生徒の本当の順番はどうか.


問題 4.43


amb評価器を使い, 次のパズルを解け:49
Mary Ann Mooreの父親はヨットを持っている. また彼の四人の友人: Downing大佐. Hall氏, Barnacle Hood卿, Parker博士のそれぞれも持っている. この五人のそれぞれはまた一人ずつ娘を持っていて, それぞれ自分のヨットに他の誰かの娘の名前をつけた. Barnacle卿のヨットはGabrielleであり, Moore氏はLornaを持っている; Hall氏はRosalind. Downing大佐所有のMelissaはBarnacle卿の娘の名前をとった. Gabrielleの父親はParker博士の娘の名前をつけたヨットを持っている. Lornaの父親は誰か.
効率よく走るプログラムを書くように試みよ(問題4.40参照). Mary Annの姓がMooreだといわれなければ, 解がいくつになるか決定せよ.

問題 4.44


問題2.42はチェス盤の上にどの二つのクイーンも相手がとれないようにクイーンを置く「エイトクイーンパズル」を説明した. このパズルを解く非決定性プログラムを書け.
自然言語の構文解析
自然言語を入力として受け入れるよう設計されたプログラムは, 通常は入力を構文解析(parse)しようとする, つまり入力をある文法構造に対して一致させようとすることから始める. 例えば「The cat eats」のように, 冠詞に続いて名詞があり, それに続いて動詞があるような単純な文を認識しようとする. この解析を達成するには, 各語の品詞が認識出来なければならない. 各語を分類するリストから始めよう:50

(define nouns '(noun student professor cat class))


(define verbs '(verb studies lectures eats sleeps))


(define articles '(article the a))
また 文法(grammar), つまり文法要素をより単純な要素からどう構成するかの一連の規則が必要である. 非常に単純な文法は文は常に二つの部分---名詞句とそれに続く動詞---からなり, 名詞句は冠詞とそれに続く名詞からなると規定するかも知れない. この文法では, 文「The cat eats」は次のように構文解析される:
(sentence (noun-phrase (article the) (noun cat))
          (verb eats))

   このような構文解析は, 各文法規則に対しそれぞれの手続きを持つ単純なプログラムで生成出来る. 文を構文解析するには, その二つの構成部分を認識し, 記号sentenceのタグをつけた, その二つの要素のリストを返す:

(define (parse-sentence)
  (list 'sentence
         (parse-noun-phrase)
         (parse-word verbs)))
名詞句も同様に冠詞に続く名詞を見出すことで構文解析出来る:
(define (parse-noun-phrase)
  (list 'noun-phrase
        (parse-word articles)
        (parse-word nouns)))

   一番下のレベルでは, 構文解析は煮詰められ, 次の未解析の語は要求された品詞の語のリストのメンバーであるかの繰り返しチェックになる. この実装には未解析の入力である大域変数*unparsed*を維持する. 語をチェックする度, *unparsed*が空でなく, それが指示したリストにある語で始っていることを要求する. そうであればその語を*unparsed*から削除し, その語を品詞と共に返す(品詞はリストの先頭にある):51

(define (parse-word word-list)
  (require (not (null? *unparsed*)))
  (require (memq (car *unparsed*) (cdr word-list)))
  (let ((found-word (car *unparsed*)))
    (set! *unparsed* (cdr *unparsed*))
    (list (car word-list) found-word)))

   構文解析を開始するには, *unparsed*に入力文全体をおき, 文の構文解析を試み, 何も残っていないことをチェックする:

(define *unparsed* '())


(define (parse input)
  (set! *unparsed* input)
  (let ((sent (parse-sentence)))
    (require (null? *unparsed*))
    sent))

   これで構文解析器を試み, 単純なテスト文には働くことが確認出来る:

;;; Amb-Eval input:
(parse '(the cat eats))
;;; Starting a new problem
;;; Amb-Eval value:
(sentence (noun-phrase (article the) (noun cat)) (verb eats))

   requireの助けがあれば, 構文解析の制約を表現するのが便利なので, ここではamb評価器は有用である. しかし自動探索とバックトラックは, 要素のいろいろな分解法が出てくる更に複雑な文法を考える時に本当に有効である.

   文法に前置詞のリスト


(define prepositions '(prep for to in by with))
を追加し, (例えば「for the cat」のような)前置詞句を前置詞とこれに続く名詞句と定義しよう:
(define (parse-prepositional-phrase)
  (list 'prep-phrase
        (parse-word prepositions)
        (parse-noun-phrase)))
これで文は名詞句に続く動詞句であり, 動詞句は動詞または前置詞句で拡大した動詞句であると定義出来る:52
(define (parse-sentence)
  (list 'sentence
         (parse-noun-phrase)
         (parse-verb-phrase)))

(define (parse-verb-phrase)
  (define (maybe-extend verb-phrase)
    (amb verb-phrase
         (maybe-extend (list 'verb-phrase
                             verb-phrase
                             (parse-prepositional-phrase)))))
  (maybe-extend (parse-word verbs)))

   ついでに名詞句の定義を改善し, 「a cat in the class」のようなものも許すように出来る. これまで名詞句と呼んでいたものは, 単純名詞句と呼ぶことにし, 名詞句は単純名詞句か前置詞句で拡大した名詞句とする:

(define (parse-simple-noun-phrase)
  (list 'simple-noun-phrase
        (parse-word articles)
        (parse-word nouns)))

(define (parse-noun-phrase)
  (define (maybe-extend noun-phrase)
    (amb noun-phrase
         (maybe-extend (list 'noun-phrase
                             noun-phrase
                             (parse-prepositional-phrase)))))
  (maybe-extend (parse-simple-noun-phrase)))

   新しい文法は更に複雑な文の構文解析が出来る. 例えば

(parse '(the student with the cat sleeps in the class))
(sentence
 (noun-phrase
  (simple-noun-phrase (article the) (noun student))
  (prep-phrase (prep with)
               (simple-noun-phrase
                (article the) (noun cat))))
 (verb-phrase
  (verb sleeps)
  (prep-phrase (prep in)
               (simple-noun-phrase
                (article the) (noun class)))))
を生じる.

   ある入力は一つを超えた合法的構文解析を持つかも知れない. 「The professor lectures to the student with the cat」という文では, 教授が猫をつれて講義しているかも知れず, あるいは学生が猫をつれているかも知れない. われわれの非決定性プログラムは両方の可能性を見出す:

(parse '(the professor lectures to the student with the cat))
(sentence
 (simple-noun-phrase (article the) (noun professor))
 (verb-phrase
  (verb-phrase
   (verb lectures)
   (prep-phrase (prep to)
                (simple-noun-phrase
                 (article the) (noun student))))
  (prep-phrase (prep with)
               (simple-noun-phrase
                (article the) (noun cat)))))
を生じる. 評価器に再実行を頼むと
(sentence
 (simple-noun-phrase (article the) (noun professor))
 (verb-phrase
  (verb lectures)
  (prep-phrase (prep to)
               (noun-phrase
                (simple-noun-phrase
                 (article the) (noun student))
                (prep-phrase (prep with)
                             (simple-noun-phrase
                              (article the) (noun cat)))))))
が出てくる.

問題 4.45


上の文法で次の文は異る五通りに構文解析出来る: 「The professor lectures to the student in the class with the cat」 五通りの構文解析を与え, それらの間の違いを説明せよ.

問題 4.46


4.1および4.2節の評価器は, 被演算子を評価する順を決めなかった. amb評価器では被演算子を左から右へ評価するのが分る. 構文解析プログラムが, 被演算子が異る順で評価されると, 動かなくなる理由を説明せよ.

問題 4.47


Louis Reasonerは, 動詞句は動詞か, 動詞に前置詞句が続いたものだから, 手続きparse-verb-phraseを次のように(また名詞句も同様に)定義すると遥かに直截であるという:
(define (parse-verb-phrase)
  (amb (parse-word verbs)
       (list 'verb-phrase
             (parse-verb-phrase)
             (parse-prepositional-phrase))))
これは働くか. ambの中の式の順を交換すると, プログラムの振舞いは変るか.

問題 4.48


上に与えた文法を拡張し, 更に複雑な文が扱えるようにせよ. 例えば名詞句や動詞句を拡張し, 形容詞や副詞を含めるように, あるいは合成文が扱えるように出来る.53

問題 4.49


Alyssa P. Hackerは文を構文解析するより, 面白い文を生成する方に関心がある. 彼女は手続きparse-wordを, 「入力文」を無視し, その代り常に成功し, 適切な語を生成するように変更するだけで, 構文解析用に作ったプログラムを, 生成に使えると考えた. Alyssaの考えを実装し, 生成した初めの数個程度の文を示せ.54



48 われわれのプログラムは次の手続きを使って, リストの要素が異るかどうか調べることが出来る:

(define (distinct? items)
  (cond ((null? items) true)
        ((null? (cdr items)) true)
        ((member (car items) (cdr items)) false)
        (else (distinct? (cdr items)))))
memberは等価性のテストにeq?の代りにequal?を使う他はmemqと同じである.

49 これはLitton Industriesが1960年代に出版した「むずかしいリクリエーション」という本からとった. Litton Industriesはカンサス州技術者(Kansas State Engineer)になっている.

50 各リストの第一要素はリストの残りの語の品詞を示すという便法に従う.

51 parse-wordは未解析入力リストを修正するにはset! を使うことに注意. これが働くためにはamb評価器はバックトラックする時, set!の効果も元に戻さなければならない.

52 この定義が再帰的---動詞は任意個の前置詞句を続けることが出来る---であることに注意せよ.

53 この種の文法はいくらでも複雑になり得る. しかし実際の言語理解に関する限り, それらは ままごとでしかない. 計算機による実際の自然言語理解は構文解析と意味解釈の絶妙な混合を必要とする. 一方ままごとの構文解析器でも, 情報検索システムのようなプログラムの柔軟なコマンド言語を支援するには有用であり得る. Winston 1992 は実際の言語理解の計算論的問題解決法と, 単純文法のコマンド言語への応用を論じている.

54 Alyssaの考えは実際にうまく働く(し, しかも驚くべく単純だ)が, それが生成する文は少からず退屈である. それらはこの言語の可能な文の興味ある見本にはならない. 実際文法は多くの場所で高度に再帰的であり, Alyssaの技法はこれらの再帰の一つに「落ち込み」止ってしまう. これを扱う方法については問題4.50を参照のこと.

[ 目次, 前節, 次節, 索引 ]