11

Click here to load reader

c8 Pointeri La Functi Si Functii Recursive

Embed Size (px)

DESCRIPTION

c, programare procedurala

Citation preview

  • 1

    Programare Procedural Cursul nr.8 POINTERI LA FUNCII Dup compilarea i linkeditarea unui program fiecrei funcii componente i se asociaz o adresa de memorie care este de fapt adresa de nceput a funciei. Un pointer ctre funcie este un pointer care poate conine aceast adres. S presupunem c o funie f are prototipul : tip f ( lista_param_formali ) ; Forma general a unui pointer pf la funcia f este de forma : tip ( *pf ) ( lista param formali ) ; Se observ c funcia i pointerul asociat funciei trebuie s aibe acelai tip, iar lista parametrilor formali trebuie s fie identic. De asemenea, pointerul precedat de stelu trebuie sa fie inclus ntre paranteze rotunde datorit regulilor de preceden ale limbajului, deoarece in C numele funciei este echivalent cu adresa ei de nceput. Un pointer ctre o funcie poate primi adresa de nceput a funciei respective printr-o atribuire de forma : pf = f ; Exemplu :

    Un pointer p la funcia f void f ( float, int ) ; trebuie declarat in felul urmator : void ( *pf ) ( float, int ) ; Dup o atribuire de forma pf = f ; apelul funciei prin intermediul pointerului pf se face prin ( *pf ) ( lista_parametri_actuali) ;

  • 2

    dac funcia este de tip void, sau nume_variabila = ( *pf ) ( lista param_actuali ) ; dac ntoarce un rezultat . Exemplu :

    # include < stdio.h > # include < conio.h > int adunare (int x, int y) ; int scadere (int x, int y) ; int inmultire (int x ,int y) ; int impartire (int x, int y) ; void main ( ) { int a,b,i ; clrscr ( ) ; printf (\ n intoduceti datele : ) ; printf (\n a=);scanf(% d,& a); printf (\n b=);scanf(% d,& b); int( *t [ ] )(int, int)={adunare, scadere, inmultire ,impartire} ; char *operatie[]={adunare ,scadere ,inmultire ,impartire }; randormize ( ) ; i=randorm (4); printf(\n s-a selectat functia % s, operatie [i] ); printf(\n rezultatul % s este % d ,operatie [i] ,( * t [i] )(a,b)); getche( ); } int adunare(int x,int y) { return x+y ; } int scadere(int x,int y) { return x-y ; } int inmultire( int x,int y) { return x*y ; } int impartire(int x,int y)

  • 3

    { return x/y ; } Exemplu :

    Transmiterea ca parametri a pointerilor la funcii : suma(a,b) dac x > 0 Fie funcia f(x)= dif(a,b) dac x 0 Se poate folosi o singur funcie f( )pentru definirea lui f(x), care va avea ca argumente pe x i un pointer la funcie notat cu p. Acest pointer poate primi att adresa funciei suma( ) ct i a funciei dif( ). # include < stadio.h > # include < conio.h > int sum ( int a, int b); int dif (int a ,int b); void f (float x, int a, int b , int( *p )(int a, int b)); void main( ) { float x; int a,b; clrscr ( ); printf(\n x=); scanf( % f ,&x ); printf(\n a =); scanf(% d ,&a ); printf(\n b =); scanf(% d ,&b); if(x>0) f(x,a,b,sum) ; else f(x,a,b,dif); getch ( ); } int sum(int a,int b) { return(a+b); } int dif(int a,int b) { return (a-b); } void f(float x,int a,int b,int(*p)(int a,int b))

  • 4

    { printf(\n f ( % f ) = % i ,x, ( *p )(a, b)); } FUNCII RECURSIVE Definiie. O funcie este definit recursiv dac pornind de la anumite valori ale ei se pot calcula alte valori prin autoapelarea funciei. Valorile unei funcii recursive se calculeaz din aproape n aproape, pe baza valorilor cunoscute ale funciei pentru anumite argumente iniiale. Pentru a calcula noile valori ale unei funcii recursive, trebuie memorate deja valorile calculate, care sunt strict necesare. Acest fapt face ca implementarea n program a calculului unor funcii recursive s necesite un consum mare de memorie, rezultnd timpi mari de execuie. Recursivitatea poate fi transformat n iteraie. In general, forma iterativ a unei funcii este mai eficient dect cea recursiv n ceea ce privete timpul de execuie i memoria consumat. n alegerea cii (interativ sau recursiv) de rezolvare a unei probleme, trebuie considerai o serie de factori: uurina programrii, testrii i ntreinerii programului, eficiena, complexitatea etc. Dac o problem are o complexitate redus este preferat varianta interactiv. Forma recursiv este preferat acolo unde transformarea recursivitii n iteraie cere un efort de programare deosebit, algoritmul pierzndu-i claritatea, testarea i ntrtinerea devenind astfel foarte dificile. Calculul valorilor funciilor respective se poate face direct sau indirect. Funcia f( ) se numete direct recursiv dac n corpul ei apar apeluri la ea nsi (autoapeluri). Funciile f( ) i g( ) se numesc indirect recursive daca f ( ) conine apeluri la g( ), iar g( ) conine apeluri la f( ). Exemple :

    1. Funcia n! pe care o notam cu fact, se definete recursiv astfel : 1 dac n=0 sau n=1 fact:NN fact(n) = n . fact(n-1) dac n>1 2. Funcia lui Fibonacci: 1 daca n=0 sau n=1 fib:NN fib(n)= fib(n-1)+fib(n-2) dac n>=2

  • 5

    3. Funcia Manna Pnuelli: x-1 dac x>12 f:NN f( x ) = f(f(x+2)) dac 0xk 5. Funcia lui Akermann: n+1 dac m=0 ac:NxNN ac(m,n) = ac(m-1,1) dac n=0 ac(m-1,ac(m,n-1)) n rest Clasificarea funciilor recursive Funciile recursive se pot clasifica dupa modul n care descriu procesul de calcul al valorilor funciilor pe care le definesc. 1. Recursia liniar definete recursiv funcia f(x) n termeni de alte funcii: g(x),h(x),i(x)si (u,v) astfel: if p(x) then g(x) f(x)= else (f(h(x)),i(x)) endif O form particular este iteraia simpl : if p(x) then g(x) f(x)= else f(h(x)) endif 2. Recursia neliniar de tip cascad este de forma :

  • 6

    if p(x) then g(x) f(x)= else (f(h(x)),f(i(x))) endif 3. Recursia liniar de tip mpachetat : if p(x) then g(x) f(x)= else f(..f(f(..))) endif 4.Definii recursive pentru sisteme de funcii: f(x)=[definiie recursiv n care figureaz f(x) i g(x)] g(x)=[definiie recursiv n care figureaz f(x) i g(x)] Exemple:

    1. Funcia factorial este de tip iteraie simpl : if n=0 v 1 then 1 fact(n)= else n . fact(n-1) endif 2. Funcia lui Fibonacci este recursie neliniar de tip cascad : if(n=0) v (n=1)then 1 fib(n)= else fib(n-1)+fib(n-2) endif 3. Funcia Manna Pnuelli este recursie liniar de tip mpachetat : if x > 12 then x - 1 f(x)= else f (f ( x+2 )) endif 4. Funcia lui Ackermann corespunde formei generale a tipului mpachetat : if m=0 then n+1 else if n=0 then ac( n-1, 1) ac(m,n)= else ac (m-1, ac( m, n-1)) endif endif 5. Funcia care calculeaz c.m.m.d.c a doua numere :

  • 7

    cmmdc : NxNN este de tipul sistemelor de functii definite recursiv : if b=0 then a cmmdc (a, b)= else cmmdc (b, mod ( a, b )) endif if a= 1 # include < stdio.h > # include < conio.h > int fact ( int ) ; void main( ) { int n ; clrscr ( ) ; printf ( \ n n = ) ; scanf ( % d , & n ) ; printf ( \ n % d ! = % d , n, fact ( n )) ; getch ( ) ; } int fact(int a) { if ( a > = 1) return(a * fact ( a - 1) ; else return 1; } sau folosind operatorul conditional int fact ( int a ) { return ( a >= 1 ) ? a* fact ( a 1 ) : 1 ;

  • 8

    } Efectul unui apel de forma fact(5) const n declanarea unui lan de apeluri ale funciei factorial pentru 4,3,2,1,0 fact(5) 5 fact(4)

    2 fact(1)

    1 fact(0)

    1 Apelul lui fact ( 5 ) declaneaz un lan de apeluri ale lui f pentru 4, 3, 2, 1, dup care urmeaz revenirea din apeluri i evaluarea lui fact pentru 1,2,3,4,5. Starea stivei in timpul execuiei succesive a autoapelrii :

    1 2 2

    3 3 3

    4 4 4 4

    Stiva vida 5 5 5 5 5 apel fact(5) apel fact(4) apel fact(3) apel fact(2) fact( 1) Pentru fiecare din aceste apeluri, n stiv se vor depune parametrii actuali: 5,4,3,2,1. Strile stivei dup ieirea din autoapel sunt urmatoarele: 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 Stiva vida

    fact(1)=1 fact(2)=1.2 fact(3)=1.2.3 fact(4)=1.2.3.4 fact(5)=1.2.3.4.5

  • 9

    Rezolvarea fiecrui autoapel nseamn deplasarea pe un nivel inferior. 2. Funcia lui Fibonaci : fib : N N,

    1 pentru n=0 sau n=1 fib(n)= fib(n-1)+fib(n-2) pentru n > = 2 fib(4) fib(3) fib(2) fib(2) fib(1) fib(1) fib(0) fib(1) fib(0) 1 1 1 1 1

    int fib(int n) {

    if ( n == 0 || n == 1) return 1 ; else return fib ( n-1 ) + fib( n- 2 ) ;

    } 3. Funcia lui Ackermann : ac : N x N N n+1 dac m=0 ac ( m , n ) = ac ( m-1 , 1 ) dac n=0 ac ( m-1,ac ( m,n-1)) dac m > 0 , n >0

  • 10

    int ac ( int m , int n ) { if ( m == 0) return n+1 ; else if ( n == 0) return ac ( m-1, 1 ) ; else return ( ac ( m-1 , ac ( m , n-1 ))) ; } m = 2 , n = 1 ac(2,1) ac(1,ac(2,0)) ac(1,3)

    ac(0,ac(1,2)) ac(1,1) ac(0,4) ac(0,ac(1,1)) ac(0,ac(1,0)) 5 ac(0,3) ac(0,ac(1,0)) ac(0,2) ac(0,1) 4 ac(0,2) ac(0,1) 3 2

    3 2

  • 11

    # include < stdio.h > # include < conio.h > int a ( int m, int n ) ; void main ( ) { int m, n ; clrscr ( ) ; printf ( \ n m = ) ; scanf ( % d , & m ) ; printf ( \ n n = ) ; scanf ( % d , & n ) ; printf ( \ n a ( % d, % d ) = % d , m, n, a ( m, n )) ; getche ( ) ; } int a ( int m, int n ) { if ( m == 0 ) return n + 1 ; else if ( n == 0 ) return a ( m 1, 1 ); else return a ( m 1, a ( m, n 1 )); }