2017-05-14 13 views
0

で数学的言語を定義だから私はこの数学の言語を持って、それはこのように書きます:プロローグ

E -> number 
[+,E,E,E] //e.g. [+,1,2,3] is 1+2+3 %we can put 2 to infinite Es here. 
[-,E,E,E] //e.g. [-,1,2,3] is 1-2-3 %we can put 2 to infinite Es here. 
[*,E,E,E] //e.g. [*,1,2,3] is 1*2*3 %we can put 2 to infinite Es here. 
[^,E,E] //e.g. [^,2,3] is 2^3 
[sin,E] //e.g. [sin,0] is sin 0 
[cos,E] //e.g. [cos,0] is cos 0 

と私は、この言語で書かれた数式の数値を見つけた一連のルールを書きたいですプロローグで

私が最初に「チェック」と呼ばれる関数を書いて、それはリストは、我々が持っている言語に応じて適切な方法で書かれているかどうかをチェックします:

check1([]). 
check1([L|Ls]):- number(L),check1(Ls). 
check([L|Ls]):-atom(L),check1(Ls). 

今私は、関数を記述する必要がある「を評価この言語によって書かれた式であるリストと、この言語に対応する数値である変数とを取ります。 例:

?-evaluate([*,1,[^,2,2],[*,2,[+,[sin,0],5]]]],N) -> N = 40 

ので、私はこれを書いた:

sum([],0). 
sum([L|Ls],N):- not(is_list(L)),sum(Ls,No),N is No + L. 
min([],0). 
min([L|Ls],N):-not(is_list(L)), min(Ls,No),N is No - L. 
pro([],0). 
pro([X],[X]). 
pro([L|Ls],N):-not(is_list(L)), pro(Ls,No), N is No * L. 
pow([L|Ls],N):-not(is_list(L)), N is L^Ls. 
sin_(L,N):-not(is_list(L)), N is sin(L). 
cos_(L,N):-not(is_list(L)), N is cos(L). 

d([],0). 
d([L|Ls],N):- L == '+' ,sum(Ls,N); 
L == '-',min(Ls,N); 
L == '*',pro(Ls,N); 
L == '^',pow(Ls,N); 
L == 'sin',sin_(Ls,N); 
L == 'cos',cos_(Ls,N). 

evaluate([],0). 
evaluate([L|Ls],N):- 
is_list(L) , check(L) , d(L,N),L is N,evaluate(Ls,N); 
is_list(L), not(check(L)) , evaluate(Ls,N); 
not(is_list(L)),not(is_list(Ls)),check([L|Ls]),d([L|Ls],N), 
L is N,evaluate(Ls,N); 
is_list(Ls),evaluate(Ls,N). 

をし、それだけでリストのために働くと正しい答えを返すが、メインリスト内の複数のリストのために、どのように私のコードがあるべきではないです?

+0

'チェック(L)は'リストに失敗した場合、述語全体が単純ではなく失敗してはいけません無効なリストを評価しようとするよりも? 'not(is_list(Ls))'は意味的ではないかもしれません。なぜならリストの末尾(この場合は 'Ls')は常にこの場合リストであると予想されるからです。 – lurker

+0

私はこの方法でプログラムを編成しませんでしたが、あなたのアプローチを考えれば、「d/2」の呼び出しで各演算子の述語を再帰的にし、サブリスト(現在は 'not(is_list(L))'のみを許可しています)。 – lurker

答えて

3

あなたが扱う仕様は、E(おそらくの式はの略)が6つの指定された操作の数または1つである可能性があることを記述する制作ルールのように見えます。それは空リストです[]は式ではありません。だから事実

evaluate([],0). 

はあなたのコードに入れてはいけません。あなたの述語sum/2は、あなたの仕様に従って有効な入力ではない、空リストと単一要素のリストを除いて、あなたが書いた方法とほぼ同じです。しかし、述語min/2とpro/2は正しくありません。次の例を考えてみましょう:

?- sum([1,2,3],X). 
X = 6      % <- correct 
    ?- sum([1],X). 
X = 1      % <- incorrect 
    ?- sum([],X). 
X = 0      % <- incorrect 
    ?- min([1,2,3],X). 
X = -6     % <- incorrect 
    ?- pro([1,2,3],X). 
X = 6 ? ;     % <- correct 
X = 0      % <- incorrect 

数学的に言えば、加算と乗算はassociativeですが、減算はできません。プログラミング言語では、これらの操作の3つすべては、通常、数学的に正しい結果を得るために左結合(left-associative)(例えば、Operator associativityを参照)である。

[A,B,C]: ((0 op C) op B) op A 

ほかの罰金うまくいく:

1-2-3 = (1-2)-3 = -4 

あなたはこれらの一連の操作を定義する方法は、次の計算に似ている:それは、上記のクエリでは減算のシーケンスが計算される、次のとおりです。

[1,2,3]: ((0 + 3) + 2) + 1 = 6 

しかし、それは減算にしません。

[1,2,3]: ((0 - 3) - 2) - 1 = -6 

そして掛けるときは、第2、間違った解決策に責任がある:

[1,2,3]: ((0 * 3) * 2) * 1 = 0 

あなたのコードといくつかの他の問題もあります(例えば、参照@ lurkerのコメント)しかし、私はそれについてさらに詳しく説明しません。代わりに、私は特定の生産ルールに密接に従う述語を提案する。文法は式を記述しており、対応する値を知りたいので、expr_val/2と呼ぶことにしましょう。

expr_val(X,X) :- 
    number(X). 

それは、それぞれ加算または減算または乗算の任意の長配列であることができる:それは数になります今の表現ができるかトップダウン説明しましょう。上記の理由のために、3つの配列は全て左結合的方法で評価されるべきである。だから、それらのすべてのための一つのルールを使用するために魅力的です。

expr_val([Op|Es],V) :- 
    sequenceoperator(Op),  % Op is one of the 3 operations 
    exprseq_op_val(Es,Op,V). % V is the result of a sequence of Ops 

パワー機能は三つの要素、^さなどが表現されて最初にリストとして与えられています。だからルールは非常に簡単です:

expr_val([^,E1,E2],V) :- 
    expr_val(E1,V1), 
    expr_val(E2,V2), 
    V is V1^V2. 

サインとコサインの式は、2つの要素を持つ両方のリスト、sinまたはcosである第1および表現である第2のです。引数がsincosの場合は、角度がラジアンであることに注意してください。リストの2番目の引数がラジアンの角度を返す場合は、コードで行ったようにsin/1とcos/2を使用できます。ただし角度を度で取得する場合は、最初にラジアンに変換する必要があります。私は例として後者の場合を含め、アプリケーションに合ったものを使用します。

sequenceoperator(+). 
sequenceoperator(-). 
sequenceoperator(*). 

、その後、述語exprseq_op_val/3:あなたは三つの可能なシーケンス演算子を定義する必要がexpr_val/2の二番目のルールについては

expr_val([sin,E],V) :- 
    expr_val(E,V1), 
    V is sin(V1*pi/180).  % radians = degrees*pi/180 
expr_val([cos,E],V) :- 
    expr_val(E,V1), 
    V is cos(V1*pi/180).  % radians = degrees*pi/180 

。先頭の演算子はすでにexpr_val/2でリストから削除されているため、リストには指定に応じて少なくとも2つの要素が必要です。リストの先頭の値が他の述語にアキュムレータとして渡され、左連想方法で配列を評価するために、/ 4

実際の評価を記載されている
exprseq_op_val([E1,E2|Es],Op,V) :- 
    expr_val(E1,V1), 
    exprseq_op_val_([E2|Es],Op,V,V1). 

をexprseq_op_val_。基本的に2つのケースがあります。リストが空の場合、演算子に関係なく、アキュムレータは結果を保持します。それ以外の場合、リストには少なくとも1つの要素があります。

exprseq_op_val_([],_Op,V,V). 
exprseq_op_val_([E1|Es],Op,V,Acc0) :- 
    expr_val(E1,V1), 
    op_val_args(Op,Acc1,Acc0,V1), 
    exprseq_op_val_(Es,Op,V,Acc1). 

:別の述語その場合には、/ 4は、リストの末尾(Es)と一緒に/ 4 exprseq_op_val_する再帰的アキュムレータとして渡され、それぞれの操作(Acc1)の結果を提供しop_val_args最後にop_val_args/4を定義する必要がありますが、それはかなり簡単です。

op_val_args(+,V,V1,V2) :- 
    V is V1+V2. 
op_val_args(-,V,V1,V2) :- 
    V is V1-V2. 
op_val_args(*,V,V1,V2) :- 
    V is V1*V2. 

ここで、この動作のしくみを見ていきましょう。まず、あなたの例のクエリ:

?- expr_val([],V). 
no 

オペレーター+-

?- expr_val(-3.14,V). 
V = -3.14 ? ; 
no 

空のリストが式ではありません。

?- expr_val([*,1,[^,2,2],[*,2,[+,[sin,0],5]]],V). 
V = 40.0 ? ; 
no 

あなたの仕様に応じて、最も単純な式は、数あります*には少なくとも2つの引数が必要です。

?- expr_val([-],V). 
no 
    ?- expr_val([+,1],V). 
no 
    ?- expr_val([*,1,2],V). 
V = 2 ? ; 
no 
    ?- expr_val([-,1,2,3],V). 
V = -4 ? ; 
no 

電源機能が正確に二つの引数を持っています

?- expr_val([^,1,2,3],V). 
no 
    ?- expr_val([^,2,3],V). 
V = 8 ? ; 
no 
    ?- expr_val([^,2],V). 
no 
    ?- expr_val([^],V). 
no 

のように...