所以我有这种数学语言,它是这样的:
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
我想编写一组规则,在prolog中找到由这种语言编写的数学表达式的数值。
我首先编写了一个名为" check"的函数,它根据我们的语言检查列表是否以正确的方式编写:
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 :(得分:3)
您使用的规范看起来像是一个生产规则,它描述了E(可能是 Expression 的缩写)可能是6个指定操作中的一个或一个。那是空列表[]
不是表达式。所以事实
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但是减法不是。在编程语言中,所有这三个操作通常都是左关联的(参见例如Operator associativity)以产生数学上正确的结果。也就是说,将计算上述查询中的减法序列:
1-2-3 = (1-2)-3 = -4
定义这些操作序列的方式类似于以下计算:
[A,B,C]: ((0 op C) op B) op A
这对于补充来说很好:
[1,2,3]: ((0 + 3) + 2) + 1 = 6
但它不适用于减法:
[1,2,3]: ((0 - 3) - 2) - 1 = -6
当乘以时,它负责第二个错误的解决方案:
[1,2,3]: ((0 * 3) * 2) * 1 = 0
您的代码还存在其他一些问题(请参阅@ lurker的评论),但是,我不会详细介绍。相反,我建议一个与指定生产规则密切相关的谓词。由于语法是描述表达式而你想知道相应的值,我们称之为expr_val / 2。现在让我们自上而下地描述一个表达式:它可以是一个数字:
expr_val(X,X) :-
number(X).
它可以是任意长的加法或减法或乘法序列。由于上述原因,所有三个序列都应以左关联方式进行评估。因此,对所有这些规则使用一条规则很诱人:
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
power函数是一个包含三个元素的列表,第一个是^
,其他是表达式。所以这条规则很简单:
expr_val([^,E1,E2],V) :-
expr_val(E1,V1),
expr_val(E2,V2),
V is V1^V2.
正弦和余弦的表达式都是包含两个元素的列表,第一个是sin
或cos
,第二个是表达式。请注意,sin和cos的参数是以弧度表示的角度。如果列表的第二个参数以弧度表示角度,则可以像在代码中一样使用sin / 1和cos / 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的第二条规则,您需要定义三个可能的序列运算符:
sequenceoperator(+).
sequenceoperator(-).
sequenceoperator(*).
随后谓词exprseq_op_val / 3。由于已经从expr_val / 2中的列表中删除了前导运算符,因此列表必须根据您的规范至少包含两个元素。为了以左关联方式评估序列,列表头部的值作为累加器传递给另一个谓词exprseq_op_val_ / 4
exprseq_op_val([E1,E2|Es],Op,V) :-
expr_val(E1,V1),
exprseq_op_val_([E2|Es],Op,V,V1).
描述了实际评估。基本上有两种情况:如果列表为空,那么无论运算符如何,累加器都保存结果。否则列表至少有一个元素。在这种情况下,另一个谓词op_val_args / 4传递相应操作的结果(Acc1
),然后将其作为累加器递归传递到exprseq_op_val_ / 4以及列表的尾部(Es
) :
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).
最后你必须定义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([*,1,[^,2,2],[*,2,[+,[sin,0],5]]],V).
V = 40.0 ? ;
no
根据您的规范,最简单的表达式是一个数字:
?- expr_val(-3.14,V).
V = -3.14 ? ;
no
空列表不是表达式:
?- expr_val([],V).
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
power函数有两个参数:
?- expr_val([^,1,2,3],V).
no
?- expr_val([^,2,3],V).
V = 8 ? ;
no
?- expr_val([^,2],V).
no
?- expr_val([^],V).
no
等等......