Prolog 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

Prolog 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,prolog,Prolog,对于expr_val/2的第二条规则,您需要定义三个可能的序列运算符: sequenceoperator(+). sequenceoperator(-). sequenceoperator(*). 然后是谓词exprseq_op_val/3。由于已从expr_val/2中的列表中删除了前导运算符,因此根据您的规范,该列表必须至少包含两个元素。为了以左关联的方式计算序列,列表头的值作为累加器传递给另一个谓词exprseq_op_val_/4 exprseq_op_val([E1,E2|Es],O

对于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
):

最后,您必须定义op_val_args/4,这同样非常简单:

expr_val([^,E1,E2],V) :-
   expr_val(E1,V1),
   expr_val(E2,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.
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(X,X) :-
   number(X).
   ?- expr_val(-3.14,V).
V = -3.14 ? ;
no
空列表不是表达式:

   ?- expr_val([],V).
no
运算符
+
-
*
至少需要两个参数:

   ?- 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
幂函数正好有两个参数:

   ?- 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
在本例中)总是一个列表。我不会以这种方式组织程序,但考虑到您的方法,您需要做的是使每个操作符谓词在调用
d/2
时递归,并在遇到子列表时调用
d/2
(您当前只允许
不允许(is_list(L))
)。如果
检查(L)
在列表上失败,整个谓词不应该失败,而不是尝试评估无效列表吗?而
not(is_list(Ls))
在语义上可能不合理,因为在这种情况下,列表的尾部(
Ls
在本例中)总是一个列表。我不会以这种方式组织程序,但考虑到您的方法,您需要做的是在调用
d/2
时使每个运算符谓词递归,并在遇到子列表时调用
d/2
(您当前只允许
不允许(is_list(L))
)。
expr_val([^,E1,E2],V) :-
   expr_val(E1,V1),
   expr_val(E2,V2),
   V is V1^V2.
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
sequenceoperator(+).
sequenceoperator(-).
sequenceoperator(*).
exprseq_op_val([E1,E2|Es],Op,V) :-
   expr_val(E1,V1),
   exprseq_op_val_([E2|Es],Op,V,V1).
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(+,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
   ?- 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