Programação Imperativa

Aula 11

Sumário

Esta aula introduz o conceito de função em linguagens de programação e dá exemplos utilizando a linguagem C.


Funções

Em exemplos anteriores utilizamos a função sqrt que está definida em math.h. A função sqrt é uma função que já está pré-definida. Quando fazemos programas também podemos definir as nossas próprias funções. Por exemplo, podemos fazer uma função abs para calcular o valor absoluto de um número. Depois de definida, podemos usar essa função tal e qual como usamos a função sqrt em exemplos anteriores. Exemplo:

    #include <stdio.h>

    main()
    {
      float a;

      printf("Introduz um número: ");
      scanf("%f", &a );
      printf("O valor absoluto de %f é %f\n", a, abs(a) );
    }   

O programa não funciona tal como está porque a linguagem C não conhece a função abs. No entanto, podemos definir a função abs do seguinte modo:

    float abs( float x )
    {
      if( x < 0 ) 
        return -x;
      else
        return x;
    }
Concentrem-se na primeira linha da definição da função abs:
    float abs( float x )

O float que aparece antes da palavra abs e o float que aparece antes de x correspondem na Matemática ao contra-domínio e ao domínio da função (ver figura).

A função em si é uma espécie de caixa preta que recebe um valor real x e manda cá para fora um outro valor real que é o valor absoluto de x. Dentro da função propriamente dita, podemos utilizar quaisquer instruções da linguagem C. A instrução return serve para "mandar cá para fora" o valor da função e terminar a sua execução.

O programa completo ficaria assim:

    #include <stdio.h>

    /* função para calcular o valor absoluto de um número */
    float abs( float x )
    {
      if( x < 0 ) 
        return -x;
      else
        return x;
    }

    main()
    {
      float a;

      printf("Introduz um número: ");
      scanf("%f", &a );
      printf("O valor absoluto de %f é %f\n", a, abs(a) );
    }   

O x que aparece na definição da função abs chama-se parâmetro formal. A variável a que aparece quando a função é chamada chama-se parâmetro actual. Na altura em que a função é chamada o parâmetro formal recebe uma cópia do valor do parâmetro actual (no exemplo, x recebe uma cópia do valor de a).

Outro exemplo

Imaginem que queriam calcular o valor da seguinte fórmula:

    f = x! / a! (x-a)!

Numa das aulas passadas fizemos um programa que calculava o factorial de um número. O programa tinha um ciclo que varria os números de 1 a n e ia acumulando o produto dos respectivos números.

Para calcular a fórmula f = x! / a! (x-a)! , temos de calcular o factorial de 3 números. Podemos fazer 3 ciclos, um para cada factorial, mas isso é uma grande chatice porque os 3 ciclos fazem basicamente a mesma coisa.

Em vez de fazer isso, podemos definir a função factorial e depois é só chamar a função 3 vezes. Deste modo o programa fica muito mais simples e muito mais legível:

    #include <stdio.h>

    /* função para calcular o factorial de um número */
    int factorial( int n )
    {
      int i,p;

      p = 1;
      for( i=2; i<=n; i++ )
        p = p * i;
      return p;
    }

    main()
    {
      int f, x, a;

      ...
      f = factorial(x) / ( factorial(a) * factorial(x-a) );
      ...
    }

Reparem na semelhança entre

    f = factorial(x) / ( factorial(a) * factorial(x-a) )

e a notação matemática usual

    f = x! / a! (x-a)!

As funções são uma espécie de mini-programas em que podemos declarar variáveis e usar quaisquer instruções da linguagem C. O segredo da programação é conseguir dividir um problema complexo em bocados mais pequenos. Cada bocado corresponde a um sub-problema que é mais simples de resolver. Por sua vez, cada sub-problema pode ser novamente dividido em bocados ainda mais pequenos, e assim sucessivamente.

A utilização de funções tem as seguintes vantagens:

Noção de variável local

Podemos declarar variáveis dentro de uma função. Considerem o programa que se segue.

    #include <stdio.h>

    /* função para calcular o factorial de um número */
    int factorial( int n )
    {
      int i,p;

      p = 1;
      for( i=2; i<=n; i++ )
        p = p * i;
      return p;
    }

    main()
    {
      int i,f;

      i = 3;
      f = factorial(6);
      printf("%d %d\n", i, f );
    }

Ao ser executado o programa vai escrever o seguinte no ecrã:

    3 720

O que quero mostrar com este exemplo é que a variável i que está definida em main não tem nada a ver com a variável i que está definida na função factorial. As variáveis definidas dentro de uma função só existem enquanto essa função estiver a ser executada. Outra coisa que é de salientar é que se o main tivesse a instrução printf("%d", p );, o compilador iria dar um erro, visto que o main não conhece p (a variável p que está definida na função factorial é invisível para o exterior da função).

É possível declarar variáveis que têm validade em todo o programa. Essas variáveis chamam-se variáveis globais, mas apenas iremos ver isso mais tarde.

Mais um exemplo

Função para calcular o máximo de 2 números:

    float max( float x, float y )
    {
      if( x > y ) 
        return x;
      else
        return y;
    }

Função para calcular o máximo de 3 números:

    float max3( float x, float y, float z )
    {
      return max( x, max(y,z) );
    }

Programa que utiliza as funções max e max3:

    #include <stdio.h>

    /* calcula o máximo de 2 números */
    float max( float x, float y )
    {
      if( x > y ) 
        return x;
      else
        return y;
    }

    /* calcula o máximo de 3 números */
    float max3( float x, float y, float z )
    {
      return max( x, max(y,z) );
    }

    main()
    {
      float a,b,c;

      a = -3;
      b = 4;
      c = 2;
      printf("O máximo de %f, %f, e %f é %f\n", a, b, c, max3(a,b,c) );
    }   

A função max3 está definida à custa da função max. Na altura em que o programa executa max3(a,b,c), os parâmetros formais x,y,z recebem cópias dos valores de a,b,c respectivamente. Se no main tivéssemos declarado variáveis x,y,z em vez de a,b,c, não iria haver problema nenhum e o programa funcionava exactamente do mesmo modo, isto é, os parâmetros formais x,y,z iriam recebem cópias dos valores das variáveis x,y,z do main.

Funções que não retornam nada

O conceito de função na linguagem C é mais geral que o conceito de função em Matemática. Em C, é possível ter funções que não retornam nenhum valor (noutras linguagens de programação, tais como o Pascal e o Fortran, este tipo de função costuma ser chamado de procedimento ou rotina). O próximo exemplo ilustra uma função chamada atelogo que não retorna nenhum valor.

    #include <stdio.h>

    /* escreve até logo n vezes */
    atelogo( int n )
    {
      int i;

      for( i=1; i<=n; i++ )
        printf("Até logo\n");
    }

    main()
    {
      int n;

      printf("Diz um número: ");
      scanf("%d", &n)
      atelogo( n );
    }

O programa acima é um programa "imbecil" mas serve para ilustrar o conceito. Reparem que o main também é uma função. Estritamente falando, deveríamos colocar a palavra void na definição de uma função que não retorna nada.

    #include <stdio.h>

    /* escreve até logo n vezes */
    void atelogo( int n )
    {
      int i;

      for( i=1; i<=n; i++ )
        printf("Até logo\n");
    }

    void main()
    {
      int n;

      printf("Diz um número: ");
      scanf("%d", &n)
      atelogo( n );
    }

Em geral, os programas em C são constituídos por um conjunto de funções que comunicam entre si através dos parâmetros. Cada função costuma ter meia dúzia de linhas e faz uma tarefa específica.