這篇與 Day11 類似,又是 Datalog 特有的功能。我們要來談論 Datalog 特有抽象機制: 規則 (rules)。
什麼是規則呢?像下方的 Datalog 查詢,它要找出『魔鬼終結者 (Terminator) 這部電影的演員名字』
[:find ?name
:where
[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title "The Terminator"]]
查詢之中的一小部分,用來『從電影名查演員名』的,總是反復地在不同的查詢使用,是否可以有什麼抽象機制可以讓我們不需要一次又一次地重寫下方這段程式碼呢?
[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title ?title]
有的,這個抽象機制叫做規則。先看一個範例吧:
;; 我們定義的規則
[(actor-movie ?name ?title)
[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title ?title]]
;; 改寫後的 Datalog 查詢
[:find ?name
:in $ %
:where (actor-movie ?name "The Terminator")]
既然稱之為抽象化機制了,其實規則就很像一般程式語言裡的函數。函數可以看成三個部分:「函數名稱、輸入引數、函數實作」。
(defn add-2 ;; add-2 是『函數名稱』
[x] ;; [x] 是輸入引數
(+ x 2)) ;; (+ x 2) 是函數實作
Datalog 的規則也差不多,它的組成是:[規則名稱 規則變數 對應子句]
。actor-movie
是規則名稱,?name
與 ?title
是規則變數,最後是對應子句。其中,規則名稱與規則變數又可以合稱為規則頭部 (rule head)。
參考下方的程式碼:
rules
的外部變數,它是一組規則的集合。rules
這個外部變數,並且在 :in
子句使用 %
去對應外部傳入的規則。:where
子句裡,呼叫規則名稱,即 (actor-movie ?name ?movie-title)
。(def rules
'[[(actor-movie ?name ?title)
[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title ?title]]]])
(d/q '[:find ?name
:in $ % ?movie-title
:where (actor-movie ?name ?movie-title)]
db rules "The Terminator")
讀者可能會問,「咦,這個規則的部分,SQL 查詢有對應的語法嗎?」
我認為沒有。儘管我們可以利用類似 dbt/jinja 之類的工具,把 SQL 查詢之中區段的 join 語句包裝起來,並且在不同的 SQL 查詢裡重複使用。但是,如果讀者仔細去了解 Datalog 規則的種種特性之後,想必會認同我的結論:『沒有』。
這個結論其實不太意外,畢竟,規則算是 Datalog 語言的宗廟之美、百官之富了。
這邊讓我們來看看規則的特性吧:
[(actor-movie ?name ?title)
[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title ?title]]
由於 Datalog 語言是邏輯編程 (logic programming),所以它支援不同方向的運算,即可以正的方向算過去,也可以逆的方向算回來。也就是說,每一個規則變數既可以用來當輸入變數,也可以用來當輸出變數。
這到底是什麼意思呢?
以 actor-movie
這個規則來舉例,我們有以下幾種用法:
?title
的值,透過電影名找演員名。?name
的值,透過演員名找電影名。規則也可以用作編寫邏輯或 (or) 查詢的另一種工具,因為相同的規則頭部可以使用多次。
[[(associated-with ?person ?movie)
[?movie :movie/cast ?person]]
[(associated-with ?person ?movie)
[?movie :movie/director ?person]]]
當使用 associated-with?
這個規則時,它既會去比對演員與電影、也會去比對導演與電影,所以下方的查詢,它可以找出終極戰士:掠奪者 (Predator) 這部電影的導演與演員。
[:find ?name
:in $ %
:where
[?m :movie/title "Predator"]
(associated-with ?p ?m)
[?p :person/name ?name]]
規則有對應子句,而這些對應子句可以是任何一種子句。在 Day 13 時,我們已經有提到過,Datalog 有五種子句:
這邊就產生了一個有趣的問題:「如果某個規則 A 的對應子句部分,我們不僅使用規則來寫這些對應子句,而且還在子句裡呼叫了 A 規則的自身呢?」
上述的寫法是可行的,而且如果我們這樣子寫,就等於寫出了遞迴的規則。沒錯,規則讓 Datalog 查詢可以遞迴。換言之,即使樹走訪、圖走訪之類的查詢,對於 Datalog 來說,一樣是小菜一碟。(註1)
註: