Esercizio Prolog

One2
Ho appena iniziato a studiare il linguaggio Prolog,ho provato a svolgere questo esercizio:
Scrivere un programma in PROLOG per la seguente relazione: $lunpar(L,N)$ se e solo se $N$ e il numero di elementi pari nella lista $L$.
L'ho svolto così:
lunpar([],0).
lunpar([A|L],N):- A mod 2 is 0,X is X+1,X=<N,lunpar(L,X).
lunpar([A|L],N):- A mod 2 isnot0,lunpar(L,N).

Non sono affatto sicuro di averlo svolto correttamente,mi potete dire se/quali errori ho commesso?

Risposte
apatriarca
Utilizza il tag code per inserire il codice, non il tag matematico. Non sono sicuro inoltre di aver compreso il significato del testo dell'esercizio.

apatriarca
Se l'esercizio è quello di stabilire se N è il numero di elementi pari di L, allora il codice così com'è non è corretto. In effetti non compila neanche sul mio interprete Prolog (cosa utilizzi?). Non capisco comunque che cosa stai cercando di fare con
X is X+1

One2
Se l'esercizio è quello di stabilire se N è il numero di elementi pari di L

Si.
Non capisco comunque che cosa stai cercando di fare con
Codice: Seleziona tutto
X is X+1

La mia idea era di incrementare $X$ tutte le volte che nella lista compare un numero pari;per poi controllare se $X<=N$,però poi non saprei come vedere se $X=N$.
Ho provato così:
lunpar([],N):-X==N. %Dovrebbe controllare se,a lista finita,X=N!!!
lunpar([A|L],N):- A mod 2 is 0,X is X+1,lunpar(L,N).
lunpar([A|L],N):- A mod 2 is not 0,lunpar(L,N).

apatriarca
Per prima cosa sono tutt'altro che un esperto di Prolog. Ma prima di tutto scriverei 1 al posto di not 0. Prima di tutto perché la mia versione di Prolog non accetta la tua versione con il not e poi perché scrivendo 1 è tutto più esplicito è chiaro. Venendo poi al tuo problema di X ed N, devi per prima cosa legare le due variabili. Per cui devi al massimo scrivere N is X+1. Inoltre devi richiamare lunpar ricorsivamente passandogli X e non N (se no valuta sempre gli stessi argomenti). Infine nella prima riga devi scrivere 0 al posto di N come avevi già scritto nel primo codice. Era corretto. Con queste modifiche il codice diventa:
lunpar([],0).
lunpar([A|L],N):- A mod 2 is 0, N is X+1, lunpar(L,X).
lunpar([A|L],N):- A mod 2 is 1, lunpar(L,N).

Per far funzionare il codice ho a questo punto dovuto fare qualche modifica. Per prima cosa cambiare A mod 2 is 0 in 0 is A mod 2 ha fatto un'enorme differenza. Facendo questa semplice modifica mi ha infatti informato che il successivo is non era sufficientemente definito. Per cui la soluzione è stata di scambiare di posizione la ricorsione e questa definizione. Continuando la ricorsione immediatamente arriva ad un certo punto alla fine della lista e a questo punto sa quanto deve valere la corrispondente variabile e può tornare indietro verificando quindi tutte le condizioni.
lunpar([], 0).
lunpar([A | L], N) :- 0 is A mod 2, lunpar(L, X), N is X + 1.
lunpar([A | L], N) :- 1 is A mod 2, lunpar(L, N).


La versione ricorsiva di lunpar non è però molto efficiente in quanto non è tail recursive e ha bisogno di molto spazio se la lista è molto grande. Una versione più "costruttiva" di lunpar è la seguente. Immagino che sia comunque meglio consegnare la versione più simile alla tua, ma credo possa essere istruttivo vedere una soluzione diversa.
lunpar2(L, N) :- lunpar2cl(L, 0, N).
lunpar2cl([],N,N).
lunpar2cl([A | L], C, N) :- 0 is A mod 2, C1 is C+1, lunpar2cl(L,C1,N).
lunpar2cl([A | L], C, N) :- 1 is A mod 2, lunpar2cl(L,C,N).

One2
Innanzitutto grazie per la risposta!
Continuando la ricorsione immediatamente arriva ad un certo punto alla fine della lista e a questo punto sa quanto deve valere la corrispondente variabile e può tornare indietro verificando quindi tutte le condizioni.

Non ho ben capito cosa vuoi dire.
Forse ho intepretato male il codice della versione ricorsiva,ma in questo modo mi limito a contare i valori pari contenuti nella lista,ma non capisco dove controla se il numero di elementi pari è uguale ad $N$

apatriarca
Per ogni valore pari della lista N viene decrementato per cui se si arriva alla fine della lista con N = 0, N era corretto. Nell'altro codice invece viene incrementato un contatore per ogni valore pari trovato e quindi confrontato direttamente con N.

One2
Per ogni valore pari della lista N viene decrementato per cui se si arriva alla fine della lista con N = 0, N era corretto.

Ho capito il ragionamente,ma non capisco dove avviene questo decremento nel codice (sempre nella versione ricorsiva).
Cioè se il valore di testa è pari incremento $X$ (OK),poi effettuo la rocorsione
lunpar(L,X)
(OK),può darsi che sia
N is X-1
invece di
N is X+1
:?:

apatriarca
Ma no.. Devi infatti avere che X è uguale a N-1.. Per cui N è uguale a X+1.

One2
Ma no.. Devi infatti avere che X è uguale a N-1.. Per cui N è uguale a X+1.

Giusto!
Approfitto per chiedere un'(altra)mano per risolvere il seguente esercizio:
sotlist(X,K,Y) è vera se è solo se Y e la sottolista data dal pre fisso di K elementi (primi K elementi) della lista X.
Io l'ho svolto così:(è abbastanza simile all'esercizio di prima)
sotlist(A,0,[]).
sotlist([],0,[]).
sotlist(A|X,K,B|Y):-A is B,Z is K-1,sotlist(X,Z,Y).

Premesso che non sò se il codice scritto sia corretto,mi è venuto il dubbio che debba mettere una "condizione" aggiuntiva quando $A$ è diverso da $B$ :?:

apatriarca
Per prima cosa, dal poco che so di Prolog, non credo che si possano avere variabili nei fatti. Questo è almeno vero su SWI-Prolog (che ho installato). Per indicare che il primo argomento possa essere qualsiasi si usa l'underscore '_' . Non è necessario scrivere due fatti per questo caso particolare. E' sufficiente (e anche più corretto credo) scrivere:
sotlist(_,0,[]).

La seconda regola è comunque sbagliata. Per prima cosa, quando vuoi che due cose sia uguali, è sufficiente usare la stessa variabile per indicarle. Dopodiché le parentesi quadre intorno ad A|X e A|Y credo siano necessarie (con funziona, senza no):
sotlist(_, 0, []).
sotlist([A|X], K, [ A|Y]) :- Z is K-1,sotlist(X,Z,Y).

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.