2016-04-07 41 views
5

Sto cercando di capire come creare un predicato in prolog che sommi i quadrati dei soli numeri pari in una data lista.Creazione di un predicato in Prolog che somma i quadrati dei soli numeri in una lista

risultato atteso:

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

Sum = 120 ; 

false. 

Quello che so fare è quello di rimuovere tutti i numeri dispari da una lista:

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

che mi dà:

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

E So anche come sommare tutti i quadrati dei numeri in una lista:

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

Ma non riesco a capire come collegare questi due insieme. Sto pensando che forse ho sbagliato strada ma non sono sicuro di come definire le relazioni appropriate per farlo avere un senso.

Grazie!

+0

Idealmente, i programmi Prolog sono relazioni * puri *. Ciò significa che dovrebbero essere utilizzabili in tutte le direzioni, anche nel caso più generale. Ad esempio, vorremmo anche ottenere risposte per '? - sumsq_even (Ls, Sum) .'. Controlla le risposte fornite da @repeat e @tas per questa generalità. – mat

risposta

2

Dividi il tuo problema in parti più piccole. Come già detto, si dispone di due funzionalità differenti che dovrebbero essere combinati:

  • rimuovere i numeri dispari da un elenco (even)
  • somma tutti i quadrati dei numeri in un elenco (sumsq)

Quindi, in primo luogo, utilizzare nomi diversi predicato per diverse funzionalità:

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. 

in un terzo predicato si c un ora combinare i due successivi passaggi più piccoli:

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

In questa regola, prima il (ingresso) elenco si riduce a anche elementi (Even_List) e dopo che la somma dei quadrati vengono calcolati.

questo è il risultato per il tuo esempio:

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

si può effettivamente fare entrambe le attività (che filtrano il numero pari e li riassumono) in una sola volta:

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

Interrogare il predicato dà la risultato desiderato:

È possibile scrivere anche più breve utilizzando if_/3 come definito 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). 

nota che il confronto nel primo argomento di IF_/3 viene fatto con =/3 come definito here.

+1

Wow! Funziona in tutte le direzioni! – mat

+1

Un dettaglio è in realtà un po 'fuori ... Che dire sommando ** piazze **? – repeat

+1

@repeat: che imbarazzo. Era proprio davanti ai miei occhi. Grazie per il suggerimento, ho corretto il mio codice. – tas

0

Una volta acquisite le nozioni di base, potreste essere interessati a conoscere i builtin.Biblioteca aggregate, fornisce un modo semplice per gestire le liste, usando membro/2 elenchi elementi 'di accesso':

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

Non c'è bisogno di usare if-then-else qui. 'C = 0' non contribuisce alla somma! – repeat