在 Prolog 中创建一个谓词,仅对列表中偶数的平方求和

Creating a predicate in Prolog that sums the squares of only the even numbers in a list

我正在尝试弄清楚如何在 prolog 中创建一个谓词,该谓词仅对给定列表中的偶数的平方求和。

预期输出:

?- sumsq_even([1,3,5,2,-4,6,8,-7], Sum).

Sum = 120 ;

false.

我知道该怎么做是从列表中删除所有奇数:

sumsq_even([], []).
sumsq_even([Head | Tail], Sum) :-
    not(0 is Head mod 2),
    !,
    sumsq_even(Tail, Sum).
sumsq_even([Head | Tail], [Head | Sum]) :-  
    sumsq_even(Tail, Sum).

这给了我:

Sum = [2, -4, 6, 8]

而且我还知道如何对列表中所有数字的平方求和:

sumsq_even([], 0)
sumsq_even([Head | Tail], Sum) :-
    sumsq_even(Tail, Tail_Sum),
    Sum is Head * Head + Tail_Sum.

但我似乎无法弄清楚如何将这两者联系在一起。我在想我可能走错了路,但我不确定如何定义适当的关系才能让它有意义。

谢谢!

将您的问题分解成更小的部分。正如您已经说过的,您有两个不同的功能应该结合起来:

  • 从列表中删除奇数 (even)
  • 对列表中所有数字的平方求和 (sumsq)

因此,首先,为不同的功能使用不同的谓词名称:

even([], []).
even([Head | Tail], Sum) :-
    not(0 is Head mod 2),
    !,
    even(Tail, Sum).
even([Head | Tail], [Head | Sum]) :-  
    even(Tail, Sum).

sumsq([], 0).
sumsq([Head | Tail], Sum) :-
    sumsq(Tail, Tail_Sum),
    Sum is Head * Head + Tail_Sum.

在第三个谓词中,您现在可以组合两个后续的较小步骤:

sumsq_even(List, Sum) :-
    even(List, Even_List),
    sumsq(Even_List, Sum).

在此规则中,首先将(输入)列表减少为偶数元素 (Even_List),然后计算平方和。

这是您的示例的结果:

sumsq_even([1,3,5,2,-4,6,8,-7], Sum).
S = 120.

您实际上可以同时完成这两项任务(过滤偶数并将它们相加):

:- use_module(library(clpfd)).

nums_evensumsq([],0).
nums_evensumsq([X|Xs],S0) :-
    X mod 2 #= 0,
    nums_evensumsq(Xs,S1),
    S0 #= S1 + X * X.
nums_evensumsq([X|Xs],S) :-
    X mod 2 #= 1,
    nums_evensumsq(Xs,S).

查询谓词给出了想要的结果:

   ?- nums_evensumsq([1,3,5,2,-4,6,8,-7],S).
S = 120 ? ;
no

您可以使用 if_/3 定义的 here:

来写得更短
nums_evensumsq([],0).
nums_evensumsq([X|Xs],S0) :-
    nums_evensumsq(Xs,S1),
    Y #= X mod 2,
    if_(Y = 0, S0 #= S1 + X * X, S0 #= S1).

请注意,if_/3 的第一个参数中的比较是根据 here 定义的 =/3 完成的。

掌握了基础知识后,您可能会对了解内置函数感兴趣。库 aggregate,提供了一种处理列表的简单方法,使用 member/2 作为列表元素 'accessor':

sumsq_even(Ints, Sum) :-
  aggregate(sum(C), I^(member(I, Ints), (I mod 2 =:= 0 -> C is I*I ; C = 0)), Sum).

使用 and Prolog 写入:

:- use_module(library(clpfd)).
:- use_module(library(lambda)).

zs_sumevensq(Zs, S) :-
   maplist(\Z^X^(X #= Z*Z*(1-(Z mod 2))), Zs, Es),
   sum(Es, #=, S).

OP 给出的示例查询:

?- zs_sumevensq([1,3,5,2,-4,6,8,-7], S).
S = 120.