2016-04-07 4 views
5

Próbuję dowiedzieć się, jak utworzyć predykat w prologu, który sumuje kwadraty tylko liczb parzystych z danej listy.Tworzenie predykatu w Prologu, który sumuje kwadraty tylko liczb parzystych na liście

wyjście z oczekiwaniami:

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

Sum = 120 ; 

false. 

Co wiem, jak zrobić, to usunąć wszystkie numery nieparzyste z listy:

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). 

co daje mi:

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

I Wiem również, jak sumować wszystkie kwadraty liczb na liście:

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

Ale nie mogę wymyślić, jak połączyć te dwa elementy. Myślę, że mogłem pójść w niewłaściwą stronę, ale nie jestem pewien, jak zdefiniować właściwe relacje, aby było to sensowne.

Dzięki!

+0

Idealnie, twoje programy Prolog są czyste * relacje *. Oznacza to, że powinny być użyteczne we wszystkich kierunkach, na przykład w najbardziej ogólnym przypadku. Na przykład chcielibyśmy uzyskać odpowiedzi na '? - sumsq_even (Ls, Sum).". Sprawdź odpowiedzi udzielone przez @repeat i @tas dla tej ogólności. – mat

Odpowiedz

2

Podziel swój problem na mniejsze części. Jak już powiedziałem, masz dwie różne funkcje, które powinny być łączone: numery nieparzyste

  • Usuń z listy (even)
  • suma wszystkich kwadratów liczb na liście (sumsq)

Tak więc, w pierwszej kolejności należy użyć różnych nazw bazowych dla różnych funkcji:

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. 

w trzecim orzecznika ty c teraz łączyć dwa kolejne mniejsze etapy:

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

W zasadzie pierwszy (wejście) Lista jest skrócony nawet elementy (Even_List) i po że suma kwadratów są obliczane.

Jest to wynik dla przykładu:

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

Można faktycznie oba zadania (filtrowanie parzystą liczbę i zsumowaniu ich w górę) na raz:

:- 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). 

Zapytania orzecznik daje pożądany wynik:

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

Możesz napisać jeszcze krótszy używając if_/3 zgodnie z definicją 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). 

Należy zauważyć, że porównanie z pierwszego argumentu if_/3 jest wykonane z =/3, jak zdefiniowano here.

+1

Wow! Działa we wszystkich kierunkach! – mat

+1

Fragment jest w rzeczywistości trochę niecelny ... A co z sumowaniem ** kwadratów **? – repeat

+1

@repeat: jak żenujące. To było tuż przed moimi oczami. Dzięki za podpowiedź, poprawiłem swój kod. – tas

0

Gdy opanujesz podstawy, możesz chcieć dowiedzieć się więcej o wbudowanych funkcjach.Biblioteka aggregate, zapewnia prosty sposób obsługi list, używając elementu/2 lista elementów 'akcesor':

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

Nie ma potrzeby używania polecenia if-then-else tutaj. 'C = 0' nic nie wnosi do sumy! – repeat

Powiązane problemy