Sunteți pe pagina 1din 7

Inteligen artificial

Laboratorul 4

1. Obiective
Obictivele acestui laborator sunt urmtoarele:

prezentarea nregistrrilor n F#;


prezentarea operatorilor de compunere a funciilor.

2. nregistrri
n laboratorul 1, am prezentat tuplele, grupuri de valori ordonate, de tipuri posibil diferite,
dar neetichetate. nregistrrile (engl. records) au un rol similar, dar valorile lor sunt etichetate,
ceea ce crete claritatea n cazul datelor mai complexe.
nregistrrile se definesc ca n exemplele urmtoare:
type ComplexNumber = { Real: float; Imaginary: float }
type GeoCoord = { Latitude: float; Longitude: float }
type Point = { X : float; Y: float; Z: float }
type Customer = { FirstName: string; LastName: string; AccountNumber: uint32 }

Pentru a defini o valoare a unei nregistrri, se folosete sintaxa de mai jos:


let myComplexNumber = { Real = 1.1; Imaginary = 2.2 }
let myCustomer = { FirstName = "John"; LastName = "Doe"; AccountNumber = 123456789u }

Dei exist mai multe modaliti de a accesa valoarea unei etichete (engl. label), cea mai
clar este sintaxa folosit i n limbaje precum Java sau C#:
let fn = myCustomer.FirstName
let ln = myCustomer.LastName
let an = myCustomer.AccountNumber

Pot exista situaii cnd dou sau mai multe nregistrri au aceleai nume de etichete. n acest
caz, la definirea unei valori se specific i numele nregistrrii:
type Employee = { FirstName : string; LastName: string; AccountNumber : uint32 } // aceleasi etichete ca la Customer
let myEmployee = { Employee.FirstName = "Mary"; LastName = "Poppins"; AccountNumber = 987654321u }

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

nregistrri. Compunerea funciilor

O nou valoare poate fi creat prin modificarea unei valori existente:

nregistrrile sunt n mod implicit imutabile. Totui, pot conine etichete mutabile, marcate
explicit astfel. Actualizarea valorilor etichetelor mutabile se face tot cu ajutorul operatorului <-.
Dac o nregistrare are mai multe etichete, sau cnd se dorete creterea claritii, acestea pot fi
dispuse pe mai multe linii:
type Customer = {
FirstName : string;
LastName: string;
mutable AccountNumber : uint32
}
let myCustomer = { FirstName = "John"; LastName = "Doe"; AccountNumber = 123456789u }
myCustomer.AccountNumber <- 111111111u

3. Compunerea funciilor
O alt caracteristic fundamental a programrii funcionale este compunerea mai multor
funcii pentru a realiza funcii mai complexe i cu o putere de prelucrare mai mare. Compunerea
funciilor conduce la o structur de apeluri mai natural i deci mai uor de neles.
S considerm un prim exemplu, o funcie care calculeaz dimensiunea unui director:
open System
open System.IO
let sizeOfFolder folder =
// ia toate numele de fisiere din directorul folder si subdirectoarele sale
let filesInFolder = Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories)
// transforma numele fisierelor in obiectele FileInfo corespunzatoare
let fileInfos = Array.map (fun file -> new FileInfo(file)) filesInFolder
// transforma vectorul de obiecte FileInfo intr-un vector de dimensiuni
let fileSizes = Array.map (fun (info : FileInfo) -> info.Length) fileInfos
// sumeaza dimensiunile si returneaza suma
Array.sum fileSizes
let n = sizeOfFolder """d:\"""
printfn "%A bytes" n

n aceast funcie, fiecare rezultat intermediar este pasat funciei urmtoare, astfel nct
avem nevoie de multe instruciuni let. O alternativ este compunerea acestor funcii folosind
paranteze:
let sizeOfFolder' folder =
Array.sum(
Array.map (fun (info : FileInfo) -> info.Length)
(Array.map (fun file -> new FileInfo(file))
(Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories))))

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

let myComplexNumber2 = { myComplexNumber with Real = 2.1 }

Acum nu mai avem valori intermediare, dar codul este foarte greu de citit iar ordinea
operaiilor aa cum apar n funcie este invers fa de ordinea natural a aplicrii lor.

Limbajul F# are o modalitate elegant de a trimite rezultatele intermediare ale prelucrilor


ntre diferite funcii, cu ajutorul operatorului de compunere pipe-forward, notat |>. El primete o
funcie la dreapta i o aplic valorii din stnga. Folosind acest operator, funcia de mai sus devine:
let sizeOfFolder folder =
Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories)
|> Array.map (fun file -> new FileInfo(file))
|> Array.map (fun info -> info.Length)
|> Array.sum

Acum funcia este mai succint i se vd clar paii de prelucrare. De asemenea, compunerea
ajut i procesul de inferen a tipurilor: argumentul info nu mai are nevoie de adnotare. Compunerea n acest mod se bazeaz pe modul de transmitere a parametrilor funciilor prin currying, proces
descris n laboratorul 1. Operatorul pipe-forward primete o funcie cu un singur parametru i o
aplic la valoarea trimis de funcia anterioar.
Pe lng beneficiile importante, operatorul pipe-forward are totui dezavantajul c procesul
de depanare devine mai dificil, deoarece programatorul nu mai poate inspecta rezultatele intermediare ale prelucrrilor.
3.2. Operatorul de compunere nainte
Operatorul pipe-forward nu este de fapt un operator de compunere, ci un operator de
trimitere a rezultatelor ntre mai multe funcii. Operatorul de compunere nainte (engl. forward
composition), notat >>, este utilizat din punct de vedere practic ntr-un mod asemntor, dar
compune funciile n mod generic, fr a lua n calcul parametrii acestora. El primete dou funcii,
o aplic mai nti pe cea din stnga i apoi pe cea din dreapta.
Mai jos este prezentat aceeai funcie, dar rescris cu ajutorul acestui operator:
let sizeOfFolder =
let getFilesInFolder folder = Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories)
getFilesInFolder
>> Array.map (fun file -> new FileInfo(file))
>> Array.map (fun info -> info.Length)
>> Array.sum

Spre deosebire de varianta anterioar, funcia sizeOfFolder nu mai are parametrul folder.
Acum, ea ntoarce de fapt o funcie: funcia getFilesInFolder, care primete un parametru, folder, i
care este compus cu celelalte funcii.
S considerm un alt exemplu, pentru a vedea mai clar diferena dintre cei doi operatori: o
funcie care calculeaz numrul de cifre al cubului unui numr ntreg.

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

3.1. Operatorul pipe-forward

Funcia clasic este:

Funcia rescris cu ajutorul operatorului pipe-forward este:


let cube x = x * x * x
let toString x = string x
let length (s : string) = s.Length
let cubeLength x = x |> cube |> toString |> length

Funcia rescris cu ajutorul operatorului de compunere nainte este:


let cubeLength = cube >> toString >> length

n acest caz, cubeLength este o funcie compus, care primete argumentul primit de cube i
ntoarce rezultatul calculat de length.
Operatorul de compunere nainte poate fi utilizat mpreun cu funciile de transformare ale
listelor. n exemplul urmtor, aplicm n ordine trei funcii simple asupra elementelor unei liste de
numere reale:
let functions = [
fun x -> abs x;
fun x -> sqrt x;
fun x -> x + 1.0 ]
let list = [ 1.0; -2.5; 4.0 ]
let list2 = List.map (List.reduce (fun a f -> a >> f) functions) list // [2.0; 2.58113883; 3.0]

3.3. Operatorul pipe-backward


Acest operator primete o funcie la stnga i o aplic valorii din dreapta. La prima vedere,
acest lucru pare inutil, deoarece aplicarea parametrului se poate face i fr acest operator:
// cele 2 operatii sunt echivalente:
List.iter (printf "%d ") <| [1..3]
List.iter (printf "%d ") [1..3]

ns operatorul pipe-backward este important atunci cnd se dorete schimbarea precedenei. n exemplul urmtor, folosirea sa conduce la eliminarea parantezelor.
printfn "123^3 = %d" (cube 123)
printfn "123^3 = %d" <| cube 123

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

let cubeLength x =
let x3 = x * x * x
let str = string x3
str.Length

3.4. Operatorul de compunere napoi

let isEven n =
n%2=0
let odds = [1 .. 10] |> List.filter (not << isEven) // [1; 3; 5; 7; 9]

4. Aplicaii
4.1. S se afieze antepenultimul element al unei liste, fr a folosi funcia List.nth.
Rezolvai problema n dou moduri: folosind operatorul pipe-forward |> i respectiv operatorul de
compunere nainte >>.
Exemplu:
let list = [ 1 .. 10 ]
printfn "Antepenultimul element: %A" (lastButTwo list)
Antepenultimul element: 8

La rezolvarea problemelor 4.2 - 4.4, folosii operatorul pipe |>.


4.2. La un magazin, dac un client cumpr cel puin 3 produse, i se acord o reducere de
10% pentru al doilea cel mai scump produs i o reducere de 20% pentru al treilea cel mai scump
produs. S se calculeze suma total pe care trebuie s o plteasc clientul.
Pentru rezolvare, nu extragei primele dou elemente pentru a le prelucra separat, ci operai
o transformare asupra ntregii liste de preuri.
Exemplu:
let mainP3() =
let list = [ 20.0; 10.0; 30.0 ]
printfn "Preturile articolelor fara reduceri: %A" list
printfn "Suma fara reduceri: %.2f" (List.sum list)
printfn "Suma finala: %.2f\n" (discountedSum list)
let list = [ 10.0; 20.0 ]
printfn "Preturile articolelor fara reduceri: %A" list
printfn "Suma fara reduceri: %.2f" (List.sum list)
printfn "Suma finala: %.2f\n" (discountedSum list)
Preturile articolelor fara reduceri: [20.0; 10.0; 30.0]
Suma fara reduceri: 60.00
Suma finala: 56.00
Preturile articolelor fara reduceri: [10.0; 20.0]
Suma fara reduceri: 30.00
Suma finala: 30.00

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

Acest operator primete dou funcii, o aplic mai nti pe cea din dreapta i apoi pe cea din
stnga. Este util n general cnd se dorete negarea rezultatului unei funcii, pstrnd ordinea
natural a operaiilor, ca n exemplul de mai jos:

father, Christopher, Arthur


// Christopher este tatl lui Arthur
father, Christopher, Victoria
father, Andrew, James
father, Andrew, Jennifer
father, James, Colin
father, James, Charlotte
father, Roberto, Emilio
father, Roberto, Lucia
father, Pierro, Marco
father, Pierro, Angela
father, Marco, Alfonso
father, Marco, Sophia
mother, Penelope, Arthur
mother, Penelope, Victoria
mother, Christine, James
mother, Christine, Jennifer
mother, Victoria, Colin
mother, Victoria, Charlotte
mother, Maria, Emilio
mother, Maria, Lucia
mother, Francesca, Marco
mother, Francesca, Angela
mother, Lucia, Alfonso
mother, Lucia, Sophia
husband, Christopher, Penelope
husband, Andrew, Christine
husband, Arthur, Margaret
husband, James, Victoria

husband, Charles, Jennifer


husband, Roberto, Maria
husband, Pierro, Francesca
husband, Emilio, Gina
husband, Marco, Lucia
husband, Tomaso, Angela
wife, Penelope, Christopher
wife, Christine, Andrew
wife, Margaret, Arthur
wife, Victoria, James
wife, Jennifer, Charles
wife, Maria, Roberto
wife, Francesca, Pierro
wife, Gina, Emilio
wife, Lucia, Marco
wife, Angela, Tomaso
son, Arthur, Christopher
son, Arthur, Penelope
son, James, Andrew
son, James, Christine
son, Colin, Victoria
son, Colin, James
son, Emilio, Roberto
son, Emilio, Maria
son, Marco, Pierro
son, Marco, Francesca
son, Alfonso, Lucia
son, Alfonso, Marco
daughter, Victoria, Christopher

daughter, Victoria, Penelope


daughter, Jennifer, Andrew
daughter, Jennifer, Christine
daughter, Charlotte, Victoria
daughter, Charlotte, James
daughter, Lucia, Roberto
daughter, Lucia, Maria
daughter, Angela, Pierro
daughter, Angela, Francesca
daughter, Sophia, Lucia
daughter, Sophia, Marco
brother, Arthur, Victoria
brother, James, Jennifer
brother, Colin, Charlotte
brother, Emilio, Lucia
brother, Marco, Angela
brother, Alfonso, Sophia
sister, Victoria, Arthur
sister, Jennifer, James
sister, Charlotte, Colin
sister, Lucia, Emilio
sister, Angela, Marco
sister, Sophia, Alfonso
uncle, Arthur, Colin
uncle, Charles, Colin
uncle, Arthur, Charlotte
uncle, Charles, Charlotte
uncle, Emilio, Alfonso
uncle, Tomaso, Alfonso

uncle, Emilio, Sophia


uncle, Tomaso, Sophia
aunt, Jennifer, Colin
aunt, Margaret, Colin
aunt, Jennifer, Charlotte
aunt, Margaret, Charlotte
aunt, Angela, Alfonso
aunt, Gina, Alfonso
aunt, Angela, Sophia
aunt, Gina, Sophia
nephew, Colin, Arthur
nephew, Colin, Jennifer
nephew, Alfonso, Emilio
nephew, Alfonso, Angela
nephew, Colin, Margaret
nephew, Colin, Charles
nephew, Alfonso, Gina
nephew, Alfonso, Tomaso
niece, Charlotte, Arthur
niece, Charlotte, Jennifer
niece, Sophia, Emilio
niece, Sophia, Angela
niece, Charlotte, Margaret
niece, Charlotte, Charles
niece, Sophia, Gina
niece, Sophia, Tomaso

Se d urmtorul program, care rezolv problema ntr-o manier imperativ. Rescriei funcia
findParents ntr-o manier funcional. Se pot aduga i alte funcii n program.
open System
open System.IO
let list =
let lineToTuple (line : string) =
let toks = line.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
(toks.[0], toks.[1], toks.[2])
File.ReadAllLines "kinship.csv"
|> List.ofSeq
|> List.map lineToTuple
let findParents() =
let mothers = List.filter (fun (a, _, _) -> a = "mother") list
let fathers = List.filter (fun (a, _, _) -> a = "father") list
[
for (_, m, c1) in mothers do
for (_, f, c2) in fathers do
if c1 = c2 then
yield (m, f, c1)
]
let printParents parents =
List.iteri (fun i (m, f, c) -> printfn "%d. mama %s / tata %s / copilul %s" (i + 1) m f c) parents
let mainP3() =
findParents() |> printParents

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

4.3. S se realizeze un program care, plecnd de la relaiile de rudenie dintre mai multe
persoane, prezentate mai jos, s determine ce persoane sunt prinii aceluiai copil.

4.4. Fie urmtorul arbore genealogic:


Liviu

Vlad

Oana

Mircea

Maria

Dan

Nelu

Paul

Radu Sorin

S se afieze verii unei anumite persoane.


Indicaii: se dau urmtoarele funcii:
let parents =
let vlad = { Name = "Vlad"; Children = ["Oana"; "Mircea"] }
let maria = { Name = "Maria"; Children = ["Dan"] }
let nelu = { Name = "Nelu"; Children = ["Paul"; "Radu"; "Sorin"] }
let liviu = { Name = "Liviu"; Children = ["Vlad"; "Maria"; "Nelu"] }
[ liviu; vlad; maria; nelu ]
let mainP4() =
let query = "Sorin"
printf "Verii lui %s sunt: " query
let result = findCousins query
printfn "%A" result

Definii tipul nregistrare Person corespunztor persoanelor definite n parents. Din vectorul
parents, construii un alt vector de string-uri numit persons, care conine doar numele tuturor
persoanelor din arborele genealogic, indiferent de relaiile lor de rudenie. Creai apoi funcia
isCousin care returneaz true dac dou persoane sunt veri. Este util i crearea unei funcii isParent
care se poate apela din isCousin.
Exemplu:
Verii lui Sorin sunt: ["Oana"; "Mircea"; "Dan"]

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

Florin Leon, Inteligenta artificiala - Laborator, http://florinleon.byethost24.com/lab_ia.htm

1. mama Penelope / tata Christopher / copilul Arthur


2. mama Penelope / tata Christopher / copilul Victoria
3. mama Christine / tata Andrew / copilul James
4. mama Christine / tata Andrew / copilul Jennifer
5. mama Victoria / tata James / copilul Colin
6. mama Victoria / tata James / copilul Charlotte
7. mama Maria / tata Roberto / copilul Emilio
8. mama Maria / tata Roberto / copilul Lucia
9. mama Francesca / tata Pierro / copilul Marco
10. mama Francesca / tata Pierro / copilul Angela
11. mama Lucia / tata Marco / copilul Alfonso
12. mama Lucia / tata Marco / copilul Sophia

S-ar putea să vă placă și