例題プログラムを実行するには、その内容を適当なファイルに記述してdescartesの引数として実行してください。
print述語でメッセージを表示します。
実行結果
let述語で整数を計算します。
変数は頭に#を付けます。
letf述語は浮動小数点数を計算します。
letを省略すると、整数演算式として計算します。
letc述語は複素数の計算を行います。
デカルト言語は、1階述語論理を基にしているため、prolog言語と多くの共通点を持ちます。
そこで、リストを連結するプログラムでデカルト言語とprolog言語を比較してみましょう。
デカルト言語では以下のようになります。
prolog(DEC-10 prolog)では以下のようになります。
どちらの例でも、第2引数と第3引数のリストを連結し、第1引数に結果を返します。
違いがお分かりでしょうか。
この例では出てこないのですが、prologのカットオペレータ! は、デカルト言語では、<!>となります。
前項でデカルト言語とprologとの類似点について言及しましたが、ここでは相違点について述べます。
デカルト言語では、ボディ部に書けるものが述語の羅列に限られるprologとは異なり、任意のリストが書けることが大きく異なります。 リストの場合の実行順序は記述された左から順に実行されます。
上記の例では、exampleが実行されると、e1, e2, e3, e4, e5の順に実行されます。
この記述の利点は、述語のつらなりをまとめて述語の引数に渡すようなメタな処理を行えることや、次に示す構文解析用の記号のために処理をまとめるために利用できることです。
構文解析のための記号として{}繰り返し、[]省略可能、|オア選択などが使えるのもprologとの大きな相違点です。
上記の例では、example2が実行されると、abcが実行され、defか(hij lkm)のどちらか成功する処理が繰り返され、どちらも該当しなければループを抜けて、nopは成功しても失敗しても処理され、最後にendが実行されます。
デカルト言語では述語が実行され、評価された後は、true, false, unknownの3値を取ります。
一方、prologでは述語の評価された後に取る値には、trueとfalseがあります。
デカルト言語のunknownが、prologのfalseに相当します。この値のときは、他に解がないか探索が継続して実行されることになります。
trueはどちらでも同じです。
デカルト言語のfalseは、prologのfalseとは異なり、明に実行を失敗させ処理を打ち切るのに使います。
for述語を使うと、繰り返しのループ処理を実行できます。
1から10までのループを実行します。
2重以上のループ処理も可能です。
foreach述語を使うと、引数のリストの要素を使って繰り返しのループ処理を実行できます。
上記では、I, think, about, itが順に表示されます。
let, letf述語の引数は関数述語として評価されます。関数述語の返す関数値は第1引数です。
返り値の変数には、無名変数"_"を指定すると便利です。
関数述語を使用することにより、簡潔に再帰関数を書くことができます。
以下に階乗を計算する例を示します。
let文の中でlet文を呼び出すこともできます。
let, letfは、数を返す関数しか使えませんが、func述語を使えば、さまざまなデータを引数とする関数を記述できます。
以下の例では、関数型のLISP言語のようにリストを処理しています。
結果は次のようになります。
func述語を使い、htmlファイルを合成するプログラムです。
テンプレートを用意し、その中の可変部分を述語や変数で埋め込み、関数述語として実行すると、目的のhtmlファイルが合成されます。
<html #html #title #body> // ヘッド
"<HTML>
<HEAD>
<TITLE>" #title "</TITLE>
</HEAD>
<BODY>
" </BODY>
</HTML>"
ちょっと分かりにくいのですが、#title, #body, ::sys<random _>以外は全部文字列で""で囲まれています。デカルト言語では改行も文字列にそのまま含まれます。
つまり、func述語の引数では、文字列と変数と述語を混在させておくと、実行時に変数と述語は実行された結果が展開されます。述語は関数述語として扱われるので、第一引数の値が展開されるのです。
文字列はそのままで、結果としてすべてが連結されて返され、func述語の第一引数の変数に設定されます。
以下は結果です。
?<html #h "Hello World" "This program is test."> ::sys <writenl #h>; // 実行
<HTML>
<HEAD>
<TITLE> Hello World </TITLE>
</HEAD>
<BODY>
</HTML>)
引数として入力した、"Hello World"、 "This program is test." および乱数の部分が置き換えられているのが判るでしょうか。
構文解析のためのEBNF記法が使えます。
拡張バッカス記法:EBNF(Extended Backus–Naur Form)記法とは、コンピュータのプログラミング言語の文法を規定するのに使われるメタ言語です。 元来は、プログラム言語のコンパイラやインタプリタの記述に用いられてきました。
デカルト言語では、EBNF記法を構文の基本的要素として採用しています。
EBNF記法を使うことにより、定められた文法に従って、プログラムやデータおよび英語や日本語などの自然言語でさえもある程度の自動的な解釈を可能とします。
以下には簡単な日本語の文章の解析例を示しましょう。
<>でくくられていない文字列または"文字列"は、入力された文字列と比較され一致するとtrueとなって処理が続行されます。 以下の例では、"田中", 佐藤, hniwaが、それに相当します。
次のように入力すると名前だけが抽出されます。
#name変数に入力文の中の名前が取り出されています。
TOKEN述語を使用して、任意の文字列パターンにマッチするトークンを合成できます。(正規表現の代替となります。)
- 先頭が英字で2文字目から英数字の文字列
? ::sys<getline _
getline述語はキーボードより1行入力し、TOKEN述語を呼び出しています。
A述語は英数字1文字と、AN述語は英数字1文字とマッチします。
- 数字列 (<NUM #token>と同等です)
? ::sys<getline _
- 大文字か数字の文字列
? ::sys<getline _
RANGE述語は指定された2つの引数の範囲にある文字とマッチします。上の例ではAからZの英大文字とマッチします。
- “DISK”+数字3桁
? ::sys<getline _
- ひらがな
? ::sys<getline _
- カタカナ
? ::sys<getline _
- 行の先頭がNo.で次に数字が続く
? ::sys<getline _
構文解析の対象はファイルあるいはgetline述語の入力だけではなく、syntax述語を使うことにより、任意の文字列に対しても実行できます。
- 先頭が英字で2文字目から英数字の文字列
syntax述語の第一引数の文字列"var123b"が、「先頭が英字で2文字目から英数字の文字列」か判定します。
オブジェクトは以下のような形式で定義します。
:: < オブジェクト名
>;
例として鳥、ペンギン、鷹のオブジェクト例を以下に示します。
::<bird
>;
::<penguin
>;
::<hawk
>;
オブジェクトの呼び出し方は、ライブラリの呼び出し方法と同じです。
以下を試してみましょう。
? ::bird <swim>;
? ::penguin <swim>;
? ::bird <walk>;
? ::penguin <walk>;
? ::bird <fly>;
? ::penguin <fly>;
? ::penguin <run>;
? ::hawk <fly>;
? ::hawk <walk>;
? ::hawk <swim>;
結果は以下のようになります。
result --
(<obj bird <swim>>)
-- unknown 鳥が泳ぐのか分からない
result --
(<obj penguin <swim>>)
-- true ペンギンは泳ぐ
result --
(<obj bird <walk>>)
-- true 鳥は歩く
result --
(<obj penguin <walk>>)
-- true ペンギンは歩く
result --
(<obj bird <fly>>)
-- true 鳥は飛ぶ
result --
(<obj penguin <fly>>)
-- false ペンギンは飛ばない
result --
(<obj penguin <run>>)
-- unknown ペンギンが走るか分からない
result --
(<obj hawk <fly>>)
-- true 鷹は飛ぶ
result --
(<obj hawk <walk>>)
-- true 鷹は歩く
result --
(<obj hawk <swim>>)
-- unknown 鷹が泳ぐのかわからない
ユークリッドの互除法を使い最大公約数を求めるプログラムを作ります。
<gcd 答 整数1 整数2>の形式で整数1と整数2の最大公約数を答に設定します。
ユークリッドの互除法については、書籍やWWW上で参照してください。
<gcd #x #x 0>; // #xと0の最大公約数は、#xである。
<gcd #x #a #b>
<gcd #x #a #b>
?<gcd #x 511639100 258028360>;
result --
(<gcd 20 511639100 258028360>)
-- true
フィボナッチ数を求めるプログラムをつくります。
<fib 答 入力数>
入力数に対応するフィボナッチ数を答に設定します。
フィボナッチ数については、書籍やWWW上で参照してください。
このプログラムの特徴は、「通常の計算処理」を行った結果をキャッシュのようにプログラムに追加していくことです。
計算するほどキャッシュにたまる結果が増えて、より大きな数の計算を高速化することができます。
<fib 0 0>; // 0の場合
<fib 1 1>; // 1 の場合
<fib #result #n> // 通常の計算処理
?<fib #x 30>;
result --
(<fib 832040 30>)
-- true
quick sort アルゴリズムを使いリストの中の要素をソートするプログラムを作ります。
<append #X () #X>;
<append (#A : #Z) (#A : #X) #Y>
<qsort () ()>; // ()をソートした結果は()
<qsort #sortedlist (#x : #list) >
<qsplit _ () () ()>;
<qsplit #x (#y : #list) (#y : #l1) #l2> // #y が#xより小さければ#l1に入れる
<qsplit #x (#y : #list) #l1 (#y : #l2)> // #yが#xより大きいので#l2に入れる
?<qsort #s (11 12 3 7 1 2 0 -1 10 9 4 8 5 6) >;
result --
(<qsort (-1 0 1 2 3 4 5 6 7 8 9 10 11 12) (11 12 3 7 1 2 0 -1 10 9 4 8 5 6) >)
-- true