Sub-rotinas: procedimentos e funções - 1


Uma sub-rotina é uma parte separada do programa que tem um nome e resolve sua própria tarefa separada. A sub-rotina está localizada no início do programa principal e pode ser lançada (chamada) a partir do programa principal especificando o nome

A utilização de sub-rotinas permite evitar a duplicação de código, caso seja necessário escrever o mesmo código em locais diferentes do programa. 
As bibliotecas que são importadas para um programa (por exemplo, a biblioteca math math) consistem em sub-rotinas que já foram compiladas por alguém. Os programadores não precisam pensar em quais algoritmos implementam, mas simplesmente aplicá-los, pensando apenas no que exatamente estão fazendo. Esta é uma grande economia de tempo. Não há necessidade de escrever um algoritmo que já foi escrito por outra pessoa.

Cada rotina deve fazer apenas uma coisa:  apenas calcule algo, ou forneça alguns dados, ou faça outra coisa. 

Existem dois tipos de sub-rotinas - procedimentos e funções

As sub-rotinas realizam algumas ações, como imprimir o resultado na tela de uma determinada forma (um exemplo simples, a instrução writeln() é uma sub-rotina padrão que imprime para informações da tela)

As sub-rotinas de função retornam um resultado (número, cadeia de caracteres, etc.) que podemos usar no programa principal.

Vamos tentar escrever um procedimento simples:
Suponha que precisamos exibir a string "Error" na tela toda vez que um erro pode ocorrer no código devido à falha do usuário (por exemplo, quando ele insere dados incorretos)
Isso pode ser feito escrevendo a declaração writeln('Erro'); E agora imagine que tal linha precise ser inserida em vários lugares do programa. Claro, você pode simplesmente escrevê-lo em todos os lugares. Mas esta solução tem duas desvantagens.
1) esta string será armazenada na memória muitas vezes
2) se quisermos alterar a saída em caso de erro, teremos que alterar esta linha ao longo do programa, o que é bastante inconveniente

Para tais casos, são necessários procedimentos.
Um programa com um procedimento pode se parecer com isto: ... usando namespace std; procedimento printError(); // descrição do procedimento começar writeln('Erro'); // corpo do procedimento - comandos que o procedimento irá executar fim; // programa principal começar; ... impressorarr(); // inicia o procedimento para execução. Apenas especificamos o nome do procedimento que queremos executar. ... impressorarr(); ... fim. O procedimento começa com a palavra procedimento. Após o nome do procedimento, escrevem-se parênteses, que indicam as variáveis ​​e seus tipos dos quais depende a execução do procedimento. Por exemplo:

var a, b, resposta: inteiro;
procedimento Soma(a, b: inteiro);
começar
    resposta := a + b;
fim;

Todas as instruções executadas em um procedimento são recuadas. 

Os procedimentos são escritos antes do programa principal

Para executar um procedimento, no programa principal você precisa chamá-lo pelo nome e lembre-se de colocar parênteses!
Você pode chamar um procedimento em um programa quantas vezes quiser.

Agora vamos imaginar que precisamos exibir diferentes mensagens em resposta ao erro de um usuário, dependendo do tipo de erro que ele cometeu.
Nesse caso, você pode escrever seu próprio procedimento para cada erro:   procedimento imprimirErroZero(); começar writeln('Erro. ​​Divisão por zero!'); fim; procedimento printErrorInput(); começar writeln('Erro na entrada!'); fim; E se houver muitos outros erros possíveis? Esta solução não nos convém!
Precisamos aprender a controlar o procedimento, informando qual mensagem de erro exibir.
Para fazer isso, precisamos de parâmetros que escreveremos entre parênteses após o nome do procedimento procedimento printError(s: string); começar gravação(ões); fim; Neste procedimento, s é um parâmetro - uma variável especial que permite controlar o procedimento.
O parâmetro é uma variável que determina como a sub-rotina funciona. Os nomes dos parâmetros são listados separados por ponto e vírgula no cabeçalho do subprograma. Após o parâmetro, dois pontos é seguido por seu tipo.

Agora, ao chamar o procedimento, você precisa indicar entre parênteses o valor real que será atribuído ao parâmetro (variável s) dentro do nosso procedimento printError('Erro! Divisão por zero!'); Esse valor é chamado de argumento.
O argumento é o valor do parâmetro que é passado para a sub-rotina quando ela é chamada.
Um argumento pode ser não apenas um valor constante, mas também uma variável ou uma expressão aritmética.

Muitas vezes é necessário utilizar variáveis ​​adicionais que serão utilizadas somente na sub-rotina. Tais variáveis ​​são denominadas locais (ou locais) e só podem ser manipuladas dentro da sub-rotina em que são criadas.
 
Escopo de variável local é a função ou procedimento dentro do qual ela é declarada

Assim, é possível limitar o escopo (scope) de uma variável apenas à sub-rotina onde ela é realmente necessária. Na programação, essa técnica é chamada de encapsulamento  - ocultar uma variável de ser alterada de fora.

Se for necessário declarar uma variável que seria visível em qualquer lugar do programa (em qualquer sub-rotina), então tais variáveis ​​são declaradas fora de todas as sub-rotinas (ver programa 3 da tabela abaixo)
Essas variáveis ​​são chamadas globais.

Analise três programas: é exibido na tela
1) Neste programa, a variável i é local. Uma variável local é declarada dentro de uma sub-rotina 2) Aqui, mesmo que exista uma variável i no programa principal (com valor 7), uma nova variável local i com valor 5 será criada. 
Ao executar este programa, a tela exibirá o valor 75
3) Este programa possui uma variável global i. Seu valor pode ser alterado dentro de uma sub-rotina, e dentro do programa principal
O procedimento funcionará com a variável global i e será atribuído a ela um novo valor igual a 2. O valor 2
procedimento teste();
var i: inteiro;
começar
    i := 5;
    writeln(i);
fim;
var i: inteiro;

procedimento teste();
var i: inteiro;
começar
    i := 5;
    writeln(i);
fim;

começar
    i := 7;
    escreva(i);
    teste();
fim.
var i: inteiro;

procedimento teste();
começar
    i := 2;
fim;

começar
    teste();
    writeln(i);
fim.

Problema: escrever um procedimento que troca os valores de duas variáveis.
A peculiaridade dessa tarefa é que precisamos que as alterações feitas no procedimento se tornem conhecidas pelo programa chamador.

Vamos tentar escrever o procedimento assim:

var x, y: inteiro; procedimento Swap(a, b: inteiro); // com tal descrição dos parâmetros do procedimento, var c: inteiro; begin // irá copiar os valores dos argumentos (x e y) // variáveis ​​a e b são variáveis ​​independentes não relacionadas a x e y c := a; a := b; b := c; fim; começar x := 1;   y := 2; Trocar(x, y); //valores das variáveis ​​x e y (argumentos) são copiados nos parâmetros a e b writeln('x = ', x, ', y = ', y); // x=1, y=2 fim. Se você executar este programa, poderá ver que os valores das variáveis ​​x e y não foram alterados. Para que os parâmetros alterem os valores dos argumentos, deve-se utilizar passagem de dados por referência. Para isso, após o nome do tipo de dado no cabeçalho da sub-rotina, você deve colocar a palavra var  procedimento Swap(var a, b: integer);   // agora as variáveis ​​a e b obtêm os endereços das variáveis ​​x e y na memória var c: inteiro;  começar c := a; a := b; b := c; fim; Uso: Se você passar um argumento por referência, somente o nome da variável (NÃO um número e NÃO uma expressão aritmética) pode estar neste local ao chamar o procedimento!< br />
NÃO chame um procedimento como este: Swap(x, 4); Swap(5+x, y);