子供を学校に屆けるだけではなく、お金をかせいだり、一日の残りをどのようにすごすかも考えてみてください。GPSは下のような構文でそういった問題を簡単に解決することができます。
> (gps '(son-at-home have-money car-works)
'(have-money son-at-school)
*school-ops*)
(EXECUTING DRIVE-SON-TO-SCHOOL)
SOLVED
しかしながら、お金をバッテリーにつかうような次の例では正しくない成功が返ってきてしまいます。
> (gps '(son-at-home car-needs-battery have-money have-phone-book)
'(have-money son-at-school)
*school-ops*)
(EXECUTING LOOK-UP-NUMBER)
(EXECUTING TELEPHONE-SHOP)
(EXECUTING TELL-SHOP-PROBLEM)
(EXECUTING GIVE-SHOP-MONEY)
(EXECUTING SHOP-INSTALLS-BATTERY)
(EXECUTING DRIVE-SON-TO-SCHOOL)
SOLVED
この「バグ」はGPSはゴールのセットをするために(every #'achieve goals)という表現を使うせいです。この表現がtrueを返すなら、ひとつずつ順にゴールたどりつくことを意味しますが、それは最終的にtrueだったことは意味しません。言いかえると、(have-money son-at-school)というゴールは、have-moneyとson-at-schoolが最終的に両方存在している状態であればtrueなのですが、GPSにとっては「最初にhave-moneyを、それからson-at-schoolを」という意味にとります。一つのゴールにたどりつくまでは、別のゴールを達成しないといったこともあります。こういったことを「prerequisite clobbers sibling goal(厳密でひどい兄弟ゴール)」問題と呼んでいます。have-moneyとson-at-schoolは兄弟の問題であり、そのうちson-at-schoolのためにはcar-worksが必要で、その目的のためにhave-moneyが必要になります。
「prerequisite clobbers sibling goal」問題を認識したプログラムの更新を直接的に行ってみましょう。最初に(every #'achieve something)というのはプログラムの中で2回呼びだされているところを、(achieve-all something)におきかえましょう。achieve-allは下記のように定義できます。
(defun achieve-all (goals)
"すべてのゴールを試し、その状態を確認する"
(and (every #'achieve oals) (subsetp goals *state*)))
Common Lispのsubsetpは第一引数がsubsetに2番目のものが含まれていたらtrueを返します。achieve-allでは、すべてのゴールを試したあとで現在の状態がそれぞれゴールのうちの一つがゴールだったらtrueを返します。これを確認しましょう。
achieve-allによって、GPSがひどいゴールのときにtrueを返さないようになりますが、ひどいゴールから回復しようとしたり再計画するようにはなっていません。可能性についてここでは言及しませんが、Sussmanの例をあとのセクションでとりあげます。
最終更新:2008年01月25日 05:29