あなたが扱う仕様は、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のです。引数がsinとcosの場合は、角度がラジアンであることに注意してください。リストの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
のように...
'チェック(L)は'リストに失敗した場合、述語全体が単純ではなく失敗してはいけません無効なリストを評価しようとするよりも? 'not(is_list(Ls))'は意味的ではないかもしれません。なぜならリストの末尾(この場合は 'Ls')は常にこの場合リストであると予想されるからです。 – lurker
私はこの方法でプログラムを編成しませんでしたが、あなたのアプローチを考えれば、「d/2」の呼び出しで各演算子の述語を再帰的にし、サブリスト(現在は 'not(is_list(L))'のみを許可しています)。 – lurker