Documente Academic
Documente Profesional
Documente Cultură
Laboratorul 3
Trebuie spus c i n cazul valorilor imutabile, este permis urmtoarea secven de cod:
let x = 0
let x = x + 1
1. Obiective
Valoarea iniial a lui x nu mai este accesibil, dei se pstreaz n memorie. Acest proces
care imit actualizarea unei variabile, dar respect ideea de imutabilitate, i care se face cu un
consum de memorie suplimentar, se numete shading.
n programarea funcional pur, buclele nu sunt posibile pentru c nu este posibil
actualizarea valorii unui contor. Iteraiile sunt realizate prin funcii recursive, unde apelurile
succesive au valori actualizate ale argumentelor. n F# exist ns construcii imperative de tip
bucl, care pot fi exprimate n mai multe moduri, dup cum se arat mai jos.
let mutable i = 0
while i < 10 do
i <- i + 1
printfn "%d" i
for i = 1 to 10 do
printfn "%d" i
for i = 10 downto 1 do
printfn "%d" i
for i in 1 .. 10 do // sau: for i in [1 .. 10] do
printfn "%d" i
n ultimul caz, se folosete enumeratorul listei, fiind echivalentul lui foreach din C#.
3. Vectori (Arrays)
Listele sunt foarte eficiente cnd se adug sau se terg elemente la nceputul lor, dar
accesul la celelalte elemente este ncet. Dac se dorete modificarea elementului de la sfritul unei
liste, trebuie clonat toat lista.
Vectorii sunt blocuri continue de memorie cu zero sau mai multe elemente, n care fiecare
element poate fi modificat individual, spre deosebire de liste, care sunt imutabile.
3.1. Funcii pentru crearea i accesarea elementelor
Ca i listele, vectorii pot fi creai n mai multe moduri, inclusiv prin array comprehensions:
> let array1 = [| 1; 2; 3 |] // [|1; 2; 3|]
> let array2 = [| 1 .. 10 |] // [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
> let array3 = [| 1 .. 2 .. 10 |] // [|1; 3; 5; 7; 9|]
> let array4 = [| for i in 1 .. 10 -> i * i |] // [|1; 4; 9; 16; 25; 36; 49; 64; 81; 100|]
let x = 0
let x_2 = x + 1
//
//
//
//
//
//
9
atribuire
[|9; 17; 25|]
[|1; 4; 9; 17|]
[|17; 25; 36; 49; 64; 81; 100|]
[|1; 4; 9; 17; 25; 36; 49; 64; 81; 100|]
Descriere
blit
Copiaz un numr de elemente ncepnd cu un index dintr-un vector n alt vector ncepnd cu alt
index.
let a1 = [| 11 .. 15 |]
let a2 = Array.zeroCreate 10
// copiaza 3 elemente din a1, de la indexul 1 din a1, la indexul 2 din a2
Array.blit a1 1 a2 2 3 // a2 = [|0; 0; 12; 13; 14; 0; 0; 0; 0; 0|]
copy
create
fill
init
zeroCreate
Spre deosebire de funciile de sortare de la liste, care returneaz alte liste ca rezultat,
funciile corespunztoare de la vectori modific elementele vectorului asupra cruia sunt aplicate.
Funcie
Descriere
sortInPlace
sortInPlaceBy
Elementul cu indexul i al unui vector a poate fi accesat cu sintaxa a.[i]. Indexarea pornete
de la 0. Elementele pot fi modificate. De asemenea, se poate accesa o anumit poriune dintr-un
vector, dup cum se poate vedea mai jos:
ntr-o matrice zimat, liniile sunt iniializate independent i pot avea un numr diferit de
elemente (asemntor cu modul n care se iniializeaz matricele n C/C++):
let jagged = [| for a in 1 .. 5 -> [| 1 .. a |] |]
for x = 0 to jagged.Length - 1 do
for y = 0 to jagged.[x].Length - 1 do
printf "%d " jagged.[x].[y]
printfn ""
Rezultat:
1
12
123
1234
12345
4. Secvene
4.1. Evaluarea pasiv
Pn acum, codul creat a fost evaluat n mod activ (engl. eager). Cnd se definete de
exemplu o list, ea se creeaz efectiv n memorie, chiar dac nu o folosim ulterior. Exist ns
situaii n care dorim s evalum o expresie doar atunci cnd dorim. De exemplu, putem avea
nevoie s definim o secven foarte mare de date, dar dorim s se calculeze aceste valori doar n
momentul n care le folosim efectiv. Acest mod de evaluare se numete pasiv sau ntrziat (engl.
lazy) i poate conduce la o scdere foarte mare a necesarului de memorie a unui program.
Cea mai des utilizat form de evaluare pasiv este prin utilizarea secvenelor. O secven
este un ir ordonat de elemente, asemntor unei liste. S considerm cteva exemple de iniializare:
Spre deosebire de iniializarea listelor i vectorilor, se vede c aici n FSI nu apar valorile,
pentru c acestea nu au fost evaluate nc.
Elementele pot fi afiate cu ajutorul funciei Seq.iter:
> Seq.iter (printf "%d ") seq1;;
1 2 3 4 5 6 7 8 9 10
O secven poate fi parcurs n maniera dorit cu ajutorul funciei Seq.skip, care sare peste
un numr de elemente i Seq.take, care evalueaz un numr de elemente:
> let seq3 = Seq.skip 2 seq2;;
val seq3 : seq<int>
> Seq.take 4 seq3;;
val it : seq<int> = seq [9; 16; 25; 36]
Avnd n vedere evaluarea pasiv a unei secvene, este posibil definirea unor secvene
infinite. n exemplul urmtor, funcia Seq.initInfinite definete irul numerelor naturale. Din acest
ir, se evalueaz primele 10.
> let seqInfinite = Seq.initInfinite (fun n -> n + 1);;
val seqInfinite : seq<int>
> Seq.take 10 seqInfinite;;
val it : seq<int> = seq [1; 2; 3; 4; ...]
Rezultatul este prezentat mai jos. Se observ c valorile sunt evaluate doar atunci cnd este
nevoie: mai nti primele trei de care este nevoie pentru a ajunge la a patra i apoi urmtoarele,
nainte s fie afiate.
Skip 3
Take 3
Print
In definitie: 1
In definitie: 2
In definitie: 3
In definitie: 4
In iteratie: 4
In definitie: 5
In iteratie: 5
In definitie: 6
In iteratie: 6
Expresia unei secvene poate fi recursiv. Prin utilizarea cuvntului cheie yield! (yield
bang), se pot returna subsecvene, care vor fi introduse n ordine n secvena principal.
Urmtoarea funcie ntoarce toate fiierele dintr-un director i subdirectoarele acestuia:
open System.IO
let rec allFilesUnder basePath = seq {
yield! Directory.GetFiles(basePath) // toate fisierele din directorul de baza
for subdir in Directory.GetDirectories(basePath) do
yield! allFilesUnder subdir // toate fisierele din subdirectoare
}
Seq.iter (printfn "%A") (allFilesUnder """d:\""")
Funcie
Descriere
countBy
Aplic o funcie care atribuie o cheie fiecrui tip de element al unei secvene i returneaz o secven
care include tipurile gasite i numrul elementelor de fiecare tip din secvena original. Aceast funcie
se bazeaz pe traversarea secvenei, prin urmare nu trebuie folosit pentru secvene lungi sau infinite.
let seq1 = seq { -10 .. 10 }
let seq2 = Seq.countBy (fun elem -> if elem < 0 then -1 elif elem % 2 = 0 then 0 else 1) seq1
printfn "%A" seq2 // seq [(-1, 10); (0, 6); (1, 5)]
distinct
skipWhile
Returneaz secvena original din care sunt excluse primele elementele care ndeplinesc condiiile
specificate prin funcia definit de utilizator.
let seq1 = seq { for i in 1 .. 10 -> i *i }
Seq.iter (fun e -> printf "%d " e) seq1; printfn "" // 1 4 9 16 25 36 49 64 81 100
let seq2 = Seq.skipWhile (fun x -> x < 50) seq1
Seq.iter (fun e -> printf "%d " e) seq2 // 64 81 100
takeWhile
Returneaz elementele din secvena iniial care ndeplinesc condiiile specificate prin funcia definit
de utilizator.
let seq1 = seq { for i in 1 .. 10 -> i *i } // seq1 = 1 4 9 16 25 36 49 64 81 100
let seq2 = Seq.takeWhile (fun x -> x < 50) seq1 // 1 4 9 16 25 36 49
truncate
windowed
Returneaz o secven de ferestre, fiecare cu un numr specificat de elemente, mergnd din element n
element. Fiecare fereastr este returnat ca un vector nou.
let seq1 = seq { for i in 0..2..8 -> i *i } // seq1 = 0 4 16 36 64
let windows = Seq.windowed 3 seq1
Seq.iter (fun e -> printf "%A " e) windows; printfn "" // [|0; 4; 16|] [|4; 16; 36|] [|16; 36; 64|]
let movingAverage = Seq.map (fun a -> Array.averageBy (fun e -> float e) a) windows
Seq.iter (fun e -> printf "%.2f " e) movingAverage // 6.67 18.67 38.67
Lista tuturor funciilor din modulul Seq poate fi consultat n documentaia MSDN:
https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/collections.seq-module-%5Bfsharp%5D .
n coleciile Map i Set, pe care le vom prezenta n seciunea 6, exist urmtoarele funcii de
conversie: Map.ofArray, Map.ofList, Map.ofSeq, Map.toArray, Map.toList, Map.toSeq, Set.ofArray,
Set.ofList, Set.ofSeq, Set.toArray, Set.toList, Set.toSeq.
6. Alte colecii
6.1. Map
Colecia Map reprezint tabele hash imutabile. Mai jos sunt descrise cteva funcii specifice:
Funcie
Descriere
add
Adaug un element nou n tabel. Dac se adaug un element cu o cheie existent, valoarea este nlocuit.
let m1 = Map.ofList [ (1, "unu"); (2, "doi") ]
let m2 = Map.add 3 "trei" m1
Map.iter (fun key value -> printfn "Cheie: %d, valoare: %s" key value) m2
Rezultate:
Cheie: 1, valoare: unu
Cheie: 2, valoare: doi
Cheie: 3, valoare: trei
containsKey
find
findKey
Returneaz prima cheie pentru care predicatul este satisfcut. Dac nu exist nicio astfel de cheie, se
arunc o excepie.
let m = Map.ofList [ (1, "unu"); (2, "doi"); (3, "trei") ]
printfn "%A" (Map.findKey (fun key value -> key >= 2) m) // 2
remove
Elimin un element din tabel. n cazul n care elementul nu exist, nu apare nicio excepie.
Lista tuturor funciilor din modulul Map poate fi consultat n documentaia MSDN:
https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/collections.map-module-%5Bfsharp%5D .
5.3. Set
Funcia
Descriere
contains
count
difference
Returneaz o nou mulime format din elementele primei mulimi din care s-au eliminat
elementele celei de-a doua.
let s1 = Set.ofList [1;2;3]
let s2 = Set.ofList [2;3;4]
printfn "%A" (Set.difference s1 s2) // set [1]
intersect
intersectMany
isProperSubset
Returneaz true dac toate elementele din prima mulime sunt n cea de-a doua i cel puin un
element din a doua mulime nu este n prima mulime.
let set1 = Set.ofList [ 1 .. 6 ]
let set2 = Set.ofList [ 1 .. 5 ]
let set3 = Set.ofList [ 1 .. 6 ]
printfn "%b" (Set.isProperSubset set2 set1) // true
printfn "%b" (Set.isProperSubset set3 set1) // false
isProperSuperset
Returneaz true dac toate elementele din a doua mulime sunt n prima mulime i cel puin un
element din prima mulime nu este n a doua.
isSubset
Returneaz true dac toate elementele din prima mulime sunt n a doua mulime.
printfn "%b" (Set.isSubset set2 set1) // true, cu mulimile set1, set2 i set3 de la isProperSubset
printfn "%b" (Set.isSubset set3 set1) // true
isSuperset
Returneaz true dac toate elementele din a doua mulime sunt n prima mulime.
maxElement
minElement
singleton
union
unionMany
Lista tuturor funciilor din modulul Set poate fi consultat n documentaia MSDN:
https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/collections.set-module-%5Bfsharp%5D .
Colecia Set reprezint mulimi imutabile. Mai jos sunt descrise cteva funcii specifice:
6. Comparaie
Imutabil
Caracteristici
Da
Avantaje:
Posibilitatea aplicrii potrivirii modelelor (engl. pattern matching);
Posibilitatea crerii unor iteraii complexe prin recursivitate;
Prelucrare rapid a primului element;
Dezavantaje:
Accesul indexat i alte tipuri de accesare ale elementelor sunt lente.
Nu
Seq
Da
Map
Da
Set
Da
ResizeArray
Nu
Alias pentru tipul .NET List. Avantajele i dezavantajele sunt similare cu ale
vectorilor, dar listele sunt redimensionabile.
List
Array
7. Aplicaii
7.1. S se realizeze un program care calculeaz valoarea real a unei fracii continue. O
fracie continu este o expresie obinut n urma unui proces iterativ de reprezentare a unui numr
ca suma unor numere ntregi i inverse ale unor ntregi, de forma:
= +
1
+ +
= , , , ,
unde i , 1.
Avnd n vedere tema laboratorului, rezolvai mai nti problema cu o bucl for.
Rezolvai-o apoi i n mod funcional, folosind funcia Array.fold.
10
Colecie
Exemplu:
let mainP1() =
printfn "Valoarea lui pi este:\t%A" System.Math.PI
printfn "Valoarea fractiei este:\t%A" (contFraction [| 3; 7; 15 |])
printfn "Valoarea fractiei este:\t%A" (contFraction [| 3; 7; 15; 1; 292; 1 |])
=
+ + + +
!
0! 1! 2! 3!
Definii o secven infinit ai crei termeni corespund termenilor seriei de mai sus. Realizai
o funcie care aproximeaz funcia exponenial cu o precizie dorit, precizia nsemnnd n acest
caz faptul c termenii seriei mai mici dect aceast valoare sunt ignorai.
Indicaie: Pentru definirea secvenei infinite, observai c termenul generic corespunztor lui
n poate fi scris recursiv:
=
! ( 1)!
Funcia principal poate fi urmtoarea:
let mainP2() =
let x = 2.0
let p = 1e-5
// x este exponentul iar p este precizia
printfn "Serie:\t%.6f" (exp' x p)
printfn "Exact:\t%.6f" (exp x)
Rezultate:
Serie: 7.389046
Exact: 7.389056
11
Rezultate:
let mainP3() =
let rand = System.Random()
let a = Array2D.init 2 3 (fun x y -> rand.Next(10))
let b = Array2D.init 3 4 (fun x y -> rand.Next(10))
let c = multiply a b
printfn "A = \n%A\n\nB = \n%A\n\nA * B = \n%A" a b c
let d = multiply b a
printfn "\nB * A = \n%A" d
Exemplu:
A=
[[8; 4; 4]
[7; 9; 5]]
B=
[[6; 4; 5; 1]
[4; 9; 0; 9]
[4; 2; 6; 0]]
A*B=
[[80; 76; 64; 44]
[98; 119; 65; 88]]
B*A=
[[0]]
12