ルール

このチュートリアルの中で何度も、下記の3行がクエリの中で繰り返し出てきました。

[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title ?title]

ルールはDatalogにおける抽象化の手段です。 クエリの中で再利用可能な部分をルールとして抽象化し、わかりやすい名前をつけて、実装の詳細について忘れてしまうことができます。 これはちょうどあなたの好きなプログラミング言語で、関数を使ってできることと同じです。 上記の3行のためにルールを作ってみましょう。

[(actor-movie ?name ?title)
 [?p :person/name ?name]
 [?m :movie/cast ?p]
 [?m :movie/title ?title]]

ベクタ内の最初のコレクションはルールのヘッドと呼ばれ、コレクション内の最初のシンボルがルール名になります。 ルールの残りの部分はボディと呼ばれます。

ルールでは、(...)[...]のどちらでも使うことができますが、ボディと区別するため、ルールのヘッド部には(...)を用いるのが慣習となっています。この慣習はルールの呼び出しと通常のデータパターンを区別するためにも使われています。下記のクエリを参照してください。

ルールを関数と考えることもできますが、これは論理プログラミングなので、同じルールを、

  • 与えられた俳優名を元に映画のタイトルを検索する
  • 与えられたタイトルを元に俳優名を検索する

のどちらにも使えることに留意してください。

別の見方をすると、(actor-movie ?name ?title)?name?titleは入力値、出力値のどちらにも使えるということもできます。もしどちらにも値を指定しなければ、データベース内の全ての組み合わせを取得することができます。もし片方、また両方に値を指定すれば、期待通りに結果を制約することになります。

上記のルールを使うには、データパターンの代わりにルールのヘッド部を書けばよいだけです。値がすでに束縛されている変数は入力値に、残りは出力値になります。

ある映画のキャストを検索するクエリは、以前は下記のように書かなければなりませんでした。

[:find ?name
 :where
 [?p :person/name ?name]
 [?m :movie/cast ?p]
 [?m :movie/title "The Terminator"]]

ルールを使うと、

[:find ?name
 :in $ %
 (actor-movie ?name "The Terminator")]

となります。:in句内の%シンボルは、ルール群を表します。複数のルールを記述し、ベクタで囲んで、他の入力値と同様にクエリエンジンに渡すことができます。

[[(rule-a ?a ?b)
  ...]
 [(rule-b ?a ?b)
  ...]
 ...]

ルールのボディ部では、データパターン述語変換関数、他のルールを使うことができます。

ルールは、同じルール名を複数回使うことで論理和のクエリを書くツールとして使うこともできます。

[[(associated-with ?person ?movie)
  [?movie :movie/cast ?person]]
 [(associated-with ?person ?movie)
  [?movie :movie/director ?person]]]

後続のルールは、前にあるルールが満たされない場合のみ用いられます。

このルールを使うことで、監督とキャストを非常に簡単に見つけることができます。

[:find ?name
 :in $ %
 :where
 [?m :movie/title "Predator"]
 (associated-with ?p ?m)
 [?p :person/name ?name]]

あるルールは他のルールを呼び出せるという事実から、もしルールが自分自身を呼び出したらどうなると思いますか? 興味深い点ですが、これについては演習の中で考えることにしましょう。

ルール(movie-year ?title ?year)を記述しなさい。?titleは映画のタイトルで、?yearは映画が公開された年である。

クエリ:[ 答を見る ]

ルール:

ある映画で俳優同士、あるいは監督と俳優として一緒に働いたことのある2人は友達であるとする。ルール(friends ?p1 ?p2)を記述しなさい。p1p2はpersonエンティティを表します。例外的な振る舞いがあるかもしれないので、入力値を、例えば"Mel Gibson"に変えて試してみなさい。

クエリ:[ 答を見る ]

ルール:

入力値2:

ルール(sequels ?m1 ?m2)を記述しなさい。(sequelは「続編」の意味)?m1?m2はmovieエンティティである。属性:movie/sequelを使う必要がある。このルールを正しく実装するためには、下記のようなケースを考える必要がある。ある映画?m2?m1の続編であるためには、下記のどちらかの条件を満たす必要がある。

  • ?m2?m1の直接の続編である。 または
  • ?m2はある映画?mの続編であり、?m?m1の続編である。

上記のクエリを記述するには、少なくとも3通りの方法がある。3通り全てを見つけよ。

クエリ:[ 答を見る ]

ルール:

入力値2: