From: LeonardoBizzoni Date: Wed, 10 Jan 2024 09:15:45 +0000 (+0100) Subject: Finita documentazione Prolog X-Git-Url: http://git.leonardobizzoni.com/?a=commitdiff_plain;h=f32944969004a96050c2395da69014af10b1776f;p=ObjectOriented-Prolog-Lisp Finita documentazione Prolog --- diff --git a/Prolog/README.org b/Prolog/README.org index ce52d9e..06c5d01 100644 --- a/Prolog/README.org +++ b/Prolog/README.org @@ -10,14 +10,27 @@ * Primitive principali ** def_class *** Definizione -Definisce la struttura di una classe, dato: -- un nome -- una lista di genitori -- una lista di `field`/`metodi` - -Per definire la def_class si inseriscono variabili -che rappresentano ed i metodi che definiscono il comportamento -della classe. +Definisce una classe dato un atomo rappresentante il nome della classe, +una lista possibilmente vuota di /genitori/ +(/classi definite in precedenza da cui ereditare/) e un'opzionale lista +contenente la definizione di proprietà nella forma +/field(name, value, type)/ oppure /field(name, value)/ e la definizione +di metodi nella forma /method(name, args, body)/. + +In particolare il corpo di un metodo della classe deve essere dato in +forma normale congiunta. + +Il risultato della definizione della classe è la semplice aggiunta, +tramite /assert*/, alla base di conoscenza delle clausole dinamiche: +- /is_class/ per affermare che l'atomo è il nome di una classe definita +- /is_child_of/ per creare una gerarchia di classi + (/utile nel controllo dei tipi/) +- /is_member/ per specificare che fields e methods appartengono + alla classe /ClassName/ + +In caso di errori durante la definizione della classe, è necessario +rimuovere tutte le informazioni rimaste nella base di conoscenza +riguardanti la classe /ClassName/ prima di restituire un fallimento. *** Implementazione #+begin_src prolog :tangle oop.pl @@ -126,26 +139,37 @@ def_class(node, [], [field(value, 0, integer), ** make *** Definizione -Istanzia una classe dato: -- il nome dell'istanza -- la classe da istanziare -- una lista di `assegnamenti` - -Sostanzialmente craiamo il costruttore della classe. - -Prima di creare l'istanza si controlla che l'atono non sia -già un'istanza di una classe. -Se esiste non si procede con l'operazione, in quanto rindondante, -in caso contrario si passa alla creazione effettiva dell'istanza. - -Dopo di che: -- Se non viene dato il campo /Fields/ allora si crea un - istanza con i valore di default della classe. -- Se abbiamo il campo /Fields/ associamo ai fields dell'istanza i valori - di default e successivamente quelli presenti in /Fields/. -- Se fallisce la creazione dell'istanza si elimanano le eventuali - informazioni residue dalla base di conoscenza. - +Questo predicato svolge 2 compiti fondamentali in base a cosa gli viene +dato in ingresso. + +**** Definizione di un'istanza +Dato un atomo rappresentante il nome dell'istanza da creare, un atomo +rappresentante una classe *necessariamente* definita in precedenza ed +un'opzionale lista di "assegnamenti" a field della classe, +crea l'istanza /InstanceName/. + +Come per la [[*def_class][definizione di una classe]] ciò avviene tramite modifiche +alla base di conoscenza utilizzando i predicati dinamici: +- /is_instance/ per affermare che l'atomo /InstanceName/ è un'istanza + ed in particolare è un'instanza della classe /ClassName/ +- /field/ per creare relazioni tra i vari campi di una classe e l'istanza + +Se dovessero verificarsi degli errori durante la creazione dell'istanza +le informazioni residue vengono rimosse tramite /retract/ prima +di restituire un fallimento. + +**** Ricerca di un'istanza +Data una *variabile* come nome d'istanza, un atomo rappresentante una +classe *necessariamente* definita in precedenza ed un'opzionale lista +di "assegnamenti" a field della classe, cerca all'interno della +base di conoscenza un'istanza precedentemente definita. + +Questo predicato sfrutta il potente meccanismo di back-tracking di +Prolog per cercare di unificare la variabile /InstanceName/ con +un'istanza della classe /ClassName/ che, se la lista di "assegnamenti" +non è nulla, abbia i valori dei field specificati. +/Naturalmente se non si ha nessun "assegmaneto" allora InstanceName/ +/unifica con tutte e sole le istanze della classe specificata./ *** Implementazione #+begin_src prolog :tangle oop.pl @@ -194,12 +218,10 @@ make(Instance, bar, [bar = "69"]). %% Instance = b; false. ** field *** Definizione -Assegna dinamicamente ed estrae il valore di un field di un'istanza. - +Assegna tramite /assert*/ e recupera il valore di un field data un'istanza. Questa funzione svolge lo stesso compito di un getter/setter in -un *inferiore* linguaggio ad oggetti, ovvero una funzione che fornisce -un modo controllato per accedere al valore di un attributo all'interno di -un'istanza. +un *inferiore* linguaggio ad oggetti sfruttando il meccanismo di +back-tracking. *** Implementazione #+begin_src prolog :tangle oop.pl @@ -217,13 +239,17 @@ field(root, value, X). %% X = 200 ** fieldx *** Definizione -Estrae il valore dalla classe percorrendo una catena di attributi. -Il caso base del metodo è quando /FieldName/ è una lista formata da -un solo elemento, con il predicato /field/ possiamo estrattre l'ultimo -(e solo) valore di /FieldName./ -Se /FieldName/ ha più elementi possiamo richiamare il metodo ricorsivamente -sulla coda della lista finchè la lista non avrà un solo elemento -riportandoci al caso base. +Data un'istanza, sia essa una variabile o un atomo, ed una lista +non vuota di field, estrae il valore associato all'ultimo elemento +della lista di field dell'ultima istanza. + +Questo predicato e la seguente clausola sono equivalenti. +#+begin_src prolog +field(instance, field1, Valore1), +field(Valore1, field2, Valore2), +field(Valore2, field3, Risultato), +fieldx(instance, [field1, field2, field3], Risultato). %% true. +#+end_src *** Implementazione #+begin_src prolog :tangle oop.pl @@ -243,14 +269,18 @@ fieldx(fb, [foo,bar,foobar], Result). ** inst *** Definizione -Dato un oggetto che rappresenta un'istanza restituisce il nome -dell'istanza. -Si nella nostra implementazione è banale perchè l'oggetto che rappresenta -l'istanza è il nome dell'istanza. +Dato il nome con cui è stata create l'istanza restituisce l'istanza +stessa. + +Dato che nella nostra implementazione la creazione di un'istanza è la +semplice aggiunta alla base di conoscenza del fatto +/is_instance(InstanceName)/ questo predicato si limita a controllare +che il nome dato in ingresso è un'istanza. *** Implementazione #+begin_src prolog :tangle oop.pl -inst(InstanceName, InstanceName). +inst(InstanceName, InstanceName) :- + is_instance(InstanceName). #+end_src *** Esempio pratico @@ -263,8 +293,8 @@ inst(fb, root). %% false. * Predicati helper ** Predicati dinamici -Predicati usati dal metodo /asserta/, svolgono una funzione di controllo -per diverse entità. +Predicati utilizzati da /assert*/ all'interno delle altre funzioni +per tenere traccia di cosa rappresentano i simboli definiti. #+begin_src prolog :tangle oop.pl :- dynamic is_class/1. @@ -275,20 +305,20 @@ per diverse entità. #+end_src ** Aggiunta di `field` e `metodi` -Data una classe devo poter dire che un `field` appartiene alla classe -che sto definendo. -Se non ci sono `field`/`metodi` da bindare allora è finita la definizione -delle parti della classe. +Le seguenti regole servono per creare relazioni tra una classe ed i suoi +*membri* ovvero /field/ e /methods/. +Di base, il successo di questo predicato dipende dal fatto che il simbolo +dato in ingresso *deve* essere una classe precedentemente definita. +In caso contrario non sarebbe possibile creare una relazione con i membri. #+begin_src prolog :tangle oop.pl add_part(ClassName, []) :- is_class(ClassName), !. #+end_src -Se viene dato un `field` privo di tipo controlliamo che non sia già presente -nella base di conoscenza e se lo è lo cancelliamo sennò facciamo il -binding di classe e field dicendo che il tipo è `nil` e quindi -dinamico(stile *python*). - +Dato un field da aggiungere alla classe è necessario controllare che +quest'ultimo non sia già in relazione con la classe, questo vorrebbe +dire che il campo è già stato ereditato ed è quindi necessario effettuare +l'override con il nuovo valore. #+begin_src prolog :tangle oop.pl add_part(ClassName, [field(Name, Value) | OtherParts]) :- is_class(ClassName), @@ -314,9 +344,9 @@ add_part(ClassName, [field(Name, Value) | OtherParts]) :- add_part(ClassName, OtherParts). #+end_src -Se invece viene dato un `field` come prima controlliamo se è già definito -ma inoltre controlliamo che il `value` sia dello stesso tipo di `type`. - +Similarmente al caso precedente se il field da aggiungere è anche +dotato di un /type/ allora viene effettuato il controllo del tipo +del valore prima di modificare la base di conoscenza. #+begin_src prolog :tangle oop.pl add_part(ClassName, [field(Name, Value, Type) | OtherParts]) :- is_class(ClassName), @@ -347,9 +377,22 @@ add_part(ClassName, [field(Name, Value, Type) | OtherParts]) :- add_part(ClassName, OtherParts). #+end_src -Se viene dato un `method` allora definiamo nella base di conoscenza un -predicato `Name(InstanceName, ArgList)`. - +Il caso dell'aggiunta di un metodo è particolare, in quanto possiamo +ignorare il fatto che il metodo potrebbe essere già stato definito +da un genitore. +Questo è possibile aggiungendo l'operatore /cut/ al corpo del "metodo" +impedendo quindi al back-tracking di esplorare le definizioni alternative +dello stesso "metodo". + +L'/installazione/ del metodo viene fatta aggiungendo alla +base di conoscenza la funzione /Name(InstanceName, Args)/ ottenuta +dall'operatore prolog "=.." che ricrea il termine composto /Head/ dalla lista +contenente le informazioni del metodo dato in ingresso. + +Inoltre dato che all'interno del corpo deve essere possibile utilizzare +l'atomo /this/ per fare riferimento all'instanza stessa viene effettuata +un'operazione di sostituzione di quest'ultimo con una variabile che +unificherà con il nome dell'istanza su cui dobbiamo chiamare il "metodo". #+begin_src prolog :tangle oop.pl add_part(ClassName, [method(Name, ArgList, Body) | OtherParts]) :- is_list(ArgList), @@ -366,8 +409,10 @@ add_part(ClassName, [method(Name, ArgList, Body) | OtherParts]) :- #+end_src ** Impostazione dei field ereditati -Mi dai una lista di `field` presi da una superclass e fai il binding anche -con questa classe. +Predicato usato durante la creazione di un'istanza. +Data l'istanza ed i field della classe genitore, controlla che essi siano +del tipo corretto e modifica la base di conoscenza aggiungendo una +relazione tra ogni field e il simbolo che rappresenta l'istanza. #+begin_src prolog :tangle oop.pl set_superclass_fields(_InstanceName, _ClassName, []) :- !. @@ -382,8 +427,8 @@ set_superclass_fields(InstanceName, #+end_src ** Costruzione di un'istanza -Fai il binding tra i `field` di un classe e quelli di un'istanza come -`add_part`. +Predicato equivalente ad /add_part/ per l'impostazione dei field +di un'istanza. #+begin_src prolog :tangle oop.pl set_fields(_InstanceName, _ClassName, []) :- !. @@ -419,7 +464,7 @@ set_fields(InstanceName, _ClassName, _Fields) :- ** Sostituzione del termine `this` Predicato utilizzato per sostituire l'atomo /this/ con la vera istanza. Dato un predicato /Body/ questo viene separato nei suoi termini costituenti -e (/funtore e argomenti/) *per ogni* argomento richiama la /replace/. +(/funtore e argomenti/) e *per ogni* argomento richiama la /replace/. In questo modo possiamo cercare l'atomo da sostituire anche all'interno di termini composti come chimate di funzioni. @@ -479,12 +524,19 @@ type_check(ClassName, Instance) :- ** Definizione di una classe *** Effettivo predicato `def_class` -La definizione di una classe richiede che `classname` non sia già il nome -di una classe pre-esistente nella base di conoscenza. -Una volta effettuato questo controllo possiamo ereditare tutti -i field ed i metodi dalle classi genitore se presenti e creare relazioni -tra le /parts/ della classe da creare ed il nome della classe stessa grazie -al predicato /add_part/. +Predicato utilizzato per la definizione della classe ricevuta da +[[*def_class][def_class]]. +Dato che questa funzione utilizza /assert*/ per modificare la +base di conoscenza, se dovessore verificarsi degli errori queste +modifica rimarrebbero presenti. +Per ciò non è consigliato utilizzare /def_class/ al suo posto. + +Prima di aggiungere le informazioni della nuova classe alla base +di conoscenza vengono ereditate tutte le parti (/field e metodi/) +dalla lista di genitori. +Questo viene fatto per permettere ad una classe di estendere il +comportamento dei metodi o di cambiare valore/tipo dei parametri +definiti dalle classi genitore. #+begin_src prolog :tangle oop.pl wrapped_def_class(ClassName, Parents, Parts) :- @@ -492,6 +544,7 @@ wrapped_def_class(ClassName, Parents, Parts) :- nonvar(Parents), nonvar(Parts), \+ is_class(ClassName), + atom(ClassName), set_superclass(ClassName, Parents), asserta(is_class(ClassName)), @@ -505,6 +558,9 @@ Se questo non è una classe allora non possiamo ereditare niente! Dopo di chè creiamo una relazione tra il genitore e la classe figlio che creerà una gerarchia di classi utile nel [[*Controllo dei tipi][controllo dei tipi]]. +Questo permette ad un'istanza della classe figlio di +accedere *direttamente* ad ogni campo/metodo definito da una +classe genitore. #+begin_src prolog :tangle oop.pl set_superclass(_ClassName, []) :- !. diff --git a/Prolog/oop.pl b/Prolog/oop.pl index b63a1f4..91dc19b 100644 --- a/Prolog/oop.pl +++ b/Prolog/oop.pl @@ -60,10 +60,11 @@ fieldx(Instance, [FieldName], Res) :- !. fieldx(Instance, [FieldName | Others], Res) :- is_instance(Instance), - field(Instance, FieldName, _Value), - fieldx(Instance, Others, Res). + field(Instance, FieldName, Value), + fieldx(Value, Others, Res). -inst(InstanceName, InstanceName). +inst(InstanceName, InstanceName) :- + is_instance(InstanceName). :- dynamic is_class/1. :- dynamic is_child_of/2. @@ -223,6 +224,7 @@ wrapped_def_class(ClassName, Parents, Parts) :- nonvar(Parents), nonvar(Parts), \+ is_class(ClassName), + atom(ClassName), set_superclass(ClassName, Parents), asserta(is_class(ClassName)),