Langphilia! / KL1 / Tips / OOP
手続き型言語では値を変えていく同一の変数だけど、 KL1では単一代入のために異なる変数を作らなければいけないときは、 数列(変化する値の時系列)っぽく名づけると良いでしょう。
Var0, Var1, Var2, ..., Var
KLIC-3.003のソースコード(と多くの既存コード)では、 最後も数字が付きます。前から書いていこうとすると、次の例では predicateの引数を書く時点で、使う変数の個数が分かっていなければなりません。 数字なしなら最後の変数が何番なのか気にしなくて済みます。
predicate(Var0, Var3) :- true | sub1(Var0, Var1), sub2(Var1, Var2), sub3(Var2, Var3). predicate1(Var0, Var) :- true | sub1(Var0, Var1), sub2(Var1, Var2), sub3(Var2, Var).
最後が数学で良くあるNだと、読みづらかったり 大文字小文字の使い分けが面倒だったりしそう。
VarN, Varn, VN, Vn
初期のBasicやCコンパイラは マクロが速いとかそーじゃないとかいろいろ言われましたが、 現在のKL1実装技術も熟成していないので、KLICはまだ初期段階にあります。 KLICのマクロを使って速くなることは(多分)なさそう、な気がする。 マクロ展開のタイミング、展開されたコードの実行タイミングを 熟知していなければマクロは使わないのが無難でしょう。 コードの量を減らしたいなら、そもそもKL1を使うのが間違ってます(オイオイ)。 特に具体化系のマクロは、 具体化実行のタイミングが直感に反しており、 中断が頻発するようなので使わない方が無難です。
KL1では述語がやたら多くなります。 KL1の述語はCで言えばgoto-label [Inside KLIC] なので、 不要な述語を作るとどこかから制御が飛んでくるのかと疑われてしまいます。 述語が必要なのは、
case( ARGs ) :- IF-CONDITION | IF-BODY. case( ARGs ) :- ELSE-CONDITION | ELSE-BODY.
sub(Var0, Var) :- true | Var = OPERATION( Var0 ). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% main :- true | pre_sub0(Var0), sub(Var0, Var1), post_sub1(Var1), pre_subX(VarX), sub(VarX, VarY), post_subY(VarY).Var0, Var は変数の操作前、操作後です。
loop( Index, ARGs ) :- CONTINUE-CONDITION | LOOP-BODY, Index1 := Index + 1, loop( Index1, ARGs ). loop( Index, ARGs ) :- EXIT-CONDITION | LOOP-EXIT.ループ脱出後の処理 (LOOP-EXIT) を決めうちしてしまうので、 共用ループはサブルーチンとして書きます。
common_loop(Var0, Var) :- CONTINUE-CONDITION | Var1 := OPERATION( Var0 ), common_loop(Var1, Var). common_loop(Var0, Var) :- EXIT-CONDITION | Var = Var0. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% main :- true | pre_loop0(Var0), common_loop(Var0, Var1), post_loop1(Var1), pre_loopX(VarX), common_loop(VarX, VarY), post_loopY(VarY).
server(Requests) :- true | server( INITIAL-STATE, Requests ). server( State, Requests ) :- Requests = [] | true. server( State, Requests ) :- Requests = [Request | Requests1] | OPERATION( State, State1, Request ), server( State1, Requests1 ). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% main :- true | server(Requests0), Requests0 = [REQUEST0 | Requests1], Requests1 = [REQUEST1 | Requests2], Requests2 = [].
class(Object) :- true | class( INITIAL-STATE, Object ). class( OBJECT, Messages ) :- Messages = [] | DESTRUCT( OBJECT ). class( OBJECT, Messages ) :- Messages = [Message | Messages1] | METHOD( OBJECT, OBJECT1, Message ), class( OBJECT1, Messages1 ). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% main :- true | class(ObjectX), ObjectX = [MESSAGES-TO-ObjectX], class(ObjectY), ObjectY = [MESSAGES-TO-ObjectY].サーバ以外のプロセスは、 コンストラクトされてからデストラクトされるまでの間に 外部からの制御を受け付けないオブジェクトと見なすことができます。 (それってオブジェクト?)
逐次処理を書くには同期が必須なので、使おうと思えばいくらでも使えるけど、 使うべきかどうか良く考えよう。
wait(X, Y, Z) :- wait(X) | Y = Z. wait(X1, X2, Y, Z) :- X1 = X2 | Y = Z. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% main :- true | klicio:klicio([stdin(RI), stdout(RO)]), wait(RI, normal(I), I, In), wait(RO, normal(O), O, Out), tty(In, Out). tty(In, Out) :- true | Out = [fwrite("prompt: "), fflush(F) | Out1], wait(F, 0, In, [gett(T) | In1]), OPERATE(T), tty(In1, Out1).
使わないと、こうなる。
main :- true | klicio:klicio([stdin(RI), stdout(RO)]), main(RI, RO). main(RI, RO) :- RI=normal(In), RO=normal(Out) | tty(In, Out). tty(In, Out) :- true | Out = [fwrite("prompt: "), fflush(F) | Out1], tty(F, In, Out1). tty(F, In, Out) :- F=0 | In = [gett(T) | In1], OPERATE(T), tty(In1, Out).
このサイズだと使わない方が分かりやすいか。 効率は分かりません。