簡體   English   中英

Prolog中列表的偶數和奇數之和

[英]Sum of even and odd numbers of a list in Prolog

我需要做的是編寫一個帶有數字列表的謂詞,並返回一個由兩個元素組成的列表,第一個是偶數的和,第二個是奇數之和。

例如:

?- sum([5,4,9,8,1,7], L).
L = [12, 22].

到目前為止我寫了:

iseven(N) :-
    0 is mod(N,2). 

既然你已經定義了iseven/2就可以使用它:

sum([],[0,0]).
sum([H|T],[N1,N2]):- 
             sum(T,[N3,N4]),
             ( iseven(H) 

              ->  N1 is N3+H, N2 is N4
              ;   N2 is N4+H, N1 is N3

             ).

例:

?-sum([5,4,9,8,1,7], L).
L = [12, 22].

使用不同子句的非if-then-else版本:

sum([],[0,0]).
sum([H|T],[N1,N2]):- sum(T,[N3,N2]), iseven(H), N1 is N3+H.
sum([H|T],[N1,N2]):- sum(T,[N1,N3]), \+ iseven(H), N2 is N3+H.

您也可以使用accumulators和if_ / 3編寫此謂詞。 此外,您可以將iseven/1的單個目標合並到謂詞中:

list_sums(L,S) :-
   list_sums_(L,S,0-0).

list_sums_([],[E,O],E-O).
list_sums_([X|Xs],S,E0-O0) :-
   M is X mod 2,
   if_(M=0,(E1 is E0+X, O1 is O0),(E1 is E0, O1 is O0+X)),
   list_sums_(Xs,S,E1-O1).

注意累加器是如何寫成一對( EO )的。 如果您可以自由選擇兩個總和的表示,則此對符號將是具有兩個元素的列表的替代。 您的示例查詢產生了所需的結果:

?- list_sums([5,4,9,8,1,7],L).
L = [12, 22].

而評論中的示例終止得更快:

?- length(L,100),maplist(=(1),L),time(list_sums(L,[A,B])).
% 703 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 3426895 Lips)
L = [1, 1, 1, 1, 1, 1, 1, 1, 1|...],
A = 0,
B = 100.

但是,由於使用is/2這只能在一個方向上工作。 如果您還想在另一個方向使用謂詞,則可以選擇使用CLP(FD)。 在那種情況下包括線

:- use_module(library(clpfd)).  

在源文件中,用#=/2替換list_sums_/3所有出現的is/2 #=/2

list_sums_([],[E,O],E-O).
list_sums_([X|Xs],S,E0-O0) :-
   M #= X mod 2,
   if_(M=0,(E1 #= E0+X, O1 #= O0),(E1 #= E0, O1 #= O0+X)),
   list_sums_(Xs,S,E1-O1).

您的示例查詢仍會產生相同的結果,並且注釋中的示例在可接受的時間內終止:

?- length(L,100),maplist(=(1),L),time(list_sums(L,[A,B])).
% 18,928 inferences, 0.004 CPU in 0.004 seconds (99% CPU, 4888152 Lips)
L = [1, 1, 1, 1, 1, 1, 1, 1, 1|...],
A = 0,
B = 100.

但這個謂詞現在在兩個方向都有效。 在某些情況下,Prolog可以毫不費力地找到具體的答案:

?- list_sums([A,B],[2,1]).
A = 2,
B = 1 ;
A = 1,
B = 2 ;
false.

在其他情況下,您可以獲得剩余目標作為答案:

?- L=[A,B,C,D,E,F], list_sums(L,[12,22]).
L = [A, B, C, D, E, F],
A+B#=_G3306,
A mod 2#=0,
B mod 2#=0,
_G3306+C#=_G3342,
C mod 2#=0,
_G3342+D#=12,
D mod 2#=0,
E+F#=22,
E mod 2#=_G3402,
F mod 2#=_G3414,
_G3414 in 0..1,
dif(_G3414, 0),
_G3402 in 0..1,
dif(_G3402, 0) ;
...

在這些情況下,您可以將列表的元素限制為某個間隔,並使用label/1來獲取具體數字作為解決方案。 例如,您可以要求提供從0到7的六個數字的解決方案,Prolog將為您提供所有300個解決方案:

?- length(L,6), L ins 0..7, list_sums(L,[12,22]), label(L).
L = [6, 6, 1, 7, 7, 7] ;
L = [6, 6, 3, 5, 7, 7] ;
...

您可以使用函數方式:

one_sum(X, [SE,SO], [SE1, SO1]) :-
    (   X mod 2 =:= 0 ->
        SE1 is SE+X, SO1 = SO
    ;   SE1 = SE, SO1 is SO+X).

sum(L, S) :-
    foldl(one_sum, L, [0,0], S).

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM