Ripasso di teoria ed esercizi in preparazione al terzo compito ***DATA*** Teoria: domande “tipo” per il terzo compitino 1. Metaprogrammazione: In cosa consiste la metaprogrammazione? Quali sono alcuni campi di applicazione tipici per la metaprogrammazione? Quali sono i vantaggi dell’uso di metainterpreti in Prolog? Qual è la differenza tra il metainterprete banale, il metainterprete vaniglia ed il metainterprete che riprogramma la funzione di selezione? In cosa consiste il forward reasoning? Che differenza c’è tra backward e forward reasoning? In cosa consiste la adduzione? In che modo è possibile implementare meccanismi di ereditarietà tra teorie? In cosa consiste il “ragionamento approssimato”? 2. Tecniche di AI per problem solving: Cosa si intende con euristica? In che modo può essere rappresentato un problema in Prolog, in modo da adottare tecniche di AI per risolverlo? Quali sono le tecniche di ricerca sullo spazio degli stati? In cosa differiscono? Come si possono implementare in Prolog? Cosa si intende con euristica? Cosa sono gli “and/or” graph? Come si può applicare l’euristica ai grafi and/or? Illustrare lo schema generale di un programma Prolog che utilizza di alberi di ricerca per giochi. Illustrare l’algoritmo minimax. Problem solving Dato un problema bisogna rappresentarlo e risolverlo. Il problema si può rappresentare tramite le configurazioni del problema stesso, ovvero i possibili stati in cui il problema si può trovare. Lo spazio degli stati è rappresentabile come un grafo in cui i nodi sono le configurazioni e gli archi sono le azioni che fanno muovere da una configurazione ad un’altra. Dovranno essere anche rappresentati lo stato iniziale e lo stato finale. Con la rappresentazione a grafo, trovare una soluzione corrisponde a trovare un cammino dallo stato iniziale allo stato finale. Il bersaglio Data una parola di senso compiuto, modificarla (eventualmente ripetutamente) in uno dei seguenti modi al fine di ottenere un’altra parola di senso compiuto (stato finale): – aggiungere una lettera in un punto qualunque della parola (purché la parola ottenuta sia sempre di senso compiuto e non sia più lunga di 8 lettere); – rimuovere una lettera da un punto qualunque della parola (purché la parola ottenuta sia sempre di senso compiuto e non sia più corta di 3 lettere); – cambiare una lettera della parola. Il bersaglio Rappresentazione del problema: ogni configurazione consiste nella lista di lettere che compongono la parola. Stato iniziale: [c, a, r, t, a] (verra’ dato come parametro in input alla strategia di ricerca) Stato finale: final([m, o, s, t, r, o]). Configurazioni intermedie: liste di lettere accettabili secondo le regole del gioco. Il bersaglio Mosse possibili: aggiungere e togliere lettere rispettando i vincoli imposti dal problema. move(Parola, NuovaParola) :letter(X), insert(X, Parola, NuovaParola), senso_compiuto(NuovaParola). move(Parola, NuovaParola) :remove(Parola, NuovaParola), senso_compiuto(NuovaParola). Il bersaglio move(Parola, NuovaParola) :letter(X), exchange(X, Parola, NuovaParola), senso_compiuto(NuovaParola). Vanno definiti tutti i predicati usati in “move”. Il bersaglio Tecniche di ricerca adottabili: quelle viste a lezione. • Depth-first search strategy (con e senza controllo di cicli). • Breadth-first strategy. Euristica applicata alla ricerca Idea: ridurre lo spazio di ricerca con valutazioni empiriche basate sulla natura del problema. Locale: Hill climbing; globale: best-first search. Non ci saranno esercizi sulla parte di implementazione in Prolog di algoritmi che integrano l’euristica nella strategia di ricerca. Potranno però esserci dei problemi sulla modellazione di euristiche per risolvere efficaciemente un dato problema. Il bersaglio con euristica Potremmo assumere che il costo di una mossa sia sempre 1, quindi c(N, N’) = 1 per ogni coppia di configurazioni (N, N’) tali che esiste una mossa da N a N’. La difficoltà di una configurazione è la distanza di quella configurazione dallo stato finale, quindi f(N) = difficoltà della configurazione N = distanza dalla configurazione finale. La distanza di due configurazioni nel gioco del bersaglio la calcoliamo come somma delle lettere diverse a parità di posizione. Se una configurazione è più lunga dell’altra, si somma il numero di lettere in cui le due lunghezze differiscono. Il bersaglio con euristica f([m,o,s,t,o]) = 2 f([c,a,r,t,a]) = 5 f([m,a,s,t,r,o]) = 1 Metainterpreti Realizzare un meta-interprete che modifica la strategia con cui vengono usate le clausole in un programma, utilizzando le clausole in ordine inverso (dal basso, ovvero dall’ultima clausola che compare nel programma definente il predicato in oggetto, verso l’alto, ovvero verso la prima clausola che compare nel programma per definire il predicato in oggetto). Inversione delle clausole solve_revert_clauses(Programma, Goal):asserisci_clausole_invertite(Programma), call(Goal). asserisci_clausole_invertite([]). asserisci_clausole_invertite([H|Tail]) :asserta(H), asserisci_clausole_invertite(Tail). Metainterpreti Realizzare un meta-interprete che modifica l’algoritmo di unificazione. Il nuovo algoritmo deve soddisfare le seguenti specifiche: • due costanti non unificano mai (anche se sono uguali); • nel caso in cui i termini da unificare siano entrambi variabili, o il termine da unificare sia una variabile ed il termine che unifica sia una costante, l’unificazione avviene normalmente. Si noti che se il termine da unificare è una costante ed il termine che unifica è una variabile l’unificazione fallisce; Metainterpreti • nel caso in cui i due termini siano entrambi composti, l’unificazione avviene se i funtori ed il numero di argomenti sono identici, e gli argomenti unificano a due a due applicando ricorsivamente le suddette regole. Unificazione ridefinita solve_unif(true) :- !. solve_unif((A, B)) :- solve_unif(A), solve_unif(B), !. solve_unif(Goal) :functor(Goal, F, A), functor(Templ, F, A), clause(Templ, Body), unificano_termini(Goal, Templ), solve_unif(Body). Unificazione ridefinita unificano_termini(Goal, Templ) :Goal =.. [_|Arg], Templ =.. [_|Arg1], unificano(Arg, Arg1). unificano([], []). unificano([H|Tail], [H1|Tail1]) :var(H), H = H1, unificano(Tail, Tail1). Unificazione ridefinita unificano([H|Tail], [H1|Tail1]) :compound(H), compound(H1), H =.. [A|Arg], H1=..[A|Arg1], unificano(Arg, Arg1), unificano(Tail, Tail1).