Sunteți pe pagina 1din 18

Inteligen artificial

Laboratorul 1

Introducere n F#

Obiectivele acestui laborator sunt urmtoarele:

prezentarea caracteristicilor fundamentale ale paradigmei de programare funcional;


descrierea noiunilor de baz necesare pentru realizarea unui program simplu F#: valori,
funcii, metode de citire i afiare la consol, mdul de organizare al codului.

2. Programarea funcional. Limbajul F#


Programarea funcional este o paradigm de programare, cum este i programarea orientat
pe obiecte. Aceste idei sunt destul de vechi: primul limbaj funcional, Lisp, a aprut n 1958 i este
nc utilizat. Dei limbajele funcionale sunt succinte i expresive, pn recent nu au fost folosite pe
scar larg, ci n general pentru aplicaii specifice de inteligen artificial.
ns n zilele noastre exist noi provocri. Trebuie prelucrate mulimi mari de date n paralel
i ntr-o manier scalabil. Programele trebuie testate ct mai uor i ar fi de dorit ca logica acestora
s fie exprimat clar, ntr-un mod uor de neles, care s se axeze n special pe rezultatele obinute
(ce se dorete) i nu pe detaliile de execuie (cum se obin rezultatele).
De aceea, multe limbaje de programare importante au nceput s includ caracteristici
funcionale, de exemplu tipuri generice, metode anonime i expresii lambda. LINQ din platforma
.NET este bazat pe concepte funcionale. De asemenea, limbajele funcionale propriu-zise au
nceput s se bucure de o mai mare atenie i sunt folosite din ce n ce mai mult pentru aplicaii
comerciale.
Probabil unul din cele mai simple limbaje funcionale, dar cu o putere de exprimare
echivalent cu aceea a limbajelor utilizate pe scar larg este F# de la Microsoft. Un alt avantaj este
integrarea n platforma .NET, ceea ce permite interoperabilitatea deplin cu programe realizate n
alte limbaje .NET, precum C#, i folosirea numeroaselor funcii din bibliotecile disponibile.
F# nu este un limbaj funcional pur, ci multi-paradigm. Pe lng abordarea funcional,
permite utilizarea conceptelor de programare imperativ i orientat pe obiecte, ceea ce i crete
flexibilitatea. n cadrul laboratoarelor de Inteligen artificial, ne vom concentra pe aspectele
funcionale, acestea fiind considerate drept principalul caracter de noutate, ns vom prezenta
succint i celelalte caracteristici ale limbajului.
Datorit sintaxei concise, programele F# sunt mult mai compacte dect programele C#
echivalente. S considerm un program care calculeaz suma ptratelor numerelor de la 1 la n.

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

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

1. Obiective

F#

public class SumOfSquares


{
public int Square(int x)
{
return x * x;
}

let square x = x * x
let sumOfSquares n = [1..n] |> List.map square |> List.sum
sumOfSquares 100

public int SumOfSquares(int n)


{
int sum = 0;
for (int i = 1; i <= n; i++)
sum += Square(i);
return sum;
}
public int GetResult()
{
return SumOfSquares(100);
}
}

Nu este necesar nelegerea codului F# acum. Putem spune doar c ideea de baz este
transformarea succesiv a listei de numere de la 1 la n ntr-o list cu ptratele lor i apoi sumarea
acestor elemente. Se poate observa compunerea funciilor cu ajutorul operatorului pipe |>. De
asemenea, funciile sunt definite foarte simplu, aproape de echivalentul lor matematic.

3. Mediul de dezvoltare Visual Studio pentru F#


Pentru a realiza o aplicaie de tip consol F# n mediul Visual Studio, se alege File New...
Project Visual F# Console Application.
Un instrument util pentru testarea rapid a unor secvene de cod este aa numitul F#
Interactive (FSI), care se poate activa din: View Other Windows F# Interactive sau prin
combinaia de taste Ctrl + Alt + F. Spre deosebire de un program F# normal, aici blocul de cod
evaluat (care poate fi compus din una sau mai multe linii) trebuie s se termine cu ;; , dup cum
se poate vedea n figura urmtoare.

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

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

C#

4. Valori
Cele dou componente fundamentale ale unui program F# sunt valorile i funciile. Ambele
se definesc cu ajutorul cuvntului cheie let. n aceast seciune, ne vom referi la valori. Pentru a
crea o valoare ntreag n egal cu 1, se folosete expresia:

Este important de spus c una din caracteristicile programrii funcionale este imutabilitatea
(engl. immutability). Odat definit, o valoare nu mai poate fi modificat. Acest lucru poate prea
ciudat pentru un programator obinuit cu ideea de variabil din programarea structurat, al crei
sens este tocmai cel de a se modifica. Totui, imutabilitatea este prezent n platforma .NET. De
exemplu, obiectele string sunt imutabile (engl. immutable). Toate operaiile asupra unui string
returneaz alte string-uri, nu modific obiectul iniial.
Valorile imutabile sunt asemntoare celor din matematic. Datorit lor, programul poate
deveni mai clar. De asemenea, un avantaj important apare n cazul aplicaiilor concurente, unde
sincronizarea se simplific mult prin renunarea la conceptul de stare partajat (engl. shared
state).
ns F# nu este un limbaj funcional pur i permite valori mutabile, marcate explicit ca
mutable. Acestea vor fi prezentate n laboratorul 3, dedicat programrii imperative n F#. n orice
caz, recomandarea general este de a nu folosi inutil concepte non-funcionale n F#. Nu are niciun
rost s scriem cod C n F#, ci ne propunem s nvm o nou paradigm de programare.
4.1. Tipuri numerice
F# este un limbaj puternic tipizat, cu tipuri statice, dei de cele mai multe ori acestea nu apar
explicit n definiia funciilor. Pentru a defini tipul unei valori numerice, se folosesc o serie de
sufixe. Tabelul urmtor prezint tipurile numerice din F#, cu exemple de utilizare.
Tip F#

Tip .NET echivalent

Sufix

int

System.Int32

float

System.Double

byte
sbyte
int16
uint16
uint32
int64
uint64

System.Byte
System.SByte
System.Int16
System.UInt16
System.Int32
System.Int64
System.UInt64

uy
y
s
us
u
L
UL

decimal

System.Decimal

bigint

System.Numerics.BigInteger

Domeniu
231, 231 1
valoare n virgul mobil cu
dubl precizie, cu aproximativ 15
cifre semnificative
0, 255
128, 127
32768, 32767
0, 65535
0, 2311
263, 2631
0, 2641
valoare n virgul mobil cu
precizie de exact 28 de cifre
valoare ntreag cu semn, arbitrar
de mare

Exemplu
let n = 1
let hex = 0xFF // 255
let oct = 0o100 // 64
let bin = 0b1111 // 15
let n = 1.1
let n = 1uy
let n = -1y
let x = -100s
let y = 50us
let z = 2000u
let x = 1000L
let x = 1000UL
let a =
0.00000000012232423434
let b =
436426843762874628764382I

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

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

let n = 1

n FSI, se poate vedea tipul unei valori definite, ca n exemplele de mai jos. Pentru a marca
diferena ntre secvenele de cod scrise ca programe propriu-zise i cele din FSI, n primul caz vom
folosi un chenar continuu, iar n al doilea caz, un chenar punctat.
> let n = -1y;;
val n : sbyte = -1y

> let b = 436426843762874628764382I;;


val b : System.Numerics.BigInteger = 436426843762874628764382

n F# se utilizeaz operatorii normali pentru operaiile de baz: adunare +, scdere -,


nmulire * i mprire /. Pentru ridicarea la putere se folosete **, iar pentru aflarea restului
mpririi se utilizeaz %. Aceti operatori sunt prezentai n tabelul urmtor.
Operator
+
*

Descriere
Adunare
Scdere
nmulire

mprire

**
%

Ridicare la putere
Modl

Exemplu
1+2
1-2
2*3
8/3
8.0 / 3.0
2.0 ** 8.0
7%3

Rezultat
3
-1
6
2
2.666666667
256.0
1

De asemenea, se pot utiliza funcii trigonometrice (cos, sin, tan), logaritmice i exponeniale
(log, log10, exp) precum i funcii matematice de baz (sqrt, sign, abs, ceil, floor, pown). Funciile
matematice uzuale sunt prezentate n tabelul urmtor.
Funcie
abs
sign
ceil
floor
exp
log
log10
sqrt
cos
sin
tan
pown

Descriere
Valoare absolut
Semn
Rotunjire la ntregul mai mare
Rotunjire la ntregul mai mic
Exponenial
Logaritm natural
Logaritm n baza 10
Rdcin ptrat
Cosinus
Sinus
Tangent
Puterea unui ntreg

Exemplu
abs -1.0
sign -7
ceil 7.3
floor 7.3
exp 1.0
log 2.718281828
log10 100.0
sqrt 25.0
cos 0.0
sin 0.0
tan 1.0
pown 2 10

Rezultat
1.0
-1
8.0
7.0
2.718281828
1.0
2.0
5.0
1.0
0.0
1.557407725
1024

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

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

> let h = 0x7A4D;;


val h : int = 31309

Funcie
sbyte
byte
int16
uint16
int, int32
uint64
float
float32
decimal

Descriere
Conversie argument la sbyte
Conversie argument la byte
Conversie argument la int16
Conversie argument la uint16
Conversie argument la int
Conversie argument la uint64
Conversie argument la float
Conversie argument la float32
Conversie argument la decimal

Exemplu
sbyte -10
byte "25"
int16 'a'
uint16 12
int 4.5
uint64 "0xF0"
float 3.1415M
float32 8y
decimal 1.23

Rezultat
-10y
25uy
97s
12us
4
240UL
3.1415
8.0f
1.23M

4.2. Caractere, iruri de caractere, valori logice


Pentru definirea valorilor de tip caracter se utilizeaz apostroful sau codul hexazecimal.
Urmtorul exemplu evideniaz cele dou moduri de definire a caracterelor:
> let m = 'm';;
val m : char = 'm'
> let a = '\u0061';;
val a : char = 'a'

Caracterele speciale n F# sunt precedate de \: \' (apostrof), \" (ghilimele), \\ (backslash),


\b (backspace), \r (carriage return), \n (line feed), \t (tab).
De asemenea, este posibil conversia unui caracter n alte tipuri dect char. Astfel, pentru
conversia ntr-un int sau byte se pot utiliza urmtoarele expresii:
> let an = int 'a';;
val an : int = 97
> let ab = 'a'B;;
val ab : byte = 97uy

irurile de caractere se noteaz ntre ghilimele. Pentru a include caractere speciale, se


utilizeaz ghilimelele triple:
> let s1 = "abc";;
val s1 : string = "abc"
> let s2 = "c:\\ia\\lab01";;
val s2 : string = "c:\ia\lab01"
> let s3 = """c:\ia\lab01""";;
val s3 : string = "c:\ia\lab01"

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

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

Conversia ntre tipurile primare de date se face cu ajutorul funciilor din tabelul urmtor.

Valorile logice pot fi true sau false. Operatorii logici sunt: && (and), || (or) i not:
> let b1 = true;;
val b1 : bool = true
> let b2 = false;;
val b2 : bool = false

Egalitatea se testeaz cu un singur semn =, de exemplu n instruciunile condiionale:


let a = 2
let semn =
if a < 0 then -1
elif a = 0 then 0
else 1

4.3. Tuple
O tupl este o colecie ordonat de date utilizat pentru gruparea acestora. O tupl poate
avea elemente de tipuri diferite, inclusiv alte tuple, separate prin virgul i aezate, opional, ntre
paranteze. De exemplu: ("unu", "doi"), (1, 3, 5) sau (1.1, (3, 4, "cuvant"), 5L).
Intern, tipul unei tuple se compune din tipurile elementelor, unite printr-un asterisc. Se poate
observa acest lucru n FSI:
> let pereche = ("unu", "doi");;
val pereche : string * string = ("unu", "doi")
> let numere = (1, 3, 5);;
val numere : int * int * int = (1, 3, 5)
> let complex = (1.1, (3, 4, "cuvant"), 5L);;
val complex : float * (int * int * string) * int64 = (1.1, (3, 4, "cuvant"), 5L)

Pentru a accesa elementele unei tuple cu dou elemente se pot folosi funciile fst (first,
primul element) i snd (second, al doilea element):
let f = fst pereche // "unu"
let s = snd pereche // "doi"

Elementele unei tuple se pot extrage printr-o atribuire multipl a unor valori:
let (x, y, z) = numere // sau fr paranteze: let x, y, z = numere => x = 1, y = 3, z = 5

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

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

> let b3 = b1 && (not b2);;


val b3 : bool = true

5. Funcii
Funciile se definesc tot cu let, doar c parametrii apar imediat dup nume. De exemplu, o
funcie pentru ridicare la ptrat se poate scrie astfel:

n F# nu se folosete cuvntul cheie return. Ultima expresie evaluat de o funcie este cea
returnat, n cazul de fa x * x.
5.1. Inferena tipurilor
Compilatorul are un motor puternic de inferen a tipurilor, care previne multe erori
semantice (dei la nceput astfel de erori de compilare pot fi exasperante).
De exemplu, pentru funcia de mai sus, compilatorul consider implicit c argumentul este
de tip int.

Trebuie observat notaia pentru tipul funciei: (tip argument) (tip de return).
Dac funcia este apelat cu un argument real, compilatorul i actualizeaz definiia tipului
acesteia.

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

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

let square x = x * x

n F# nu se pot suprancrca funciile, prin urmare n cazul de mai sus ar trebui fcute dou
funcii cu nume diferite, una pentru argumente ntregi i alta pentru argumente reale.
Pentru a fi exaci, suprancrcarea funciilor este posibil pentru metodele din clase, pentru a
pstra compatibilitatea cu limbajele platformei .NET, i se mai poate realiza prin anumite artificii,
de exemplu cu uniuni discriminate care vor fi prezentate n laboratorul 5. ns suprancrcarea
funciilor nu este parte din filosofia funcional a limbajului.
n unele cazuri, compilatorul nu reuete s determine tipul unor argumente. Pentru a-l ajuta,
se folosete adnotarea tipurilor (engl. type annotation), n care acestea sunt definite explicit.
n exemplul de mai jos, dorim s convertim un ir de caractere care conine trei valori
separate de virgul sau spaiu, ntr-o tupl cu aceste trei valori. Pentru a folosi metoda .NET Split,
compilatorului trebuie s i se spun c argumentul line este de tip string.
open System
let lineToTuple (line : string) =
let toks = line.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
(toks.[0], toks.[1], toks.[2])

Exist i funcii generice, care pot fi apelate cu orice tip de parametru. Intern, tipurile
generice se noteaz cu un apostrof urmat de o liter, de obicei pornind de la a: 'a, 'b etc. ntruct
funcia identitate nu face prelucrri ale parametrului, acesta poate rmne generic iar programul
urmtor este corect:

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

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

n cazul n care se apeleaz funcia cu argumente de tipuri diferite, va aprea o eroare de


compilare:

Se pot defini i funcii fr parametri. Funcia urmtoare citete un ir de caractere de la


consol i l returneaz:

Tipul su este definit ca unit string. unit este echivalentul F# al cuvntului cheie void din
C/Java/C#.
Exist o diferen important ntre definirea funciei de mai sus, read(), n care se folosesc
paranteze i definirea fr paranteze:
let read =
System.Console.ReadLine()

n al doilea caz, read este o valoare, nu o funcie. Dac se folosete aa, citirea de la consol
se va face ntotdeauna, chiar dac read nu este apelat n continuare n program.
De asemenea, n secvena de mai jos, se citesc dou valori diferite de la consol:
let a = read()
let b = read() // a poate fi diferit de b, in functie de sirurile introduse in read()

n urmtoarea secven, se citete o singur valoare de la consol, care se atribuie att lui a
ct i lui b:
let a = read
let b = read // a = b = read

O funcie poate avea orici parametri, de exemplu:


> let add x y = x + y;;
val add : x:int y:int int

n general, notaia pentru tipul funciei este: (tip argument1) (tip argument2) ...
(tip de return).
S considerm acum o funcie asemntoare, dar care primete un singur parametru, o tupl
cu dou elemente:
> let addt (x, y) = x + y ;;
val addt : x:int * y:int int

Dei face acelai lucru, se vede c tipul su este diferit. Diferena este esenial i indic
modul n care F# trateaz funciile cu mai muli parametri. Procesul se numete currying.

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

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

let read() =
System.Console.ReadLine()

5.2. Currying
n F#, orice funcie cu mai muli parametri este descompus ntr-o serie de funcii cu un
singur parametru. De exemplu, funcia add de mai sus poate fi rescris astfel:

Dup cum se vede, n F# funciile pot fi imbricate. Aici, funcia add1 nu poate fi apelat din
exteriorul funciei add i are acces la parametrul x al funciei add. Pentru funcia add1, x poate fi
tratat ca o constant.
Apelul funciei add rescrise astfel este identic cu apelul primei variante: de exemplu add 1 2.
Mai jos sunt prezentate tipurile celor dou funcii:

Funcia add1 este o funcie normal: primete un parametru ntreg i ntoarce un rezultat
ntreg. Funcia add este ns o funcie de ordin superior: primete un parametru ntreg i ntoarce o
funcie, de fapt, ntoarce funcia add1, care adun parametrul su y cu numrul dat x.
Apelul add 1 2 poate fi rescris mai sugestiv (add 1) 2. (add 1) este deci o funcie care adun
parametrul su y cu numrul dat 1. Aceast funcie primete argumentul y = 2 i calculeaz rezultatul final 1 + 2 = 3. Cu alte cuvinte, dac am nota f = (add 1), apelul (add 1) 2 ar fi echivalent cu f 2,
adic f(2). f este o funcie care adun 1 la numrul primit ca argument, iar 1 este de fapt argumentul
funciei add.
5.3. Funcii recursive
n programarea funcional pur nu exist bucle, deoarece acestea presupun actualizarea
unui contor. Valorile fiind imutabile, acest lucru nu este posibil. De aceea, echivalentul funcional al
buclelor este reprezentat de funciile recursive.
n exemplul de mai jos, se prezint o funcie de calcul al factorialului, folosind i instruciunea condiional if-then-else.
let rec factorial x =
if x <= 1 then
1
else
x * factorial (x - 1)

10

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

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

let add x =
let add1 y =
x+y
add1

Atunci cnd se folosesc funcii recursive, parametrii sunt memorai pe stiv, iar dac nivelul
de recursivitate este foarte mare, acesta poate produce o excepie de memorie insuficient. Pentru a
evita acest lucru, se folosete metoda tail recursion. Mai jos este rescris funcia de calcul al
factorialului n acest mod. Rezultatul este calculat n acumulatorul acc i transmis ca parametru n
funcia recursiv.
let factorial n =
let rec fact n acc =
if n <= 1 then
acc
else
fact (n - 1) (acc * n)
fact n 1

Tail recursion este o metod de optimizare foarte util n aplicaiile din viaa real, care
permite compilatorului s transforme, intern, funcia recursiv ntr-o bucl.
Totui, considerm c aceast metod nu ine de filosofia recursivitii, ci este o modalitate
de optimizare practic. De fapt, F# permite i lucrul cu bucle ca n programarea imperativ, dup
cum vom vedea n laboratorul 3.
De multe ori, funciile de ordin superior, pe care le vom studia n laboratorul 2, pot fi
folosite n loc de recursivitate sau bucle i aceast modalitate este mai simpl i mai clar dect
recursivitatea, dar i mai elegant dect folosirea buclelor.

6. Funcii de afiare
Modalitatea de afiare la consol este asemntoare cu aceea din limbajul C. De cele mai
multe ori, se folosesc funciile printf sau printfn (care n plus fa de printf adaug la sfrit o linie

11

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

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

n F#, spaiile libere sunt delimitatori, prin urmare este obligatorie indentarea corect a
programului, ceea ce crete lizibilitatea. Dup cum se poate vedea mai jos, alinierea greit
genereaz o eroare de sintax.

nou). Aceste funcii au ca parametri un ir caractere pentru formatare i elementele care se doresc
afiate. De exemplu:

Dac nu dorim s specificm tipul unui element, putem folosi specificatorul generic %A (any
value), care afieaz orice tip de dat, conform inferenei de tip realizate de compilator. De exemplu, putem afia o tupl astfel:
let t = (1, 2, 3)
printfn "%A" t // (1, 2, 3)

Citirea de la consol se realizeaz cu ajutorul metodelor din clasa System.Console din .NET.
De exemplu, metoda ReadLine citete un ir de caractere, care poate fi convertit n tipul dorit:
open System
let readInt() =
int (Console.ReadLine())
let readFloat() =
float (Console.ReadLine())

Trebuie menionat faptul c dac irul de caractere citit nu reprezint un numr valid, este
aruncat o excepie. ntruct nelegerea mecanismelor de tratare a excepiilor n F# necesit noiuni
suplimentare, nu le vom prezenta aici.
Pentru a converti o mulime de argumente ntr-un ir de caractere, se folosete funcia
sprintf, cu o sintax asemntoare cu printf. Aceast funcie este important deoarece n programarea funcional se dorete eliminarea pe ct posibil a efectelor secundare (engl. side effects), iar
afiarea este poate fi un astfel de efect secundar dac o funcie pe de o parte face anumite prelucrri
i pe de alta afieaz rezultatele. De exemplu, n loc de secvena de mai jos:
let evaluate x =
if x >= 0 then
printfn "%d" x
else
printfn "Negativ"
let caller() =
evaluate -1
evaluate 1

ar fi de preferat urmtoarea alternativ:

12

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

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

printfn "%s" "Hello world"


let pi = System.Math.PI
printfn "%d %f" 1 pi // 1 3.141593, %d pentru numar intreg, %f pentru numar real
printfn "%.4f" pi // 3.1416, numar real cu 4 zecimale

let evaluate x =
if x >= 0 then
sprintf "%d" x
else
sprintf "Negativ"

7. Organizarea unui program


O trstur caracteristic a limbajului F# este c numai funciile i valorile definite anterior
n program pot fi apelate sau accesate. Aceasta contrasteaz cu modul de organizare al unui
program C#, de exemplu, n care metodele dintr-o clas pot fi definite oriunde i apelate de unde
este nevoie. Trstura aceasta este asumat n proiectarea limbajului i are scopul de a face programele mai clare.
Pentru a gestiona un program mai complex, se folosete de obicei organizarea codului pe
module. De exemplu, aplicaiile de la sfritul acestui laborator pot fi incluse ntr-o structur de
tipul urmtor:
module Program1 =
open System
let read() =
printf "Introduceti numarul: "
int (Console.ReadLine())
let mainP1() =
let n = read()
printfn "Numarul este %d" n
// ====================================================================
module Program2 =
let min x y = if (x < y) then x else y
let mainP2() =
printfn "min(%d, %d) = %d" 1 2 (min 1 2)
// ====================================================================
[<EntryPoint>]
let main argv =
Program1.mainP1()
Program2.mainP2()
0

13

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

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

let caller() =
printfn "%s" (evaluate -1)
printfn "%s" (evaluate 1)

8. Aplicaii
8.1. S se determine cel mai mare divizor comun pentru dou numere naturale. Cele dou
numere vor fi citite de la tastatur. Funcia gcd trebuie s fie implementat recursiv.
Indicaie: Pseudocodul algoritmului lui Euclid, n forma imperativ, este prezentat n
continuare.
procedure gcd(a, b)
while b 0
tb
b a modulo b
at
return a

Apelul poate fi de forma de mai jos, unde observm atribuirea a dou valori n aceeai
operaie let, ca tupl.
let mainP1() =
let a, b = read(), read()
printfn "Cmmdc (%A, %A) = %A" a b (gcd a b)

8.2. S se realizeze un joc simplu de ghicire a unui numr de ctre utilizator. Programul va
alege n mod aleatoriu un numr natural, dup care utilizatorul va introduce succesiv diferite valori
pn la ghicirea numrului.

14

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

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

Funcia main a programului principal, marcat cu atributul [<EntryPoint>], primete un


vector de argumente de la linia de comand. Dac valorile acestora nu se intereseaz, putem folosi
simbolul _ n loc de numele vectorului de argumente. Funcia main a programului trebuie s
returneze un ntreg, codul de ieire al procesului transmis sistemului de operare. De aceea exist un
0 pe ultima linie.
Pentru a pstra compatibilitatea cu platforma .NET, limbajul F# permite i definirea de
spaii de nume sau namespace-uri. Spre deosebire de module, namespace-urile nu pot fi imbricate i
nu pot conine valori, ci numai declaraii de tipuri.
Diferena principal dintre aceste dou modaliti de organizare a codului este c modulele
sunt optimizate pentru realizarea rapid a unor prototipuri, cum sunt programele din aceste laboratoare, pe cnd namespace-urile sunt destinate proiectelor mari, care se bazeaz i pe abordarea
orientat pe obiecte.

Introduceti numarul: 50
Numarul corect este mai mare
Introduceti numarul: 75
Numarul corect este mai mic
Introduceti numarul: 63
Numarul corect este mai mare
Introduceti numarul: 69
Numarul corect este mai mare
Introduceti numarul: 72
Numarul corect este mai mic
Introduceti numarul: 71
Numarul corect este mai mic
Introduceti numarul: 70
Corect!

Indicaii: Se d funcia main, n care se genereaz numrul aleatoriu folosind clasa .NET
Random:
let mainP2() =
guess ((new Random()).Next(100))

Realizai funcia recursiv guess, n care utilizatorul introduce un numr; dac acesta este
soluia, se returneaz 0, iar dac nu, se afieaz mesajul corespunztor i se apeleaz din nou funcia
guess.

8.3. S se introduc de la tastatur trei numere ntregi reprezentnd lungimile laturilor unui
triunghi i s se verifice dac acesta este un triunghi valid: laturile sunt pozitive i respect inegalitatea n triunghi, adic orice latur este mai mic dect suma celorlalte dou.
Exemple:
Introduceti latura 1: 2
Introduceti latura 2: 3
Introduceti latura 3: 4
Reprezinta un triunghi valid

Introduceti latura 1: 1
Introduceti latura 2: -1
Introduceti latura 3: 1
Nu reprezinta un triunghi valid
Introduceti latura 1: 1
Introduceti latura 2: 2
Introduceti latura 3: 3
Nu reprezinta un triunghi valid

15

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

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

Exemplu:

Indicaie: se pot folosi funciile de mai jos:

let mainP3() =
let a, b, c = read 1, read 2, read 3
if validate a b c then
printfn "Reprezinta un triunghi valid"
else
printfn "Nu reprezinta un triunghi valid"

Se pot defini dou funcii separate: una care s testeze inegalitatea n triunghi i una care s
testeze dac un numr este strict pozitiv.

8.4. S se realizeze un program care rezolv o ecuaie de gradul 2. Cei trei coeficieni,
numere reale, vor fi citii de la tastatur.
Exemple:
Introduceti coeficientul a: 1
Introduceti coeficientul b: 3
Introduceti coeficientul c: 2
Solutiile sunt: -1.000, -2.000
Introduceti coeficientul a: 1
Introduceti coeficientul b: -2
Introduceti coeficientul c: 1
Solutia este: 1.000
Introduceti coeficientul a: 1
Introduceti coeficientul b: 1
Introduceti coeficientul c: 1
Solutiile sunt: -0.500 +- i*0.866
Introduceti coeficientul a: 0
Introduceti coeficientul b: 0
Introduceti coeficientul c: 1
Nu este o ecuatie de gradul 2 valida

16

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

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

let read x =
printf "Introduceti latura %d: " x
int (Console.ReadLine())

Indicaii. Programul poate conine o funcie care primete o tupl cu cei trei coeficieni
numere reale (a, b, c) i returneaz o tupl de forma (x, y, t), cu urmtoarele convenii:
dac t este 2, atunci soluiile reale sunt x i y ;
dac t este 1, atunci ecuaia are o singur soluie x ;
dac t este 2, atunci ecuaia are dou soluii complexe x i y ;
dac t este 0, atunci ecuaia nu este valid.

Not: Problema se poate rezolva i mai elegant folosind alte tehnici pe care le vom prezenta
n laboratoarele viitoare.
Se poate folosi urmtoarea funcie main:
let mainP4() =
let a, b, c = read 'a', read 'b', read 'c'
let x, y, t = solve a b c
if t = 2 then
printfn "Solutiile sunt: %.3f, %.3f" x y
elif t = 1 then
printfn "Solutia este: %.3f" x
elif t = -2 then
printfn "Solutiile sunt: %.3f +- i*%.3f" x y
else
printfn "Nu este o ecuatie de gradul 2 valida"

Pseudocodul pentru rezolvarea ecuaiei, n care delta = b2 4 a c, este prezentat mai jos
i trebuie tradus n F#.
if (delta > 0)
{
sol1 = (b + sqrt(delta)) / (2 * a)
sol2 = (b sqrt(delta)) / (2 * a)
}
else if (delta == 0)
{
sol = b / (2 * a)
}
else
{
rsol = b / (2 * a)
isol = sqrt(delta) / (2 * a)
}

17

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

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

8.5. S se afieze tabela de adevr a funciei logice A and (A or (not B)). Se vor folosi mai
nti direct operatorii logici din F#, iar apoi funciile corespunztoare definite n program:
let and' a b = a && b
let or' a b = a || b
let not' a = not a

A
false
false
true
true

B
false
true
false
true

F1
false
false
true
true

A
false
false
true
true

B
false
true
false
true

F2
false
false
true
true

Indicaie: Se va defini funcia f1 cu operatorii logici F# i funcia f2 cu operatorii definii de


programator. Afiarea propriu-zis a tabelei de adevr poate fi realizat cu ajutorul urmtoarelor
funcii:
let printLine f a b = printfn "%A\t%A\t%A" a b (f a b)
let printTable f = // f este o funcie; printTable va fi apelat cu f1 sau f2
printLine f false false
printLine f false true
printLine f true false
printLine f true true

18

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

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

Exemplu: