Prolog/高階程式設計
外觀
< Prolog
高階程式設計是指編寫將其他程式作為輸入和/或輸出其他程式的程式。高階程式設計在函數語言程式設計中很常見,但也可以在 Prolog 中實現。
例如,假設您正在編寫一個語音合成應用程式,它可以朗讀電話號碼。我們可能有一個謂詞digit_english/2,它將 Prolog 整數 0 到 9 與英文單詞(作為符號)相關聯。
digit_english(0,zero).
digit_english(1,one).
digit_english(2,two).
...
現在,要翻譯電話號碼,定義
phone_english([],[]).
phone_english([Digit|Ds],[Word|Ws]) :-
digit_english(Digit,Word),
phone_english(Ds,Ws).
現在,假設我們要新增對另一種語言的支援,比如德語。你定義digit_german
digit_german(0,null).
digit_german(1,eins).
digit_german(2,zwei).
...
要翻譯電話號碼,使用phone_german
phone_german([],[]).
phone_german([Digit|Ds],[Word|Ws]) :-
digit_german(Digit,Word),
phone_german(Ds,Ws).
請注意phone_english/2和phone_german中常見的遞迴模式。您可能以前多次看到過它,甚至多次編寫過它。它可以概括為
- 基本情況:將空列表與其自身相關聯。
- 遞迴情況:透過某個謂詞將第一個列表的第一個元素與第二個列表的第一個元素相關聯,然後對兩個列表的尾部進行遞迴。
這種模式可以透過一個稱為map/3的高階謂詞來捕獲(但不是標準 Prolog 定義的)
map([],P,[]).
map([X|Xs],P,[Y|Ys]) :-
Goal =.. [P,X,Y],
call(Goal),
map(Xs,P,Ys).
在 Prolog 中,我們不能寫 P(X,Y),因為函式符的頭部必須是符號。因此,我們使用中綴運算子=..,它將第一個引數與從其右引數構建的項統一起來,該引數必須是一個列表。第一個元素,它必須是一個符號,成為函式符,其餘部分成為其引數。然後我們將call/1應用於我們構建的項以將其作為 Prolog 目標呼叫。請注意,許多 Prolog 實現也定義了call/2、call/3、…內建謂詞,它們從給定引數(第一個引數是主要函式符)構建一個可呼叫項,以便上面的程式碼示例也可以寫成
map([],P,[]).
map([X|Xs],P,[Y|Ys]) :-
call(P,X,Y),
map(Xs,P,Ys).
現在我們可以將phone_english/2和phone_german/2重新定義為
phone_english(P,E) :- map(P,digit_english,E).
phone_german(P,G) :- map(P,digit_german,G).
這兩個謂詞仍然在兩個方向上工作,並且非確定性得以保留。
好處顯而易見:透過使用map/3,我們避免了重複程式碼片段,因此我們的程式變得更短,更容易閱讀和理解。
以下研究論文是關於 Prolog 中的高階程式設計,其中包含大量示例:[1]