在列表中使用findall / 3进行数学运算的问题(序言)

时间:2019-04-12 11:07:51

标签: prolog

我想用findall / 3将List中的元素相乘。具体来说,我有两个函数double(X,Y),它们将X加倍,而square(X,Y)返回X的平方值。我的问题是,该操作仅适用于列表的第一个元素。

double(X,Y) :- Y is X*2.
square(X,Y) :- Y is X*X.
map_f(Operation,[H|List],[R|Results]) :-
   Predicate=..[Operation,H,R],
   call(Predicate),
   findall(X,( member(X,List) ), Results).

例如,如果我输入map_f(double,[3,1,2,6,3,1,6],L)., 我希望输出:L = [6,2,4,12,6,2,12], 但它显示:

?- map_f(double, [3, 1, 2, 6, 3, 1, 6], List).
List = [6, 1, 2, 6, 3, 1, 6]
Yes (0.00s cpu)

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

如果要使用findall/3,则必须这样写:

?- Xs = [3,1,2,6,3,1,6], findall(Y, ( member(X, Xs), double(X, Y) ), Ys).
Xs = [3, 1, 2, 6, 3, 1, 6],
Ys = [6, 2, 4, 12, 6, 2, 12].

如果您真的想将谓词作为参数传递并使用=..,则逻辑仍然是相同的,您只需要重新编写您的定义即可执行正确的操作:

map_f(Pred_name, L1, L2) :-
    Goal =.. [Pred_name, X, Y],
    findall(Y, ( member(X, L1), Goal ), L2).

然后:

?- map_f(double, [3,1,2,6,3,1,6], R).
R = [6, 2, 4, 12, 6, 2, 12].

?- map_f(square, [3,1,2,6,3,1,6], R).
R = [9, 1, 4, 36, 9, 1, 36].

但是,而不是:

Goal =.. [Pred_name, Arg1, Arg2], Goal

使用call/N+1更容易:

call(Pred_name, Arg1, Arg2)

因此您的定义将变为:

map_f(Pred_name, L1, L2) :-
    findall(Y, ( member(X, L1), call(Pred_name, X, Y) ), L2).

但是,实际上,如果只有列表,那么所有这些都是完全不必要的。您可以像这样使用maplist/N+1

?- maplist(double, [3,1,2,6,3,1,6], R).
R = [6, 2, 4, 12, 6, 2, 12].

...遍历列表,而不是回溯它们。例如,您可以在此处查看maplist的实现:

https://github.com/SWI-Prolog/swipl-devel/blob/2d20d4e8ac28adfcede7a9bd231ea0d9d12d0bbb/library/apply.pl#L195-L205

如果谓词是真实关系(因此,如果双向都起作用),则也可以同时使用maplistfindall无法做到!这是一个愚蠢的例子:

?- maplist(succ, [1,2,3], R).
R = [2, 3, 4].

?- maplist(succ, R, [1,2,3]).
R = [0, 1, 2].

?- map_f(succ, [1,2,3], R).
R = [2, 3, 4].

?- map_f(succ, R, [1,2,3]).
ERROR: Arguments are not sufficiently instantiated