From 1990b5902785add5185ec1fb41c7fa8cebab223a Mon Sep 17 00:00:00 2001 From: AmnesiA <57435273+Meht-evaS@users.noreply.github.com> Date: Sun, 15 Oct 2023 21:41:47 +0200 Subject: [PATCH] Aggiunte e correzioni appunti OCaml - Aggiunti elementi ed esempi sezione modulo List - Aggiunti alberi n-ari - Aggiunti algoritmi di ricerca e visita Grafi - Corretti simboli singoli apici e virgolette - Corretti errori ortografici --- .../Marcugini/README.md | 601 +++++++++++++++--- .../Marcugini/imgs/albero_nario_ocaml.png | Bin 0 -> 13147 bytes 2 files changed, 501 insertions(+), 100 deletions(-) create mode 100644 magistrale/Anno 1/Artificial Intelligent Systems/Marcugini/imgs/albero_nario_ocaml.png diff --git a/magistrale/Anno 1/Artificial Intelligent Systems/Marcugini/README.md b/magistrale/Anno 1/Artificial Intelligent Systems/Marcugini/README.md index 965555852..b63eeb19e 100644 --- a/magistrale/Anno 1/Artificial Intelligent Systems/Marcugini/README.md +++ b/magistrale/Anno 1/Artificial Intelligent Systems/Marcugini/README.md @@ -49,9 +49,9 @@ + [Funzioni di ordine superiore](#funzioni-di-ordine-superiore) + [Funzioni di forma currificata](#funzioni-di-forma-currificata) + [Liste](#liste) + - [Modulo List](#modulo-list) - [Dizionario](#dizionario) - [In Memoria](#in-memoria) - - [Modulo List](#modulo-list) + [Random](#random) + [Backtracking](#backtracking) + [Definizioni di nuovi tipi](#definizioni-di-nuovi-tipi) @@ -63,6 +63,13 @@ - [Record](#record) - [Riferimento](#riferimento) + [Grafi](#grafi) + + [Algoritmi di visita e ricerca](#algoritmi-di-visita-e-ricerca) + - [DFS](#dfs) + - [BFS](#bfs) + - [Best-First](#best-first) + - [Hill Climbing](#hill-climbing) + - [Branch and Bound](#branch-and-bound) + - [A*](#a-star) + [Cicli](#cicli) - [while](#while) - [for](#for) @@ -200,7 +207,7 @@ let square n = n*n;; #### Prodotto Cartesiano -Dati `A = {0, 1, 2}` e `B = {rosso, verde}`, allora `A x B = {(0, rosso), (0, verde), (1, rosso), (1, verde), (2, rosso), (3, verde)}` +Dati `A = {0, 1, 2}` e `B = {rosso, verde}`, allora `A x B = {(0, rosso), (0, verde), (1, rosso), (1, verde), (2, rosso), (2, verde)}` Questa è la definizione di prodotto cartesiano. @@ -208,7 +215,7 @@ Le funzioni sono quindi un sottoinsieme del prodotto cartesiano di `DOMINIO x CO - ogni $x_i$ appartiene al dominio - ogni $y_i$ appartiene al codominio -- per ogni `x` del dominio, se `(x, y)` e `(x, y')` allora `y = y'`. `x` puo' essere in coppia cono un solo elemento del codominio ! +- per ogni `x` del dominio, se `(x, y)` e `(x, y')` allora `y = y'`. `x` può essere in coppia con un solo elemento del codominio ! - `F(x) = y` allora la coppia `(x, y)` appartiene all'estensione di `F` Un esempio di estensione è quella della funzione `square`: `{(0, 0), (1, 1), (3, 9), (-1, 1), ...}`. @@ -254,7 +261,7 @@ quorem(7, 2) = (3, 2) quorem(15, 6) = (2, 3) ``` L'estensione di quorem è: `{..., (3, 2, (1, 1)), ..., (3, 3, (1, 0)), ...}`. -C'e da notare che quorem non è definito per gli argomenti `(n, 0)` (non si puo' dividere per 0). +C'e da notare che quorem non è definito per gli argomenti `(n, 0)` (non si può dividere per 0). ### Funzioni Totali vs Parziali @@ -390,7 +397,16 @@ k 10 -> 0 ### Funzioni in forma Currificata -Non ci ho capito nulla, forse lo scriverò in futuro :+1: :japanese_goblin: +Data una funzione che ha un parametro composto da 2 valori, ne sfruttiamo uno per utilizzarlo tramite un'altra funzione. + +```ocaml +let add a b = a+b;; (* val add: int -> int -> int = *) + +let add3 = add 3;; (* val add3 : int -> int = *) + +(* Sfrutto add3 per usare la funzione add con un parametro fisso di 3 e un'altro da dovergli passare, per add, con cui fare la somma *) +add3 2;; (* - : int = 5 *) +``` ## OCaml @@ -651,11 +667,11 @@ Un tipo è un insieme di valori e descrive le varie operazioni che possono esser - Note: `# Char.code 'A';;` restituisce il valore intero della tabella ASCII `-: int = 65`. Si può fare anche il contrario: `# Char.chr 65;;`. ```ocaml - # Char.code ’A’;; + # Char.code 'A';; -: int = 65 # Char.chr 65;; - -: char = ’A’ + -: char = 'A' ``` - **`string`**: @@ -670,7 +686,7 @@ Un tipo è un insieme di valori e descrive le varie operazioni che possono esser -: string = "programmazione funzionale" # "ABCDEFG".[2];; - -: char = ’C’ + -: char = 'C' # string_of_int 45;; -: string = "45" @@ -688,9 +704,14 @@ Un tipo è un insieme di valori e descrive le varie operazioni che possono esser Si possono effettuare conversioni di tipi con le seguenti funzioni: ``` -float_of_int int_of_float string_of_float -float_of_string int_of_string string_of_int - int_of_char char_of_int +- int_of_float (* Traforma float in int *) +- float_of_int (* Traforma int in float *) +- string_of_int (* Traforma int in string *) +- string_of_float (* Traforma float in string *) +- int_of_string (* Traforma string in int *) +- float_of_string (* Traforma string in float *) +- int_of_char (* Traforma char in int *) +- char_of_int (* Traforma int in char *) ``` ### Uguaglianza e Disuguaglianza @@ -807,7 +828,7 @@ val sign : int -> int = # let sort (x,y) = if x < y then (x,y) else (y,x);; -val sort : ’a * ’a -> ’a * ’a = +val sort : 'a * 'a -> 'a * 'a = # sort (5,2);; -: int * int = (2,5) @@ -840,8 +861,8 @@ Le tuple sono coppie con più di due elementi ed una tupla può essere un elemen # (true,5*4,"venti");; -: bool * int * string = (true, 20, "venti") -# ((if 3<5 then "true" else "false"), 10.3, ’K’, int_of_string "50");; --: string * float * char * int = ("true", 10.3, ’K’, 50) +# ((if 3<5 then "true" else "false"), 10.3, 'K', int_of_string "50");; +-: string * float * char * int = ("true", 10.3, 'K', 50) # (true, ("pippo",98), 4.0);; -: bool * (string * int) * float = (true, ("pippo", 98), 4) @@ -860,8 +881,8 @@ Le funzioni possono essere un elemento di una tupla dato che sono oggetti di pri Ogni tipo di dato è caratterizzato da un insieme di: -- **Costruttori**: costanti e operazioni che “costruiscono” valori di quel tipo -- **Selettori**: operazioni che “selezionano” componenti da un valore del tipo +- **Costruttori**: costanti e operazioni che "costruiscono" valori di quel tipo +- **Selettori**: operazioni che "selezionano" componenti da un valore del tipo I costruttori per i tipi di dato semplice sono i valori: `6 - int`, `5.0 - float`, `"stringa" - string`, ecc. @@ -890,10 +911,10 @@ Queste due funzioni sono polimorfe: ```ocaml # fst;; --: ’a * ’b -> ’a = +-: 'a * 'b -> 'a = # snd;; --: ’a * ’b -> ’b = +-: 'a * 'b -> 'b = ``` ### Funzioni a più argomenti @@ -927,7 +948,7 @@ Le funzioni che accettano ogni tipo di dato sono chiamate polimorfe (lo abbiamo ```ocaml # let first (x,y) = x;; -val first : ’a * ’b -> ’a = +val first : 'a * 'b -> 'a = ``` A volte OCaml, per 'far quadrare i conti' unifica i tipi: @@ -937,11 +958,11 @@ A volte OCaml, per 'far quadrare i conti' unifica i tipi: val sort1 : 'a * 'a * int -> 'a * 'a = ``` -Qui capisce che `n` è un intero perché viene confrontato con `5` (che è un intero). `x` e `y` vengono unificato (tutti e due del tipo `'a`) per 'far tornare i conti' con l'espressione `if` (la parte then e else devono avere lo steso tipo). +Qui capisce che `n` è un intero perché viene confrontato con `5` (che è un intero). `x` e `y` vengono unificati (tutti e due del tipo `'a`) per "far tornare i conti" con l'espressione `if` (la parte then e else devono avere lo steso tipo). ### Schemi di Tipo ed Istanze -`’a * ’b -> ’a` è uno schema di tipo: indica un insieme infinito di tipi, tutti quelli della forma: `T1 * T2 -> T1`. Ogni tipo che si ottiene sostituendo `’a` con un tipo e `’b` con un tipo è un’istanza di `’a * ’b -> ’a`: +`'a * 'b -> 'a` è uno schema di tipo: indica un insieme infinito di tipi, tutti quelli della forma: `T1 * T2 -> T1`. Ogni tipo che si ottiene sostituendo `'a` con un tipo e `'b` con un tipo è un'istanza di `'a * 'b -> 'a`: - `int * bool -> int` - `int * int -> int` @@ -986,7 +1007,7 @@ Questo problema può essere risolto in vari altri modi: ### Variabili locali -Nell'espressione vista prima `let x = E in F`, `x` è una variabile locale:`x` ha un valore (quello di `E`) solo all'interno dell'espressione `F`, quanto questa viene valutata tutta ,`x` non ha più valore. +Nell'espressione vista prima `let x = E in F`, `x` è una variabile locale:`x` ha un valore (quello di `E`) solo all'interno dell'espressione `F`; quando questa viene valutata tutta ,`x` non ha più valore. ```ocaml # let x = 1+2 in x*8;; @@ -1049,7 +1070,7 @@ let fraction (n,d) = ### Ricorsione Nei linguaggi funzionali puri non esistono costrutti per effettuare cicli come `while` o `for`. Si utilizza quindi la ricorsione. - + Quando si dichiara una funzione ricorsiva è necessario specificarlo tramite la parola chiave `rec`. @@ -1071,7 +1092,7 @@ Unbound value fac val fact: int -> int = ``` -È anche possibile implementare questo algoritmo in modo "iterativo", che è un po una cazzata perché' non è proprio iterativo ma in verità è sempre una merda ricorsiva solo che sfrutta una funzione ausiliaria e si chiama "ricorsione in coda". Una barca di stronzate :robot:. +È anche possibile implementare questo algoritmo in modo "iterativo", che è un po una cazzata perché non è proprio iterativo ma in verità è sempre una merda ricorsiva solo che sfrutta una funzione ausiliaria e si chiama "ricorsione in coda". È utile perchè se si deve usare la ricorsione c'è il rischio di creare "Stack overflow" a causa del numero di chiamate che rimangono attive in contemporanea. Con la ricorsione di coda invece si evita totalmente queso problema. Una barca di stronzate :robot:. ```ocaml let rec aux (n,f) = @@ -1091,7 +1112,7 @@ let fact n = in aux(n,1);; ``` -Quando un problema P1 viene convertito un un altro P2 in modo che la soluzione di P2 sia identica alla soluzione di P1, allora si dice che P1 è stato ridotto a P2 (P2 è una riduzione di P1). +Quando un problema P1 viene convertito in un altro P2 in modo che la soluzione di P2 sia identica alla soluzione di P1, allora si dice che P1 è stato ridotto a P2 (P2 è una riduzione di P1). Quando una funzione ricorsiva è definita in modo tale che tutte le chiamate ricorsive sono riduzioni, allora la funzione viene detta Ricorsiva Di Cosa (Tail Recursive). @@ -1138,17 +1159,17 @@ let max_n (n) = ### Eccezioni -Ocaml mette a disposizione una gestione delle eccezioni per segnalare problemi, come per esempio la funzione fattoriale definita prima che, se viene chiamata con un numero negativo, va in stack overflow ! Modificare la funzione per far si che accetti anche i numeri negativi vorrebbe dire creare una nuova funzione che non è più il fattoriale classico (gli si va a cambiare il dominio). Quindi si possono usare le eccezioni. +Ocaml mette a disposizione una gestione delle eccezioni per segnalare problemi, come per esempio la funzione fattoriale definita prima che, se viene chiamata con un numero negativo, va in stack overflow! Modificare la funzione per far si che accetti anche i numeri negativi vorrebbe dire creare una nuova funzione che non è più il fattoriale classico (gli si va a cambiare il dominio). Quindi si possono usare le eccezioni. -Ne esistono di default come: `Match failure`, `Division by zero`, ecc., ma se ne possono dichiarare anche di nuove cone: +Ne esistono di default come: `Match failure`, `Division by zero`, ecc., ma se ne possono dichiarare anche di nuove come: ```ocaml exception NegativeNumber;; ``` -Il nome delle eccezioni deve iniziare SEMPRE con una lettera maiuscola ! +Il nome delle eccezioni deve iniziare **SEMPRE** con una lettera maiuscola ! -Per lanciare un eccezione su usa la parola chiave `raise`: +Per lanciare un eccezione si usa la parola chiave `raise`: ```ocaml exception NegativeNumber;; @@ -1167,7 +1188,7 @@ Exception: NegativeNumber. Se durante il calcolo di un'espressione viene sollevata un'eccezione il calcolo termina immediatamente e il resto dell'espressione non viene valutata. -Le eccezioni possono essere catturate con il costrutto `try with `: +Le eccezioni possono essere catturate con il costrutto `try with`: ```ocaml # try 4 * fact(-4) with NegativeNumber -> 0;; @@ -1187,7 +1208,7 @@ let positivo n = Un esempio di come utilizzare le eccezioni è dato dal seguente problema: -_Leggere da tastiera una sequenza di numeri interi separati dal carattere ENTER, che termina con un qualsiasi carattere non numerico e calcolarne la somma_ +_Leggere da tastiera una sequenza di numeri interi separati dal carattere ENTER, che termina con un qualsiasi carattere non numerico e calcolarne la somma_ ```ocaml let rec aux tot = @@ -1199,6 +1220,23 @@ let rec aux tot = let main () = aux 0;; ``` +Le eccezioni possono restituire del testo arbitrario quando sollevate, come mostrato nel seguente caso: + +```ocaml +exception NegativeNumber of string;; + +let rec fact n = + if n < 0 + then raise (NegativeNumber "Inserito numero negativo") + else if n = 0 + then 1 + else n * fact (n-1) ;; + +# 4 * fact(-1);; +Exception: NegativeNumber "Inserito numero negativo". +``` + + ### Pattern Un pattern è un'espressione costituita da variabili e costruttori di tipo: @@ -1346,14 +1384,14 @@ xor (false, true);; - Viene valutato il pattern matching tra `(true, q)` e `(false, true)`, che fallisce dato che `true != false` - Viene valutato il pattern matching tra `(_, q)` e `(false, true)`, che ha successo (pattern matching con `_` ha sempre successo) e viene creato il valore provvisorio `q - true` - Viene valutata l'espressione `q` con il nuovo legame -- Viene ritornato il valore di `xor(false, ture)` +- Viene ritornato il valore di `xor(false, true)` - Viene sciolto il legame provvisorio `q - true` ![pattern muti](imgs/pattern_muti.png) ### Pattern Matching esplicito -Abbiamo un ulteriore forma di definire le funzioni con il patter matching: +Abbiamo un ulteriore forma per definire le funzioni con il pattern matching: ``` xor(p, q): @@ -1424,7 +1462,7 @@ val coppia : (int -> int) * (int -> int) = , (*funzioni come parametri di altre funzioni*) # let apply (f,x) = f x;; -val apply : (’a -> ’b) * ’a -> ’b = +val apply : ('a -> 'b) * 'a -> 'b = # let y = (double,7);; val y : (int -> int) * int = , 7 @@ -1434,7 +1472,7 @@ val y : (int -> int) * int = , 7 (*funzioni come valori di ritorno*) # let k a = function x -> a;; -val k : ’a -> ’b -> ’a = +val k : 'a -> 'b -> 'a = # (k 3) 0;; -: int = 3 @@ -1483,7 +1521,7 @@ e per ogni `a1, ..., an`: f (a1, ..., an) = (((fc a1) a2) ... an) ``` -(le parentesi in `fc` possono essere omesse perché in ocaml si associa a destra !) +(le parentesi in `fc` possono essere omesse perché in ocaml si associa a sinistra !) ### Liste @@ -1497,10 +1535,21 @@ Le liste sono sequenze finite di elementi dello stesso tipo: La parola `list` è un costruttore di tipi: se `T` è un tipo, `T list` è il tipo delle liste di elementi di tipo `T`. La lista vuota è denotata da `[]` ed è un oggetto polimorfo di tipo `'a list`. -L'operazione per aggiungere un elemento in testa alla lista è `::`: +L'operazione per aggiungere un elemento in testa alla lista è `::` , infatti: ```ocam -[1;2] = 1::[2] = 1::(2::[]) = 1::2::[] +(* Le seguenti operazioni sono identiche... *) +# ['a';'b';'c'] +# 'a'::['b';'c'] +# 'a'::'b'::['c'] +# 'a'::'b'::'c'::[] +# 'a'::('b'::['c']) +# 'a'::('b'::('c'::[])) + +(* ...ma queste qua non danno lo stesso risultato di quelle sopra, alcune danno errore: *) +# 'a'::'b'::'c' +# ['a';'b']::['c'] +# ['a';'b';'c']::[] (* Ritorna: [['a'; 'b'; 'c']] *) ``` Le operazioni di selezione sono: @@ -1508,9 +1557,10 @@ Le operazioni di selezione sono: ```ocaml List.hd List.tl +List.nth ``` -Con la prima andiamo a selezionare il primo elementi della lista, con la seconda tutti gli elementi tranne il primo: +Con la prima andiamo a selezionare il primo elemento della lista, con la seconda tutti gli elementi tranne il primo: ```ocaml # List.hd [1;2;3;4];; @@ -1536,16 +1586,30 @@ let rec length = function | x::rest -> 1 + length rest;; ``` -Dove `x::rest` indica una coda (tutto tranne il primo elemento) generica, `x` indica la testa e `rest` indica la coda. +e ancora meglio, con pattern matching e funzione ricorsiva di coda: +```ocaml +let rec length_help lst len = match lst with + [] -> len + | x::rest -> length_help rest len+1;; + +let length lst = + length_help lst 0;; +``` +dove `x::rest` indica una lista composta da almeno un elemento, `x` indica la testa e `rest` indica la coda. + +Altrimenti, molto più semplicemente, si può ottenere tramite il relativo modulo: +```ocaml +List.length ;; +``` Di seguito alcuni pattern per le liste: ```ocaml -[] -[x] -[x;y] -x::rest -x::y::rest (*x e' il primo elemento, y il secondo e rest il resto della lista*) +[] (* lista vuota *) +[x] (* lista con un solo elemento, x *) +[x;y] (* lista con esattamente due elementi [x::y] [[1;2]] [[1]] [["ss", "tt", "pojpo"]] *) +x::rest (* lista con almeno un elemento *) +x::(y::rest) (* lista con almeno due elementi (x è il primo, y il secondo, rest è la coda della coda) *) ``` ![lista p1](imgs/lista_p1.png) @@ -1561,23 +1625,61 @@ let rec maxlist = function | x::rest -> max x (maxlist rest) ``` -È possibile concatenare liste con il simbolo `@` ed è possibile concatenare solo liste dello stesso tipo: +È possibile concatenare liste con il simbolo `@` ed è possibile concatenare SOLO liste dello stesso tipo: ```ocaml # [1;2] @ [3;4;5;6] -: int list = [1; 2; 3; 4; 5; 6] ``` -Per l'inserimento in cosa si può utilizzare sempre l'operazione `@`: +Per l'inserimento in coda si può utilizzare sempre l'operazione `@`: ```ocaml -(*aggiungere in coda alla mia lista 3*) +(*aggiunge 3 in coda alla mia lista*) mialista @ [3];; (*con una funzione*) let in_coda x lst = lst @ [x];; ``` +#### Modulo List + +OCaml mette a disposizione vari moduli (librerie standard) tra cui uno per la gestione delle liste :scream:. Di seguito alcune funzioni utili: + +```ocaml +List.length lista (* Ritorna la lunghezza di una lista *) +List.hd lista (* Ritorna il primo elemento di una lista *) +List.tl lista (* Ritorna tutti gli elementi di una lista tranne il primo *) +List.nth lista index (* Ritorna l'elemento n-esimo nella lista *) +List.assoc dizionario (* Ricerca "val" per "key" in un dizionario *) +List.flatten lista_di_liste (* Unisce gli elementi di una lista di liste in una unica lista *) +List.rev lista (* Inverte l'ordine degli elementi di una lista *) +List.iter funzione lista (* Applica una funzione, CHE RITORNA UNIT(), ad ogni elemento di una lista *) +List.map funzione lista (* Applica una funzione, CHE RITORNA QUALSIASI COSA, ad ogni elemento di una lista *) +``` + +Alcuni esempi: + +```ocaml +# List.nth [3;4;5;6;7;8] 3;; +-: int = 6 + +# List.assoc 3 [(1,"pippo"); (2,"pluto"); (3,"paperino")];; +-: string = "paperino" + +# List.flatten [[1; 2; 3]; [3; 4; 5]];; +- : int list = [1; 2; 3; 3; 4; 5] + +# List.rev [8;7;6;5;4;3;2;1];; +- : int list = [1; 2; 3; 4; 5; 6; 7; 8] + +# List.iter print_int [8;7;6;5;4;3;2;1];; +87654321- : unit = () + +# let add3 e = e+3 in List.map add3 [3;4;5;6;7;8];; +- : int list = [6; 7; 8; 9; 10; 11] +``` + #### Dizionario Un dizionario è un tipo di dato astratto che in pratica è una lista i cui elementi sono formati da una coppia `(chiave, valore)`. In pratica si realizza con una lista associativa: @@ -1604,27 +1706,6 @@ Per esempio, quando si effettua la concatenazione di due liste succede questo: ![lista mem2](imgs/lista_mem2.png) -#### Modulo List - -OCaml mette a disposizione vari moduli (librerie standard) tra cui uno per la gestione delle liste :scream:. Di seguito alcune funzioni utili: - -```ocaml -List.length -List.hd -List.tl -List.nth -List.assoc -``` - -Alcuni esempi: - -```ocaml -# List.nth [3;4;5;6;7;8] 3;; --: int = 6 - -# List.assoc 3 [(1,"pippo"); (2,"pluto"); (3,"paperino")];; --: string = "paperino" -``` ### Random @@ -1657,13 +1738,13 @@ Random.self_init;; C'è il classico approccio a forza bruta: generare ad una ad una tutte le possibili sequenze e controllare se soddisfino le condizioni (cercare di beccare a caso la soluzione). Oppure utilizzare l'approccio con Backtracking: costruire la soluzione aggiungendo un elemento alla volta ed utilizzare un criterio per capire se la sequenza parziale (la strada che sto percorrendo) ha possibilità di successo. Soluzione: `(x1, ..., xn)` -Ad ogni stadio `i` controllo se `(x1, ..., xi)` ha possibilità di successo. Se ha possibilità, si sceglia un nuovo `xi+1` tra le possibili alternative. Se con tale scelta si arriva alla soluzione allora quella è la soluzione (ma va ?!). Altrimenti scelgo un diverso `xi+1`. Se dopo aver provato tutte le varie possibilità non si arriva ad una soluzione ritorno un fallimento. Se si verifica che `(x1, ..., xi)` non ha possibilità di successo non adro' a generare le sequenze che "passano" per lui `(x1, ..., xi, ...)`. +Ad ogni stadio `i` controllo se `(x1, ..., xi)` ha possibilità di successo. Se ha possibilità, si sceglie un nuovo `xi+1` tra le possibili alternative. Se con tale scelta si arriva alla soluzione allora quella è la soluzione (ma va ?!). Altrimenti scelgo un diverso `xi+1`. Se dopo aver provato tutte le varie possibilità non si arriva ad una soluzione ritorno un fallimento. Se si verifica che `(x1, ..., xi)` non ha possibilità di successo non adrò a generare le sequenze che "passano" per lui `(x1, ..., xi, ...)`. Così facendo riesco a diminuire lo spazio di ricerca applicando un criterio di eliminazione. Prendiamo il seguente problema: -_Dato un insieme S di numeri positivo ed un numero intero N, determinare un sottoinsieme Y di S tale che la somma degli elementi di Y dia N_. +_Dato un insieme S di numeri positivi ed un numero intero N, determinare un sottoinsieme Y di S tale che la somma degli elementi di Y dia N_. Esempio: `S = {1, 4, 5, 8}` e `N = 9`. Dobbiamo trovare un sottoinsieme di S dove sommando tutti gli elementi il risultato sia 9. Questo problema può essere rappresentato con un albero: @@ -1675,7 +1756,7 @@ exception NotFound;; (* subset_search : int list -> int -> int list *) let rec stampalista = function [] -> print_newline() - | x::rest -> print_int(x); print_string(“; “); stampalista rest;; + | x::rest -> print_int(x); print_string("; "); stampalista rest;; let subset_search set n = (* aux : int list -> int -> int list -> int list *) @@ -1707,13 +1788,13 @@ Si possono definire nuovi tipi di dato tramite la parola chiave `type` specifica #### Tipi enumerati -Sono tipi costituiti da un insieme finito di valori, tipo `bool` che contiene solo `true, false`. I valori di questi tipo sono costanti. +Sono tipi costituiti da un insieme finito di valori, tipo `bool` che contiene solo `true, false`. I valori di questo tipo sono costanti. ```ocaml type direzione = Su | Giu | Destra | Sinistra;; ``` -In questa dichiarazione di tipo `direzione` è il nome del nuovo tipo e `Su, Giu Destra, Sinistra` sono i valori del nuovo tipo. Questi devono essere sempre separati dal carattere `|` ed iniziare con la lettera maiuscola. +In questa dichiarazione di tipo, `direzione` è il nome del nuovo tipo e `Su, Giu Destra, Sinistra` sono i valori del nuovo tipo. Questi devono essere sempre separati dal carattere `|` ed iniziare con la lettera maiuscola. Le nuove costanti sono quindi costruttori di tipo e possono anche comparire all'interno di un pattern e quindi valgono le operazioni di pattern matching: @@ -1725,7 +1806,7 @@ let prova = function | Sinistra -> 10;; ``` - + ### Alberi @@ -1747,7 +1828,7 @@ Alcune definizioni utili: - **fratelli**: nodi che hanno lo stesso genitore - **sotto albero**: un insieme costituito da un nodo `n` e tutti i suoi discendenti - **foglia**: nodo senza figli -- **nodo interno:** nodo con 1 o piU figli +- **nodo interno:** nodo con 1 o più figli - **profondità del nodo:** lunghezza del cammino dalla radice al nodo stesso - **altezza del nodo**: lunghezza del cammino più lungo che va dal nodo ad una foglia - **altezza dell'albero**: altezza della sua radice ovvero la profondità massima di un nodo nell'albero @@ -1757,15 +1838,15 @@ Esiste anche l'albero vuoto, in pratica è l'insieme vuoto. Questo semplifica al ![vuoto](imgs/albero_vuoto.png) -La precedente è una rappresentazione di un albero binario con anche l'albero vuoto. In pratica le foglie hanno come figli alberi vuoti. Così facendo, ogni albero che non sia quello vuoto ha esattamente due sotto alberi, se è una foglia i sotto alberi sono vuoti, se è un nodo con un solo figlio uno dei due sotto alberi è vuoto. +La precedente è una rappresentazione di un albero binario con anche l'albero vuoto. In pratica le foglie hanno come figli alberi vuoti. Così facendo, ogni albero che non sia quello vuoto ha esattamente due sotto alberi; se è una foglia i sotto alberi sono vuoti, se è un nodo con un solo figlio uno dei due sotto alberi è vuoto. Un albero binario è _completo_ se ogni nodo interno ha esattamente 2 figli. ![albero binario completo](imgs/albero_completo.png) -Un albero di dice _bilanciato_ se per ogni nodo `n`, le altezze di sotto alberi destro e sinistro di `n` differiscono al massimo di 1. +Un albero di dice _bilanciato_ se per ogni nodo `n`, le altezze dei sotto alberi destro e sinistro di `n` differiscono al massimo di 1. -In Ocaml un albero può essere rappresentato con: +In Ocaml un _albero binario_ può essere rappresentato con: ```ocaml type 'a tree = @@ -1787,6 +1868,39 @@ genera il seguente albero: ![albero cammello](imgs/albero_ocaml.png) +Per rappresentare degli _alberi N-ari_ si usa invece: + +```ocaml +type 'a ntree = Tr of 'a * 'a ntree list;; +``` + +dove quindi si ha che: + - una foglia etichettata da `n` è rappresentata da `Tr(n,[])` (non ha sottoalberi) + ```ocaml + let leaf x = Tr(x,[]);; + ``` + - l'albero con radice `n` e sottoalberi `t1, t2, ..., tn` è rappresentata da `Tr(n,[t1;...;tn])` + +Per esempio: + +```ocaml +let t = Tr(1,[Tr(2,[Tr(3,[leaf 4; + leaf 5]); + Tr(6,[leaf 7]); + leaf 8]); + leaf 9; + Tr(10,[Tr(11,[leaf 12; + leaf 13; + leaf 14]); + leaf 15; + Tr(16,[leaf 17; + Tr(18,[leaf 19; + leaf 20])])])]);; +``` + +![albero n-ario](imgs/albero_nario_ocaml.png) + + ### Sequenze di comandi In Ocaml non esistono comandi veri e propri, ma possiamo considerare tali le funzioni che ritornano `unit`, cioè quelle funzioni che sono importanti non per il loro valore di ritorno ma per i loro effetti collaterali (tipo la funzione di stampa). @@ -1797,7 +1911,7 @@ In Ocaml non esistono comandi veri e propri, ma possiamo considerare tali le fun (E1;E2;E3;...;En);; ``` -Il tipo e il valore di questa espressione sono dati dal tipo e il valore di `En` (si, tutta sta roba ha il valore e il tipo dell'ultimo elemento :upside_down_face:). Le espressioni `Ei` vengono tutte valutate, da sinistra a destra, ma i valori sono ignorati, tranne quello dell'ultimo elemento. +Il tipo e il valore di questa espressione sono dati dal tipo e il valore di `En` (si, tutta sta roba ha il valore e il tipo dell'ultimo elemento :upside_down_face:). Le espressioni `Ei` vengono tutte valutate (quindi i loro effetti collaterali, quali la stampa ad esempio, influenzano il programma), da sinistra a destra, ma i valori sono ignorati, tranne quello dell'ultimo elemento. ```ocaml # (print_int 3;print_string "*";print_int 8; print_string " = ";print_int(3*8); print_newline(); 3*8);; @@ -1819,7 +1933,7 @@ ciao ### Dati Modificabili -In OCaml solo alcuni tipi di dato sono modificabili (puo' essere effettivamente cambiato il suo valore senza fare cose strane), per esempio le variabili non sono modificabili, modificare una variabile vuol dire creare un nuovo binding nell'ambiente che va ad oscurare quello precedente. +In OCaml solo alcuni tipi di dato sono modificabili (può essere effettivamente cambiato il suo valore senza fare cose strane), per esempio le variabili non sono modificabili, modificare una variabile vuol dire creare un nuovo binding nell'ambiente che va ad oscurare quello precedente. I tipi di dato modificabili sono: @@ -1830,13 +1944,13 @@ I tipi di dato modificabili sono: #### Array -In OCaml un array di `n` elementi si definisce come: +In OCaml un array di `n` elementi si definisce come una lista ma con il simbolo `|` a racchiudere le espressioni: ```ocaml [|expr1; expr2; ...; exprn|];; ``` -Le posizioni vanno da `0` fino a `n-1` e l'ordine di valutazione delle espressioni non è specificato (???????). +Le posizioni vanno da `0` fino a `n-1` e l'ordine di valutazione delle espressioni è sempre da sinistra a destra. Per accedere ad un elemento si usa: @@ -1906,17 +2020,17 @@ type esame = { lode:bool };; -let e1 = {studente=”pippo”; voto=28; lode=false};; +let e1 = {studente="pippo"; voto=28; lode=false};; ``` I campi sono accessibili con l'operatore `.`: `e1.studente;;` e l'ordine con cui si specificano i campi non è importante: ```ocaml -let e1 = {studente=”pippo”; voto=28; lode=false};; -let e1 = {voto=28; studente=”pippo”; lode=false};; +let e1 = {studente="pippo"; voto=28; lode=false};; +let e1 = {voto=28; studente="pippo"; lode=false};; ``` -Se un campo di un record viene definito `mutable` allora questo puo' essere modificato tramite assegnazione: +Se un campo di un record viene definito `mutable` allora questo può essere modificato tramite assegnazione: ```ocaml type mut_point = { @@ -1930,7 +2044,7 @@ punto.x <- 30.0;; #### Riferimento -In OCaml c'è un sistema simile ai puntatori chiamato `ref`, che puo' essere visto come un record polimorfo con un unico componente mutabile. +In OCaml c'è un sistema simile ai puntatori chiamato `ref`, che può essere visto come un record polimorfo con un unico componente mutabile. ```ocaml # let x = ref 3 ;; @@ -1939,10 +2053,10 @@ val x : int ref = {contents=3} # x ;; - : int ref = {contents=3} -# !x ;; +# !x ;; (* Leggiamo il valore della variabile *) - : int = 3 -# x := 4 ;; +# x := 4 ;; (* Modifichiamo il valore della variabile *) - : unit = () # !x ;; @@ -1966,26 +2080,26 @@ La prima volta che viene creato `x` avrà il tipo `'_a list ref`, poi la prima v ### Grafi -Un grafo orientato puo' essere rappresentato come una lista di archi: +Un grafo orientato può essere rappresentato come una lista di archi: ```ocaml -type ’a graph = Gr of (’a * ’a) list;; +type 'a graph = Gr of ('a * 'a) list;; let grafo1 = Gr [(1,2);(1,3);(1,4);(2,6);(3,5);(4,6);(6,5);(6,7);(5,4)];; ``` ![grafo cammello](imgs/grafo_ocaml.png) -Lo stesso grafo orientato si puo' rappresentare con una lista di successori: +Lo stesso grafo orientato si può rappresentare con una lista di successori: ```ocaml -type ’a graph = Gr of (’a * ’a list) list ;; +type 'a graph = Gr of ('a * 'a list) list ;; let grafo1 = Gr [(1,[2;3;4]); (2,[6]); (3,[5]); (4,[6]); (5,[4]); (6,[5;7])] ;; ``` -Un grafo puo' essere rappresentato anche come una funzione (utile in molti casi dove viene utilizzata solo la funzione `successori`): +Un grafo può essere rappresentato anche come una funzione (utile in molti casi dove viene utilizzata solo la funzione `successori`): ```ocaml -type ’a graph = Graph of (’a -> ’a list);; +type 'a graph = Graph of ('a -> 'a list);; let f = function 1 -> [2;3;4] | 2 -> [1;3;5] @@ -2000,7 +2114,294 @@ val g : int graph = Graph ![grafo funzione](imgs/grafo_funzione.png) - + + +#### Algoritmi di visita e ricerca +Il prof ne riporta vari quindi guardateli meglio dalle sue slide. I principali sono: + - [DFS](#dfs) + - [BFS](#bfs) + - [Best-First](#best-first) + - [Hill Climbing](#hill-climbing) + - [Branch and Bound](#branch-and-bound) + - [A*](#a-star) + +Dove `DFS` e `BFS` fanno parte degli algoritmi di _ricerca non informata_ mentre `Best-First, Hill Climbing, Branch and Bound, A*` fanno parte della _ricerca informata_ o _euristica_. + +Ricordiamo che la _ricerca informata_ è applicabile quando una funzione di valutazione consente di paragonare due soluzioni parziali allo scopo di selezionare quella che più facilmente potrebbe portare alla soluzione. In generale la funzione di valutazione non è perfetta; se lo fosse non sarebbe necessaria nessuna ricerca perché ad ogni passo si sceglierebbe l'alternativa giusta. La funzione di valutazione dà una indicazione di massima su quale è la soluzione più promettente. L'euristica prevede però di fare un compromesso tra: + - ottimalità: la soluzione trovata è la migliore + - completezza: l'algoritmo trova soluzione per ogni istanza del problema + - tempo: la soluzione viene trovata in un tempo ragionevole + +Negli algoritmi euristici solitamente si sacrificano il primo od il secondo aspetto, od entrambi. Quindi potremo avere algoritmi che risolvono il problema ma solo per una certa istanza e trovano una soluzione sub-ottima. + +##### DFS +Per usare questo algoritmo, e anche la [BFS](#bfs), dobbiamo prima definire una serie di funzioni ausiliare e la modalità di rappresentazione del grafo usato: + +```ocaml +type 'a graph = Gr of ('a * 'a list) list ;; +let grafo1 = Gr [(1,[2;3;4]); (2,[6]); (3,[5]); (4,[6]); (5,[4]); (6,[5;7])] ;; + +(* succ : 'a graph -> 'a -> 'a list *) +(* Ritorna la lista dei successori di un nodo *) +let succ (Gr succlist) node = + try List.assoc node succlist with Not_found -> [];; +``` + +Ora possiamo definire l'algoritmo di visita in profondità (DFS, Depth First Search) vero e proprio per il quale se il nodo di partenza start non è stato già visitato si analizza start e, per ogni successore x di start, si visita, con lo stesso metodo, il grafo a partire da x, ricordando che start è già stato visitato. I nodi in attesa di essere visitati vengono gestiti come una _pila_. + +```ocaml +let depth_first_collect graph start = + let rec search visited = function + [] -> visited (* List.rev visited *) + | n::rest -> if List.mem n visited + then search visited rest + (* i nuovi nodi sono inseriti in testa *) + else search (n::visited) ((succ graph n) @ rest) + in search [] [start];; + + +# depth_first_collect grafo1 1;; (* Lista visualizzata in maniera inversa a causa di inserzioni in testa *) +- : int list = [3; 7; 4; 5; 6; 2; 1] +``` + +##### BFS +Anche per questo algoritmo dobbiamo usare le funzioni ausiliare già mostrate per la [DFS](#dfs) quindi riguardale. + +Ora possiamo definire l'algoritmo di visita in ampiezza (BFS, Breadth First Search) per il quale se il nodo di partenza start non è già stato visitato allora si analizza start, si visitano tutti i suoi successori (ricordando che start è già stato considerato), poi tutti i successori dei successori di start, e così via. I nodi in attesa di essere visitati vengono gestiti come una _coda_. + +```ocaml +let breadth_first_collect graph start = + let rec search visited = function + [] -> visited + | n::rest -> if List.mem n visited + then search visited rest + (* i nuovi nodi sono inseriti in coda *) + else search (n::visited) (rest @ (succ graph n)) + in search [] [start];; + + +# breadth_first_collect grafo1 1;; (* Lista visualizzata in maniera inversa a causa di inserzioni in testa *) +- : int list = [7; 5; 6; 4; 3; 2; 1] +``` + +##### Best-First +In questa tipologia di ricerca ad ogni passo si prende in esame la soluzione più promettente. È come fare una **ricerca in ampiezza** in cui però sfrutto la funzione euristica per espandere il nodo più promettente. In pratica mi calcolo prima la funzione euristica per tutti i nodi e poi li riordino in base a quelli più promettenti. Espando quindi il nodo più promettente ed aggiungo i suoi successori alla lista. Ci calcolo l'euristica e riordino l'**INTERA** lista così poi da espandere il nodo migliore tra **TUTTA** la lista. + +Per usare questo algoritmo, e anche [Hill Climbing](#hill-climbing), [Branch and Bound](#branch-and-bound) e [A*](#a-star), dobbiamo prima definire una serie di funzioni ausiliare e la modalità di rappresentazione del grafo usato: + +```ocaml +type 'a graph = Graph of ('a -> 'a list);; + +let f = function +1 -> [2;5] +| 2 -> [1;3;5] +| 3 -> [2;4;6] +| 4 -> [3] +| 5 -> [1;2;6] +| 6 -> [3;5;7] +|7 -> [6] +| _ -> [];; + +let g = Graph f;; + +(* Esempio dato un grafo di coordinate *) +let coordinate = [ (1, (0,3)); (2, (4,6)); (3, (7,6)); (4, (11,6)); (5, (3,0)); (6, (6,0)); (7, (11,3))];; + +(* In questo caso la funzione di valutazione calcola quale soluzione parziale porta più vicino al nodo che costituisce la meta *) +let distanza nodo1 nodo2 = + let x1 = float (fst(List.assoc nodo1 coordinate)) + in let y1 = float (snd(List.assoc nodo1 coordinate)) + in let x2 = float (fst(List.assoc nodo2 coordinate)) + in let y2 = float (snd(List.assoc nodo2 coordinate)) + in sqrt ( (x1 -. x2)**2. +. (y1 -. y2)**2.);; + +let piuvicino (cammino1, cammino2, meta) = + (distanza (List.hd cammino1) meta) < (distanza (List.hd cammino2) meta);; + +let confrontacammino cammino1 cammino2 meta = + if List.hd cammino1 = List.hd cammino2 + then 0 + else if piuvicino (cammino1, cammino2, meta) + then -1 + else 1;; +``` + +Ora possiamo definire l'algoritmo di ricerca vero e proprio: + +```ocaml +exception NotFound;; + +let rec stampalista = function [] -> print_newline() + | x::rest -> print_int(x); print_string("; "); stampalista rest;; + +let searchbf inizio fine (Graph succ) = + let estendi cammino = stampalista cammino; + List.map (function x -> x::cammino) (List.filter (function x -> not (List.mem x cammino)) (succ (List.hd cammino))) + in let confronta c1 c2 = + confrontacammino c1 c2 fine + in let rec search_aux fine = function + [] -> raise NotFound + | cammino::rest -> if fine = (List.hd cammino) + then List.rev cammino + else search_aux fine (List.sort confronta (rest @ (estendi cammino))) + in search_aux fine [[inizio]];; + +# searchbf 1 7 g;; +1; +2; 1; +3; 2; 1; +4; 3; 2; 1; +6; 3; 2; 1; +- : int list = [1; 2; 3; 6; 7] +``` + +##### Hill Climbing +In questa tipologia di ricerca ad ogni passo viene scelta la soluzione parziale generata **dal passo precedente** più promettente. È come fare una **ricerca in profondità** in cui però sfrutto la funzione euristica per espandere il nodo più promettente tra i successori. In pratica mi calcolo prima la funzione euristica per tutti i nodi e poi li riordino in base a quelli più promettenti. Espando quindi il nodo più promettente e mi creo una lista con i **SOLI** suoi successori. Ci calcolo l'euristica e riordino **SOLO QUELLA LISTA** di successori così poi da espandere il relativo nodo migliore. + +Anche per questo algoritmo dobbiamo usare le funzioni ausiliare già mostrate per la [Best-First](#best-first) quindi riguardale. + +```ocaml +exception NotFound;; + +let rec stampalista = function [] -> print_newline() + | x::rest -> print_int(x); print_string("; "); stampalista rest;; + +let searchhc inizio fine (Graph succ) = + let estendi cammino = stampalista cammino; + List.map (function x -> x::cammino) (List.filter (function x -> not (List.mem x cammino)) (succ (List.hd cammino))) + in let confronta c1 c2 = + confrontacammino c1 c2 fine + in let rec search_aux fine = function + [] -> raise NotFound + | cammino::rest -> if fine = (List.hd cammino) + then List.rev cammino + else search_aux fine ((List.sort confronta (estendi cammino)) @ rest) + in search_aux fine [[inizio]];; + +# searchhc 1 7 g;; +1; +2; 1; +3; 2; 1; +4; 3; 2; 1; +6; 3; 2; 1; +- : int list = [1; 2; 3; 6; 7] +``` + +Il vantaggio della ricerca **Hill Climbing** rispetto alla ricerca **Best First** è che ordinare solo l'estensione del cammino attuale è computazionalmente meno costoso che ordinare l'intera lista dei cammini. Lo svantaggio è il rischio di tendere verso un minimo locale della funzione di valutazione. + +A livello di codice invece la differenza tra i due è letteralmente solo il come sono ordinati gli elementi nella ricerca del nodo finale: + - Best First: `List.sort confronta (rest @ (estendi cammino))` + - Hill Climbing: `(List.sort confronta (estendi cammino)) @ rest` + +Notiamo le parentesi come sono messe... in Best First si fa il sort per confronto tra tutti gli elementi; quindi sono riordinate tutte +le soluzioni sia quelle precedenti che le nuove. In Hill Climbing invece si fa il sort per confronto solo tra le nuove soluzioni e si mettono +poi prima delle precedenti. + +##### Branch and Bound +In questa ricerca si trova la soluzione di **costo minimo** ma deve essere definita una funzione costo per le soluzioni parziali. Poi un algoritmo simile al Best-First consente di calcolare la soluzione di costo minimo. Ad ogni passo l'insieme delle soluzioni parziali viene ordinato rispetto al costo totale. È in pratica una **Best First Search** in cui si usa una nuova euristica data dal costo dell'attuale path trovato (sempre calcolato in termini di sommatoria di distanza tra i vari nodi del path). È quindi come fare una ricerca in ampiezza in cui però sfrutto la funzione euristica per espandere il **PATH più promettente** ovvero quello che **ATTUALMENTE** ha distanza **MINIMA**. + +In pratica mi calcolo prima la funzione euristica per tutti i PATH e poi li riordino in base a quelli più promettenti. Espando quindi il PATH più promettente ed aggiungo i suoi successori alla lista. Ci calcolo l'euristica e riordino l'**INTERA lista di LISTE** così poi da espandere il PATH (lista) migliore **tra TUTTE le liste**. + +Anche per questo algoritmo dobbiamo usare le funzioni ausiliare già mostrate per la [Best-First](#best-first) quindi riguardale. + +```ocaml +let rec costocammino lst = match lst with (* SI DIFFERENZIA DAGLI ALTRI METODI PERCHE USA QUESTA FUNZIONE E NON 'confrontacammino' *) + [] -> 0. + | [x] -> 0.; + | x::y::rest -> (distanza x y) +. (costocammino (y::rest));; + + +exception NotFound;; + +let rec stampalista = function [] -> print_newline() + | x::rest -> print_int(x); print_string("; "); stampalista rest;; + +let searchbb inizio p (Graph succ)= + let estendi cammino = stampalista cammino; + List.map (function x -> x::cammino) (List.filter (function x -> not (List.mem x cammino)) (succ (List.hd cammino))) + in let confronta c1 c2 = + let costo1 = costocammino c1 + in let costo2 = costocammino c2 + in if costo1 = costo2 + then 0 + else if costo1 < costo2 + then -1 + else 1 + in let rec search_aux = function + [] -> raise NotFound + | cammino::rest -> if p (List.hd cammino) + then List.rev cammino + else search_aux (List.sort confronta (rest @ (estendi cammino))) + in search_aux [[inizio]];; + +# searchbb 1 ((=) 7) g;; +1; +5; 1; +2; 1; +6; 5; 1; +3; 2; 1; +2; 5; 1; +5; 2; 1; +4; 3; 2; 1; +- : int list = [1; 5; 6; 7] +``` + +##### A* +In questa ricerca si sfrutta il costo del cammino più la previsione del costo verso la meta. Realizzato nel 1968 per il robot shakey, rappresenta una versione con euristica dell'algoritmo di Dijkstra per la ricerca dei cammini minimi. La sua funzione euristica è: +`f(x) = g(x) + h(x)` con: + - `f(x)` = costo totale stimado dal nodo inziale fino al goal + - `g(x)` = costo speso fino al passo x per allontanarsi dal nodo iniziale + - `h(x)` = costo stimato dal nodo x fino al goal; se l'euristica `h(x)` non è ottimistica, quindi non stima un costo INFERIORE a quello reale, l'algoritmo non funziona + +Anche per questo algoritmo dobbiamo usare le funzioni ausiliare già mostrate per la [Best-First](#best-first) e [Branch and Bound](#branch-and-bound) quindi riguardale. In particolare si avrà che: + - `h(x)` = funzione `distanza` + - `g(x)` = funzione `costocammino` + - `f(x)` = funzione `costototale` che definiamo di seguito + +```ocaml +let rec costocammino = function + [] -> 0. + | [x] -> 0.; + | x::y::rest -> (distanza x y) +. (costocammino (y::rest));; + +exception NotFound;; + +let rec stampalista = function [] -> print_newline() + | x::rest -> print_int(x); print_string("; "); stampalista rest;; + +let costototale cammino meta = + costocammino cammino+. distanza (List.hd cammino) meta;; + +let confrontacammino cammino1 cammino2 meta = + let c1 = costototale cammino1 meta in + let c2 = costototale cammino2 meta in + if c1 = c2 + then 0 + else if c1 < c2 + then -1 + else 1;; + +let searchAstar inizio fine (Graph succ) = + let estendi cammino = stampalista cammino; + List.map (function x -> x::cammino) (List.filter (function x -> not (List.mem x cammino)) (succ (List.hd cammino))) + in let confronta c1 c2 = + confrontacammino c1 c2 fine + in let rec search_aux = function + [] -> raise NotFound + | cammino::rest -> if fine = (List.hd cammino) + then List.rev cammino + else search_aux (List.sort confronta (rest @ (estendi cammino))) + in search_aux [[inizio]];; + + +# searchAstar 1 7 g;; +1; +2; 1; +5; 1; +3; 2; 1; +6; 5; 1; +- : int list = [1; 5; 6; 7] +``` ### Cicli @@ -2022,13 +2423,13 @@ for v=e1 to e2 do e3 done -(*ciclo calante*) +(*ciclo decrescente*) for v=e1 downto e2 do e3 done ``` -Le espressioni `e1` ed `e2` sono i limiti del ciclo e devono ritornare un valore intero e vengono incrementati/decrementati a seconda del tipo di ciclo +Le espressioni `e1` ed `e2` sono i limiti del ciclo e devono ritornare un valore intero e vengono incrementati/decrementati a seconda del tipo di ciclo. ### Operazioni su file @@ -2077,7 +2478,7 @@ let length l = length_aux 0 l ```ocaml (*file list.mli*) -val length : ’a list -> int +val length : 'a list -> int ``` Se importo questo modulo in un altro file potrò utilizzare la funzione `length` ma non `length_aux` dato che non è definita nel file `.mli`: diff --git a/magistrale/Anno 1/Artificial Intelligent Systems/Marcugini/imgs/albero_nario_ocaml.png b/magistrale/Anno 1/Artificial Intelligent Systems/Marcugini/imgs/albero_nario_ocaml.png new file mode 100644 index 0000000000000000000000000000000000000000..038b9b3e739a89a3517101faaaf2a428f1816588 GIT binary patch literal 13147 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 iZ_k+j;us6wIwc^_S)w!eyrPHSS5M^C