Lógica de Programação - Subprogramas

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar

Objetivos da Aula

Após esta aula o aluno deverá ser capaz de:

  • Identificar padrões/blocos de código que se repetem na solução de determinados problemas;
  • Encapsular blocos de código na forma de subprogramas/subprocessos, representado-os com fluxogramas e pseudocódigo;
  • Identificar os dados de entrada de subprogramas, representado-os na forma de parâmetros do subprograma;
  • Identificar valores de retorno de subprogramas;
  • Aplicar subprogramas/subprocessos na solução algorítmica de problemas diversos.

Quebrando um problema em subproblemas: SUBPROGRAMAS

CASO DE ESTUDO 1: Elevando um número a uma potência inteira

Em várias algoritmos surge a necessidade de "elevar" um número a uma potência inteira. Muitos sistemas computacionais não conseguem implementar esta operação através uma operação simples da CPU. O que deve ser feito é multiplicar repetidas vezes o número de forma a se obter a operação de potenciação. Neste caso, um dado problemas simples, cuja solução requer o uso de potenciação em várias partes do algoritmo, deve replicar o código, abrindo espaço para possíveis erros, além de aumentar desnecessariamente o tamanho do algoritmo.

EXEMPLO: Implementar um algoritmo na forma de pseudocódigo de forma a resolver a seguinte equação:

DADOS DE ENTRADA: valor de x e de y

DADOS DE SAÍDA: valor computado de z

Solução 1 - inserindo código de repetição para resolver a potenciação

OBS: Vamos considerar uma solução para potências maiores ou igual a 0.

ALGORITMO  funcao_xyz()
VARIAVEIS
  x,y,z,prod1, prod2: real;
  i: inteiro;
INICIO
  LER x;
  LER y;

  prod1 ← 1;
  i ← 0;
  ENQUANTO i < 7  FAÇA
      prod1 ← prod1 * x;
      i ← i+1;
  FIM_ENQUANTO

  prod2 ← 1;
  i ← 0;
  ENQUANTO i < 3  FAÇA
      prod2 ← prod2 * y;
      i ← i+1;
  FIM_ENQUANTO  

  z ← prod1 + prod2;
  mostrar z;
FIM

Solução 2 - Usando subprograma para resolver a potenciação

Vamos identificar agora o padrão que se repete para solucionar o "subproblema" potenciação:

DestaqueCodigos.png

Podemos observar que nos dois blocos aparecem as variáveis x e y que possuem os valores que desejamos elevar a uma potência. A potência propriamente dita aparece na forma de uma contante (7 e 3). Além disso, as variáveis auxiliares (prod1 e prod2) são usadas para armazenar o produto acumulado.

Tendo identificado o padrão que se repete vamos encapsular o mesmo em um SUBPROGRAMA:

ALGORITMO  pow(numero: real, potencia: inteiro)
VARIAVEIS
  i: inteiro;
  prod: real;
INICIO
  prod ← 1;
  i ← 0;
  ENQUANTO i < potencia  FAÇA
      prod ← prod * numero;
      i ← i+1;
  FIM_ENQUANTO  
RETORNA prod

Finalmente, vamos USAR (CHAMAR) este subprograma a partir do algoritmo principal:

ALGORITMO  funcao_xyz()
VARIAVEIS
  x,y,z: real;
INICIO
  LER x;
  LER y;

  z ← POW(x,7) + POW(y,3);
  mostrar z;
FIM

Observe a redução de complexidade do código original em relação a versão usando o subprograma POW(). Pode-se interpretar a execução da linha de código z = POW(x,7) + POW(y,3); como:

  1. Chame o subprograma POW e repasse para ele os valores de x e o valor 7. O fluxo de execução é desviado para o subprograma. O "parâmetro" numero recebe o valor de x e o parametro potencia recebe o valor 7.
  2. O subprograma se executa e retorna o valor computado. Este valor fica armazenado em uma área temporária.
  3. O fluxo de execução continua no programa principal e novamente é invocado (chamado) o suprograma POW(), agora com novos parâmetros.
  4. O subprograma se executa e retorna o valor.
  5. Estes valor será agora somado com o valor temporário (resultante da primeira chamada de POW()). O resultado da soma e colocado (atribuído) a z.


FluxoChamadaSubprogramaV2.png

EXERCÍCIO

Modificar o subprograma para que as potências possam ser negativas (ver link).

CASO DE ESTUDO 2: Cálculo de fatorial

Seja elaborar um fluxograma correspondente a uma subprograma que deve calcular o fatorial de um número inteiro passado como parâmetro. O subprograma deve retornar o valor calculado. Mostrar um exemplo de algoritmo que usa o subprograma de cálculo de fatorial, chamando-o duas vezes.

A função fatorial é definida por:

FluxogramaFatorial.jpg

Neste fluxograma, o subprograma denominado CalcFatorial recebe um valor no parâmetro N (implicitamente inteiro) e retorna o valor calculado do fatorial. O parâmetro é passado por cópia neste caso.

O fluxograma principal invoca duas vezes o subrograma. O retorno é armazenado nas variáveis NUM1 e NUm3.

Quando um subprograma retorna um valor, ele é chamado de função. Para manter coerência com o C chamaremos
qualquer subrprograma de função (independente de retornar valor).

CASO DE ESTUDO 3: Controle de Acesso - Um exemplo QUASE sem matemática

Vamos retomar inicialmente o problema de controle de acesso:

PROBLEMA: Aprimorar o exemplo do controle de acesso para que, no caso de 3 tentativas de acesso seguidas, com senha errada, o sistema seja bloqueado. O administrador desbloqueia redefinindo a senha e zerando o contador de acesso.

  • DADOS DE ENTRADA: senha inserida pelo usuário na variável SENHA /* variável que armazena a senha entrada pelo usuário ou administrador */
  • DADOS DE SAÍDA: mensagem de abertura da porta / usuário bloqueado*/
  • VARIÁVEIS INTERMEDIÁRIAS: CONT_ACESSO /* valor inicial zero - incrementada a cada senha inválida */

FluxogramaControleAcessoComContador.jpg

Note que a variável CONT_ACESSO é iniciada com zero e incrementada a cada erro no fornecimento da senha. A atribuição CONT_ACESSO = CONT_+ACESSO + 1 deve ser interpretada da forma: acesse o valor de CONT_ACESSO e some 1 a este valor. Coloque o resultado novamente em CONT_ACESSO (o conteúdo anterior é sobrescrito!)

Esta solução não é perfeita e poderia ser aprimorada. Se o administrador erra, ele pode vir a bloquear o usuário.


Neste procedimento pode ser observado que:

  • começa a aparecer uma certa complexidade no fluxograma;
  • além disto, não ficou uma abordagem muito interessante, pois se for o administrador que erra a senha, ele pode bloquear o usuário. Mudando a ordem de teste pode-se contornar este problema.
  • não existe forma de desbloquear a senha (zerar o contador), a não ser mudando a senha.

Em síntese, existem subproblemas adicionais a serem resolvidos que adicionam complexidade ao sistema. Como podemos resolvê-los sem deixar um fluxograma complexo?

Usaremos a caixa de processos pré-definidos que usaremos para invocar funções que resolvem determinado subproblema.

Inicialmente, vamos construir três subprogramas:

  • Iniciar_Sistema(): inicia variáveis do sistema, colocando o sistema em um estado inicial conhecido;
  • Tratar_Admin(): é o código que trata a funcionalidade associada ao administrador;
  • Tratar_User(): é a funcionalidade que trata o usuário (procedimento de abrir porta, bloquear etc).

FluxogramaControleAcessoComSubprograma.jpg

OBS: Note que foi usada uma variável auxiliar AUX que permite ajustar o valor
de número de acessos a ser mostrado no display. Note também que na caixa DISPLAY foi usado
uma string a ser impressa e a variável AUX cujo conteúdo deve ser impresso. Ambos separados
por vírgula.
  • ANINHAMENTO DE DECISÔES

A função Tratar_User() se utiliza de aninhamento de decisões. Em um primeiro momento é testado se a senha confere com a senha do usuário. SE ela conferir ENTÂO é testado se o contador de bloqueios está dentro do aceitável.

PARÂMETROS E RETORNO DE VALORES EM SUBPROGRAMAS

Para tornar ainda mais interessante o uso de subprogramas, vamos ver o conceito de passagem de parâmetros e retorno de valores com mais detalhes.

Quando chamamos (invocamos) um subprograma podemos passar valores (dados de entrada) para este subprograma.
Estes valores são passados através de variáveis especiais que chamamos de parâmetros.
Parâmetros podem ser passados por valor e por referência. Por valor é quando os dados a serem passados na invocação
do subprograma são copiados para uma variável do subprograma.
Os parâmetros são passados por referência quando o que é passado para o subprograma é simplesmente o endereço da variável
repassada na invocação do subprograma.

Não existe uma forma explícita de definir os parâmetros em um fluxograma. Deve-se realizar um comentário antes ou ao lado do mesmo especificando o tipo e como será tratado o parâmetro.

Um exemplo de passagem de parâmetro por referência

A passagem de parâmetro por referência permite "repassar valores" nos dois sentidos. Funciona como se fosse entrada/saída de dados. Na prática, não existe cópia de valores, o que existe é uma espécie de compartilhamento de variáveis entre o subprograma e o programa(algoritmo) que chama o subprograma. Isto pode ser interessante em várias situações. Por exemplo, existem linguagens de programação que não permite retornar mais do que um valor. Neste caso, valores podem ser repassados (retornados) pelos parâmetros.

Vamos "testar" esta ideia implementando um subprograma calcula o ponto médio de um segmento de reta definido por dois pontos: e . Sabe-se que um ponto possui na realidade dois valores reais a serem retornados, o que complica o retorno pelo comando RETORNAR. Uma possível implementação do subprograma seria:

ALGORITMO  ponto_medio(pxa: real, pya:real, pxb: real, pyb:real, VAR pxm: real, pym:real)
INICIO
   pxm ← (pxa + pxb)/2;
   pym ← (pya + pyb)/2;
RETORNA

Observe que os parâmetros xm e ym foram declarados com VAR, indicando que serão passados como referência.

O uso deste subprograma poderia ser:

ALGORITMO teste_media
VARIAVEIS: 
   xa,ya,xb,yb,xm,ym: reais
INICIO
   LER xa;
   LER ya;
   LER xb;
   LER yb;
   ponto_medio(xa,ya,xb,yb,xm,ym);
   MOSTRAR xm;
   MOSTRAR ym;
RETORNA

Deve ser observado que houve o algoritmo que chamou o subprograma, recebeu nas variáveis xm e ym os valores calculados pelo subprograma. Isso não aconteceria se não fosse passagem por referência.