ADP
Programming Language ADP

English

Sourceforge.net

SourceForge.JP

Loading

文法

ADPの文法について説明します。

数値

ADPでは、整数と浮動小数点数を扱える。
整数は、64ビット長の符号付き整数
浮動小数点数は倍精度

コード例
150 # 整数
15.3 # 小数
0x8000  #16進数表記

<過去のバージョンについて>
・整数型はVer0.70までは32ビットでした。

<将来の予定>
・浮動小数点数で、以下の指数形式は、将来のバージョンでサポートする予定です。
 1.22e10
・0b、0、等の接頭辞による2進数、8進数の入力は将来のバージョンでサポートする予定です。

文字列

文字列は、クオーテーション(" ')で括ります。
文字列内には改行を含めることが出来ます。

" による文字列指定
"はCスタイルの文字列になります。現在、以下のエスケープ文字をサポートしています。
エスケープ文字置き換えられる文字
\\\
\n改行
\rCR(キャリッジリターン)
\""
<Ver 0.83から>
・\数値の指定や\b\t等、C言語のエスケープシーケンスに加えて\XXXX(16進数4ケタ)でユニコードで文字を指定できます。

' による文字列指定
'はBASICスタイルの文字列になります。
'で指定した場合、\による文字のエスケープは行われません。'を文字列に入れたい場合は、''と続けて入力します。


<現在のバージョンの制限>
ADPは日本語文字列については特別な処理を行っていません。Windowsでよく使われているシフトJISでは問題が発生する場合があります。
DB等の文字コードはUTF8を使用されることをお勧めします。

変数(変項)

変数(Prologの用語では変項とも言います)は、以下のルールで記述します。

・$または@で始まる。
・2文字目以降は、数値、アルファベット、またはアンダーライン(_)を使用する。

以下、変数になります。
$x
$first_second_name
@tableresult
@array_value

スカラー変数と配列変数
$はスカラー変数になります。基本的に全ての項をスカラー変数に入れることができます。
@は配列変数になります。配列であることを明記させる場合、配列変数を使用します。また述語の可変長引数を使用する場合も配列変数を使用します。

NIL

NILは何もないことを示す識別子になります。
DBでいうところのNULLに相当します。

配列

配列(1)
配列は、,(カンマ)で要素(項)を区切り、{ }で囲みます。

・例1 通常配列
{ "apple", "orange", "melon" }

配列(2)連想配列
配列では連想配列も記述できます。キーと値を => で区切ります。
キー部分は必ず文字列で指定します。値の部分は項および配列・リストが入ります。

・例2 連想配列
{ "apple" => "red", "orange" => "orange", "melon" => "green" }

*キー部分を変数にしたい場合、式:配列の結合を参照してください。

配列のアクセス
配列のアクセスは、[]を使います。Ver 0.76以降は{}も使用できます。

配列 [ キーまたは添え字 ]

のようにアクセスします。以下サンプルです。

・例3 arraymap.p 連想配列のサンプル
, $a = { "apple" => "red", "orange" => "orange", "melon" => "green" }
, $b = $a["melon"]
, printn($b);

・実行例
D:\sample>adp arraymap.p
green

Ver 0.76以降 {}で指定した場合、は括弧内は常にキーになります。
配列 [ キー ]
キーに数値を指定した場合、文字型に変換して文字列として扱います。

配列の要素の走査
リストの例にあります、each述語ですが、配列でも使用できます。

連想配列の用途
DBアクセスの組込述語sqlは、実行結果を連想配列で返します。
また、insert、update述語は、それぞれデータを連想配列で受け取ります。

リスト

リストの形式(1)カンマ区切り
リストは以下のように項を ,(カンマ)で区切って、[]で囲みます。

・例1 カンマ区切りのリスト
  [ "apple", "orange", "melon" ]

リストの形式(2)縦棒区切り
リストの区切りには ,(カンマ)以外に | (縦棒)で区切る形式もあります。
|(縦棒)はリストを先頭の要素(lispでいうところのcar)とリストの2番目以降の要素(cdr)に分ける。

例1のカンマ区切りのリストを縦棒区切りに変換すると以下のとおりとなる。

・例2 縦棒区切りのリスト
  [ "apple" | ["orange" | ["melon"]] ]

リストの要素の走査

|(縦棒)区切りは、ユニフィケーションを合わせて用いると以下ように要素を1つずつ検索できる。

・例3(allprint.p) リストの要素を1つずつ表示させる。
+allprint([]),!;
+allprint([$x|$y]),printn($x),allprint($y);

,allprint(["apple", "orange", "melon"]);

・実行例
D:\sample>adp allprint.p
apple
orange
melon

*リストの要素を1つずつ取り出す述語にeachがあります。

・例4(allprint2.p) each述語の使用例
+allprint($l),each($l,$x),printn($x),next;
,allprint(["apple", "orange", "melon"]);

・実行例
D:\sample>adp allprint2.p
apple
orange
melon

述語

述語は、ADPの評価単位になります。以下のように記述します。

 述語名引数リスト

引数リストが空の場合、()と記述することもできますが、省略して

述語名

と記載することもできます。述語名は、英字か_(アンダーバー)または!(カット)か =(関数形式)で始まる必要があります。
述語名は名前空間をサポートしています。名前空間::述語名と記述します。
名前空間を指定した場合は、指定された名前空間の述語のみユニフィケーションの対象となります。
名前空間を指定しない場合は、全ての名前空間の述語がユニフィケーションの対象となります。
組込み述語は名前空間 sys で定義されています。

関数形式

ADPでは、述語の引数リストで最後の引数は、戻り値として扱うことができます。
式の中に述語を記載すると、関数形式とみなされ引数の最後は戻り値として扱われます。
以下に例を示します。

・例1(functionform.p) 関数形式(1)
+pi(3.14);
,$s = 2 * 2 * pi
,printn($s);

・実行例
D:\sample>adp functionform.p
12.560000

これは以下の構文と同様になります。

・例2(functionform2.p)
+pi(3.14);
,pi($p)
,$s = 2 * 2 * $p
,printn($s);

述語の引数リスト内に、さらに述語を入れるとこちらは述語の入れ子とみなされる。
通常の言語では、A関数の引数にB関数を記述するとB関数の戻り値がA関数に渡されるが、
ADPの場合は、述語そのものが渡される。

・例3(functionform3.p) 関数形式とみなされない例
+pi(3.14);
,printn(pi); #3.14が出力されないで、piという述語そのものを表示しようとする

・実行例
D:\sample>adp functionform3.p

・例4(functionform4.p) 述語の引数リストに関数の戻り値を渡す場合
+pi(3.14);
,printn( =pi); #3.14が出力される

・実行例
D:\sample>adp functionform4.p
3.140000

=は述語を関数としてみなす接頭辞になります。式の先頭や述語の引数リスト内で述語を関数として扱い戻り値を引数とする場合は、=を述語の先頭に付与します。

述語のポストフィックス

ADPでは、述語名の後ろにポストフィックスとして記号を付与し述語の動作に変更を加えることができます。
ポストフィックスは以下のように付与します。

,predname$( 10, "3000", $result),...

$がポストフィックスになります。

以下、ポストフィックスの一覧です。

ポストフィックス一覧
ポストフィックス機能
@戻り値を配列とする
$キャッシュ
?評価結果をlast述語で返す
!評価する述語を!があるとみなす


評価結果をlast述語で返す
 述語名の後ろに'?'を付与すると、バックトラックを発生させずに評価結果をlast述語をとおして受け取ります。

コード例(sresult.p)
+test('B');

,test?('A'),$x == last, prtn("A:",$x);
,test?('B'),$x == last, prtn("B:",$x);

実行例
D:\sample>..\release\adp sresult.p
A:0
B:-1

,test('A')の評価ではバックトラックが発生するが、,test?('A')と ? ポストフィックスを付与することにより、評価結果を別途last述語を使って受け取ることが出来ます。

ホーン節・ユニフィケーション・バックトラック

ホーン節とその評価(1)

ホーン節は、手続型の言語のサブルーチン(関数)のように使うことができます。
後述するように機能的にはもっと複雑なことも出来ます。

以下の例では、$xと$yが与えられて、$zに$x*$x+$yを返す関数の例を示します。

・コード(func.p)
+func($x,$y,$z),$z = $x * $x + $y,!; #--(1)
,func(30,5,$z),print($z);

・実行結果
D:\sample>adp func.p
905

コードの(1)にあります +func ・・・の行がホーン節の定義になります。一行で終了していますが、ADPでは(Prologでも)、このようにシンプルに関数を書くことができます。
最後にあります ! はカットと言いまして後述するバックトラックに影響します。後程説明します。


ホーン節とその評価(2)ユニフィケーション

ADP(Prolog)のユニークな機能の一つユニフィケーションについて説明します。

先ずは例を見てみます。

・コード(unification.p)
+kencho('茨城県','水戸市');
+kencho('栃木県','宇都宮市');
+kencho('群馬県','前橋市');
+kencho('埼玉県','さいたま市');
+kencho('千葉県','千葉市');
+kencho('東京都','新宿区');
+kencho('神奈川県','横浜市');
,kencho('東京都',$x),printn($x);

・実行結果
D:\sample>adp unification.p
新宿区
ホーン節 kencho は、関東の都県の県庁所在地のDBになっています。
ゴール節では、
,kencho('東京都',$x)
という評価が行われています。
東京都の県庁所在地は新宿区と定義されていますが、実行結果を見ますとその通り'新宿区'が出力されています。

ADPにはこのようにホーン節と評価中の項(述語)のマッチングを行い、結果マッチするホーン節が見つかったら評価を真とします。
つまり、

,kencho('東京都',$x)

の評価では、ホーン節1つ1つとマッチングを行い、1つ目の引数が'東京都'である述語を検索することになります。unification.pのソースでは該当するホーン節がありますので、評価結果は真となります。
その時に、変数$xには、'新宿区'がマッチングされます。変数は、初期状態ではマッチングにおいて(なんでも良い)ということを示し、共に以降の評価においては、マッチングされた値を使用します。

,printn($x)

では、$xにマッチングされた、'新宿区'を出力することになります。

また、unification.pのコードにおいて、

,kencho('北海道',$x),printn($x)
としても、該当するホーン節が無い為、結果は何も表示されません。


ホーン節とその評価(3)バックトラック

ADP(Prolog)にはバックトラックと呼ばれる制御構造があります。
こちらも例を見てみます。このコートでは、全ての都県の県庁所在地が出力されます。

・コード(backtrack.p)
+kencho('茨城県','水戸市');
+kencho('栃木県','宇都宮市');
+kencho('群馬県','前橋市');
+kencho('埼玉県','さいたま市');
+kencho('千葉県','千葉市');
+kencho('東京都','新宿区');
+kencho('神奈川県','横浜市');
,kencho($x,$y),printn($x,":",$y),fail;

・実行結果
D:\sample>adp backtrack.p
茨城県:水戸市
栃木県:宇都宮市
群馬県:前橋市
埼玉県:さいたま市
千葉県:千葉市
東京都:新宿区
神奈川県:横浜市

ユニフィケーションの例との違いですが、都県の評価部分も変数になっています。

,kencho($x,$y)

上記の述語を評価しますと、全部のホーン節にマッチしますが、ADPでは先ずは先頭にあります、

+kencho('茨城県','水戸市');

にマッチして、先のprintn述語の評価を行います。その後、fail述語の評価を行いますが、fail述語は必ず評価に失敗します。
ADP(Prolog)では、述語の評価に失敗するとバックトラック(後戻り)を行います。
この例では、printn述語の再評価を行いますが、こちらも評価に失敗します。
次に、,kencho($x,$y)の再評価を行います。この再評価で、

+kencho('栃木県','宇都宮市');

にマッチし、再度printnの評価、failの評価、バックトラックと続き、結果としてkenchoの全てのホーン節とマッチします。最終的には全体がfailで終了します。
このようにADP(Prolog)ではバックトラックを使って全てのホーン節の評価を行うこともできます。


ホーン節とその評価(4)next述語(ADP独自の拡張)

バックトラックの例では、全てのホーン節kenchoの内容を出力するのにfail術語を使いましたが、fail述語では、評価に失敗するので全部のホーン節の内容を出力し終えた後は全体の評価も失敗します。
バックトラックを行いたいけど、全ての評価が終わった時は評価を続けたいということもあります。
next述語はそのような場合に使えます。以下、再び例をみていきます。


・コード(next.p)
+kencho('茨城県','水戸市');
+kencho('栃木県','宇都宮市');
+kencho('群馬県','前橋市');
+kencho('埼玉県','さいたま市');
+kencho('千葉県','千葉市');
+kencho('東京都','新宿区');
+kencho('神奈川県','横浜市');
,$c = 0 ,kencho($x,$y) ,printn($x,":",$y) ,$c = $c + 1 ,next ,printn($c,"件");

・実行結果
D:\sample>adp next.p
茨城県:水戸市
栃木県:宇都宮市
群馬県:前橋市
埼玉県:さいたま市
千葉県:千葉市
東京都:新宿区
神奈川県:横浜市
7件

少し、プログラムが複雑になりましたが、$cでマッチした個数を数えながらnext述語でバックトラックを行い、全てのホーン節のマッチングが終了したら、next述語の次のprintn述語で、マッチした件数を出力しています。

このnext述語ですが、私が知る限り、ADPオリジナルの機能になります。next述語により非常にスッキリとループが書けます。
ホーン節とその評価(5) ! - カット述語
ルーターのパケットの許可、禁止ルールによくあるのですが、ルールを上から順に実行して、一致したらそれで評価終了ということがしたいことがあります。このような場合に、! カット述語を使います。


以下、年齢別の料金計算にカットを使ったコード例を示します。


・カットを使ったコード例(cut1.p)
+calc_age_price($age, $price, 0),       $age <  2, !;                        # -- (1)
+calc_age_price($age, $price, $result), $age < 12, $result = $price / 2, !;  # -- (2)
+calc_age_price($age, $price, $price);                                       # -- (3)

,calc_age_price(  0, 100, $result), printn("乳児料金:", $result), next; # 乳児料金を表示
,calc_age_price( 10, 100, $result), printn("子供料金:", $result), next; # 子供料金を表示
,calc_age_price( 20, 100, $result), printn("大人料金:", $result), next; # 大人料金を表示

・実行結果
D:\sample>adp cut.p
乳児料金:0
子供料金:50
大人料金:100

年齢別の料金が計算されています。よく見ると気づくかと思いますが、乳児の年齢は(2)の条件(12未満)に合致します。従いましてnext述語でバックトラックすると(2)も実行されますが、カット述語のおかげで(2)の評価は行われません。カット述語はこのようにホーン節に対して一度マッチしたものは二度とマッチさせないという機能があります。以下、カットを外した例を掲載ます。

・カットを使わないコード例(cut2.p)
+calc_age_price($age, $price, 0),       $age <  2;                        # -- (1)
+calc_age_price($age, $price, $result), $age < 12, $result = $price / 2;  # -- (2)
+calc_age_price($age, $price, $price);                                    # -- (3)

,calc_age_price(  0, 100, $result), printn("乳児料金:", $result), next; # 乳児料金を表示
,calc_age_price( 10, 100, $result), printn("子供料金:", $result), next; # 子供料金を表示
,calc_age_price( 20, 100, $result), printn("大人料金:", $result), next; # 大人料金を表示

・実行結果
D:\sample>adp cut2.p
乳児料金:0
乳児料金:50
乳児料金:100
子供料金:50
子供料金:100
大人料金:100

乳児料金が3回出てきています。カットを使わないで、乳児料金を正しく算出させる為には、子供料金と大人料金に正しく条件を入れます。(2)の子供料金の場合は、
$age >= 2, $age < 12
とし、(3)の大人料金の場合は、
$age >= 12
とします。


*ADPではこのように条件を手軽に記載できますが、バックトラックが発生した場合、ホーン節は出現順で評価されます。カットを使うと条件式を記載する手間が省けますが、コードは多少技巧的になります。

ADPでは式をサポートしています。

演算子一覧
ADPでサポートしている演算子の一覧です。

演算子説明
+加算
-減算 単項(マイナス)
*乗算
/除算
%剰余
<比較(小なり、未満)
<=比較(小なりイコール、以下)
>比較(大なり、より大きい)
>=比較(大なりイコール、以上)
==比較(イコール)
!=比較(ノットイコール)
<>比較(ノットイコール)
~ビットNOT
&ビットAND
^ビットXOR
|ビットOR
=代入 単項(述語の値)
+=代入(加算後代入)
-=代入(減算後代入)
*=代入(乗算後代入)
/=代入(除算後代入)
%=代入(剰余後代入)
&=代入(AND後代入)
^=代入(XOR後代入)
|=代入(OR後代入)
.メソッド呼び出し
[]配列(連想配列)アクセス
{}連想配列アクセス
:連想配列アクセス


演算子の優先順位
以下、演算子の優先順位です。
演算子優先順位
[] {} : .1
-(単項) ~ =(単項)2
* / %3
+ -4
< <= > >= == != <>5
&6
^7
|8
= += -= *= /= %= &= ^= |= 9

優先順位の欄にある数値が小さい方が優先順位が高いです。
比較演算子は、代入演算子より優先順位が高いですが、結果がfalseとなる場合バックトラックを行いますので、比較結果を代入することはできません(Ver0.5以降)。
※Ver0.81では[],{},:は.よりも優先順位が高いです。Ver0.82移行は同一の優先順位になります。

四則演算

ADPは、四則演算に対応しています。以下四則演算の例です。
多くのプログラミング言語と同様に'*'(乗算)や'/'(除算)は'+'(加算)より優先順位が高いので先に計算されます。また括弧もサポートしています。

・コード(expression2.p)
,printn( 10 + 20 * 4 / 5);          # 26
,printn( (10 + 20) % 7 + 1);        # 3

・実行結果
D:\sample>adp expression2.p
26
3


代入

ADPは、'='代入もサポートしています。ADPでは'='代入はPrologと異なり、通常のプログラミング言語と同様の動きをするように実装しています。

・コード(let1.p)
,$a = 10, $b = 20, $c = $a * $b, printn($c);

・実行結果
D:\sample>adp let1.p
200

比較

ADPでは、比較演算子もサポートしています。比較の結果がFalseの場合、バックトラックが発生します。比較の結果がTrueの場合、そのまま次の述語へ制御を移します。

・コード例(condition.p)
,$x = 10 ,$y = 20 ,$x < $y ,printn("$x is smaller than $y."); # -- (1)
,$x = 10 ,$y = 20 ,$x > $y ,printn("$x is bigger than  $y."); # -- (2)

・実行結果
D:\sample>adp condition.p
$x is smaller than $y.


文字列の結合
 ADPでは、文字列の結合は+演算子、またはcat述語を使用します。

・コード例(cat.p)
,$str = cat("This", " is", " a", " pen."), printn($str);

・実行例
D:\sample>adp cat.p
This is a pen.


リストの結合
リストの結合は、+ 演算子を使用します。

・コード例(list_add.p)
,$x == ['a','b'] + ['c','d','e'], printn($x);

・実行例
D:\sample>adp list_add.p
[a,b,c,d,e]

配列の結合
配列の結合は、+ 演算子で行います。

・コード例(array_add1.p)
,printn({'a','b','c'} + {'d','e','f'});

・実行例
D:\sample>adp array_add1.p
{a, b, c, d, e, f}

ハッシュ形式の配列の結合も行えます。またハッシュキーが変数になる場合、以下のようにリストを使って1要素づつ追加します。

・コード例(array_add2.p)
,$key = 'C'
,$value = 'c'
,$hash={ :A => 'a', :B => 'b'} + [$key|$value]
,printn($hash);

・実行例
D:\sample>adp array_add2.p
{A => a, B => b, C => c}

リストの区切り記号とOR演算子
リストの区切り記号とOR演算子ですが同じ記号 | を使っています。
この為、意図しない解釈が行われる場合があります。
|の解釈ですが、下記のように行います。
1.式中の|は、ORとみなす。
2.リスト中の|は、リストの区切りとみなす。
3.リスト中に式があった場合は、ORとみなす。
この場合(ORおよび論理演算全体)の式の定義ですが、
・式マーカー(=)が出てきた。
・他の演算子(代入演算子、四則演算子、比較演算子)が出てきた。
となります。

以下の例で、| はOR演算子とみなされます。
,$x = $y | $z;
,$x = [$a + 10 | $b ];
,$x = [= $a | $b ];

以下の例で、|はリストの区切りとみなされます。
,$x = [$y | $z];

※リスト内で式を使用した場合、リストの区切り記号|が使えなくなります。区切り記号を使用したい場合は式をリストの外に出します。

,$a = $a + 10, $x = [$a | $b ];

糖衣構文(シンタックスシュガー)

関数形式
 述語の一番最後の引数を戻り値として扱うことができ、述語を式の中で使用すると、関数のようなります。
 以下の2つのコードは同様に動作します。

sfunc1.p
+pi(3.14);
,pi($pi),printn($pi);

sfunc2.p
+pi(3.14);
,$pi == pi,printn($pi);

メソッド形式
 '.'キーワードを使用することによりメソッド呼び出しのように述語を呼び出せます。
'.'の左側にある項を述語の最初の引数として扱われます。
前述のsfunc1.pは . を使用することにより、以下のように記述できます。

sfunc3.p
+pi(3.14);
,pi($pi),$pi.printn;

メソッド形式は、その名のとおり、変数や項をオブジェクトに見立ててメソッドのように見せるだけで、最初の引数に指定したとものと変わりないですが、この仕組みによりclassキーワードを書かなくてもメソッドを作成できる利点があり、より自由にメソッドの定義が行えます。

関数形式とメソッド形式とを組み合わせることで余分な変数を記述する必要がなくなりコードがすっきりします。

sfunc4.p
+pi(3.14);
,pi.printn;

pi述語の最後の引数$piとprintn述語の最初の引数$piが共に同じ変数の為 . キーワードを使用することにより変数$piの記述を省略することが出来ます。

関数形式で配列を受け取る
 関数形式で戻り値(可変長)を配列で受け取る場合、述語名の後ろに'@'を付与します。
以下の例では、県名と県庁所在地を配列で受け取り(kencho@)、csv形式に変換し(csv)、表示します(printn)。またnext述語でバックトラックを行い、マッチする全ての県、県庁所在地を表示します。

注)バグの為、以下のコードですが、Ver7.0では実行例のようになりません。Ver7.1以降(5/23リリース予定)でお試しください。

コード例(sarray.p)
+kencho('茨城県','水戸市');
+kencho('栃木県','宇都宮市');
+kencho('群馬県','前橋市');
+kencho('埼玉県','さいたま市');
+kencho('千葉県','千葉市');
+kencho('東京都','新宿区');
+kencho('神奈川県','横浜市');

,kencho@.csv.printn,next;

実行例
D:\>adp sarray.p
茨城県,水戸市
栃木県,宇都宮市
群馬県,前橋市
埼玉県,さいたま市
千葉県,千葉市
東京都,新宿区
神奈川県,横浜市

,kencho@.csv.printn,next;

上記のコードは以下のコードと同じです。

,kencho(@x),csv(@x,$y),printn($y),next;

リストのキーアクセス
リストに対して[]を使用するとキーアクセスとみなされるように変換されます。

コード例(slist.p)
,$x == [ ['key1' | 'value1'], ['key2' | 'value2'], ['key3' | 'value3'] ]
,print($x['key1']);

実行例
D:\sample>adp slist.p
value1

リストに対する[]は、以下のように item 述語の呼び出しに置き換えられます。

item($x,"key1",$item~1)

式は、それぞれの演算子を実装する述語の並びとしてコンパイルされます。

,$x == $b + 10
上記のコードは以下のコードに変換されます。
,_add( $b, 10, $x)

WEBページ(AWP)

工事中です。

サンプルおよびAWPの設定を参照ください。

Powered by ADP.