Prolog學習:基本概念


上一篇對Prolog有了一個感性的認識,今天介紹下Prolog中一些基本概念,想要用Prolog解決一些實際問題之前必須要先了解它們。這些概念在《七周七語言》這本書中都有介紹,我簡單提煉匯總下,就當給這門小眾語言做個宣傳吧。

變量/規則/知識庫

在Prolog中變量的命名是有特殊要求的,如果一個詞以小寫字母開頭,它就是一個原子(atom),類似於其他語言中的符號(symbol),如果一個詞以大寫或下划線開頭,那么它就是一個變量,和其他語言一樣變量值可以改變,可以賦值(不過更靈活)。

符號組成一些事實:

likes(zhangsan,lisi).
likes(wangwu,lisi).
likes(chenliu,maqi).

符號和變量在一起可以用來定義規則:

friend(X,Y):- \+(X = Y),likes(X,Z),likes(Y,Z).

事實是我們對這個世界直接觀察的結果。規則是關於現實世界的邏輯推論。

事實 + 規則 = 知識庫

上面的規則可以叫做friend/2因為它有兩個參數(類似C#方法中的形參),:-讀作“如果”,“如果”后面是由一系列“子目標”組成,子目標之間可以是且的關系,用“,”分割,也可以是或者的關系,用“.”表示。Prolog就是通過驗證規則來回到我們yes或no的,如果參數能滿足所有子目標就是yes。

合一(unification)

 合一是Prolog中一個非常重要的概念。簡單的來說合一就相當於其它語言中的賦值:

cat(lion).
cat(tiger).

dorothy(X,Y,Z) :- X = lion,Y = tiger,Z = bear.
twin_cast(X,Y) :- cat(X),cat(Y).

合一的意思是:找出那些使規則匹配的值。

所以執行dorothy(lion,tiger,bear).這句,Prolog會返回yes:

dorothy/3規則的右側,Prolog將lion賦值給X,tiger賦值給Y,bear賦值給Z,就像在命令式語言中這樣:

var X = lion;
var Y = tiger;
var Z = bear;

這些值和左側(也就是dorothy(lion,tiger,bear))對應的值相匹配,所以合一成功。

不過真正讓合一發揮作用的是因為它在規則的兩側都能工作,在GNU Prolog中執行下面的語句:

dorothy(One,Two,Three).

會得到這樣的結果:

在規則右側Prolog還是分別將X,Y,Z和lion,tiger,bear進行綁定,而在規則的右側,Prolog是的One,Two,Three分別和X,Y,Z進行合一了:

One = X = lion;
Two = Y = tiger;
Three = Z = bear;

並且合一的情況有時候不是唯一的,我們如下執行上面的twin_cast規則:

twin_cast(One,Two).

可以通過“;”來進行追問,有時候我們可能不滿足於一個答案。

怎么樣你是不是已經預見到合一在Prolog中發揮的至關重要的作用了:輸出

列表/元組

只要是語言都會有數據結構,因為程序 = 算法 + 數據結構。

Prolog中有兩個非常重要的數據結構:列表和元祖。列表是變長的容器:像[1,2,3]這樣表示,元組是定長的容器,像(1,2,3)這樣表示。兩個數據結構非常簡單,但是和合一配合起來的時候會十分強大。

| ?- (1,2,3) = (1,2,3).

yes

 | ?- [A,B,C] = [A,B,C].

yes

如果兩個元組擁有的元素數量相同並且每個元素可以合一 ,則它們整體就是合一的。

加入變量會更有趣:

| ?- [A,B,C] = [1,2,3].

A = 1
B = 2
C = 3

yes

並且變量在哪一側無所謂,只要Prolog認為它們可以相同,那么就可以合一:

| ?- [A,2,C] = [1,B,3].

A = 1
B = 2
C = 3

(16 ms) yes

這讓我想到小時候玩的一種撲克牌游戲了,當抓到大王或小王的時候,我可以把它看做是任何一張牌。

另外列表擁有一個元組不擁有的功能,這個功能在后面介紹的遞歸中會被廣泛使用。 那就是通過[Head|Tail]解析列表,很簡單看個例子就明白了:

| ?- [Head|Tail] = [1,2,3,4].

Head = 1
Tail = [2,3,4]

yes

Head綁定到1,Tail綁定到剩下的元素,Tail仍然是一個列表。因為Prolog的變量是沒有數據類型之分的,所以它可以很容易的綁定為列表或元組,這點有點動態語言的性質,Prolog中也有匿名變量:

| ?- [a,b,c,d,e]=[_,_|[Head|_]].

Head = c

yes

這樣我們就能指定提取列表中某一個元素了。

遞歸

我們趁熱打鐵看看Prolog中遞歸是怎么發揮作用的,留心它和命令式語言中的不同

我們就以經典的斐波那契數列做例子吧,我們先來看下命令式語言是如何實現,這個再熟悉不過了:

static void Main(string[] args)
{
     Console.WriteLine(Fibonacci(8));

     Console.ReadKey();
}
static int Fibonacci(int n)
{
if (n == 1) return 0; if (n == 2) return 1; return Fibonacci(n - 2) + Fibonacci(n - 1); }

 

在看Prolog是如何實現之前,我們先來描述一下關於斐波那契數列的既定事實和規則,因為Prolog正是基於事實和規則的一門語言:

  • 第一個數是0;
  • 第二個數是1;
  • 從第三個數開始等它前面兩個數之和。

好了和上面對應的Prolog的代碼也是三行:

fibonacci(1,0).
fibonacci(2,1).
fibonacci(N,V) :- N > 2, N1 is N -1, N2 is N -2, fibonacci(N1,V1),fibonacci(N2,V2), V is V1 + V2.

:is是Prolog中內置的一個謂詞。

比較起來你可能覺得和C#代碼差不多,都很簡潔,Prolog有什么特別的或者說優勢呢?其中區別各位自己體會吧,我也在慢慢體會,這個區別就是聲明式語言命令式語言之間的區別。

這個例子太簡單不能很好體現Prolog的優勢,下一篇文章我們看下用Prolog是如何解決數獨和八皇后問題的。我們來看下《七周七語言》這本書中幾個遞歸的例子,也許你能感興趣不妨試試:

count(0,[]).
count(Count,[Head|Tail]):- count(TailCount,Tail),Count is TailCount + 1.

sum(0,[]).
sum(Total,[Head|Tail]):- sum(Sum,Tail),Total is Sum + Head.

average(Average,List):- sum(Sum,List),count(Count,List),Average is Sum/Count.

count是求列表元素的個數,sum是求和,average是求平均值。

內置謂詞

所謂內置的謂詞,可以簡單理解為Prolog提供的一些基本“功能”,就像.net中的一些類庫一樣。上面提到的is就是。

length:獲取列表的長度:

| ?- length([1,2,3],L).

L = 3

yes

append:可以用來合並兩個列表,當然還有很多其他功能:

| ?- append([1],[2],What).

What = [1,2]

yes

用於列表的減法:

| ?- append([1],What,[1,2,3]).

What = [2,3]

yes

排列組合:

| ?- append(One,Two,[1,2,3,4]).

One = []
Two = [1,2,3,4] ? ;

One = [1]
Two = [2,3,4] ? ;

One = [1,2]
Two = [3,4] ? ;

One = [1,2,3]
Two = [4] ? ;

One = [1,2,3,4]
Two = []

(15 ms) yes

fd_domain:驗證值是否在一個范圍之內:

| ?- fd_domain(1,1,4).

yes

| ?- fd_domain([1,2,3,4],1,4).

yes

| ?- fd_domain([1,2,3,5],1,4).

no

fd_all_different:檢查列表中是否有重復元素:

| ?- fd_all_different([1,2,3]).

yes
| ?- fd_all_different([1,2,1]).

no

member:檢查某一個值是否在一個列表內:

| ?- member(1,[1,2]).

true ? 

yes
| ?- member(3,[1,2]).

no

 

好了關於Prolog的基本概念就介紹到這,下一篇文章我們會看下通過這些基本概念Prolog是如何解決一些復雜問題的。想了解更多推薦大家去看看這本書。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM