Curso de ScriptVox Intermediário - Aula 2 - Prof. Oswaldo Vernet - iNCE/UFRJ

O objetivo desta segunda aula é introduzir o conceito de chamada de função.

Como motivação, vamos partir de uma situação concreta: desejamos construir um
pequeno jogo, no qual usuário e computador são adversários. O jogo será bem
simples: a cada partida, os dois jogadores lançam dois dados e quem obtiver
o maior número de pontos vence a partida. Será o vencedor do jogo quem somar
o maior número de vitórias nas partidas.

Apenas com o exame do enunciado, já podemos destacar um procedimento que será
utilizado repetidas vezes durante o jogo, pelos dois jogadores: lançar dois
dados simultaneamente e somar os números obtidos nos dois.

Computacionalmente, lançar um dado consiste em obter um número gerado aleatoriamente
entre 1 e 6. Nas versões anteriores do ScriptVox, isto era feito da seguinte
maneira: no início do script, colocávamos o comando RANDOMIZA, responsável por
configurar o gerador aleatório. Em qualquer ponto do script, sempre que precisávamos
gerar um número aleatório, usávamos, por exemplo, o comando

seja n RAND 6
soma n 1 

que funciona assim: a função RAND irá gerar um número entre 0 e o limite especificado
menos um, no caso, entre 0 e 5, e este número será atribuído à variável "n". Depois,
o valor de "n" é incrementado, de modo que, após estes dois comandos, teremos um
número aleatoriamente gerado entre 1 e 6 guardado na variável "n".

Como estamos abandonando o comando Seja e já aprendemos a fazer atribuições na
aula passada, devemos reescrever estes comandos da seguinte forma:

n := rand 6
n := n + 1

Opcionalmente, e isto é importante por uma questão de legibilidade, podemos colocar
o limite que é fornecido à função RAND entre parênteses, assim:

n := rand (6)
n := n + 1

Isto é uma questão de estilo, não é obrigatório, mas facilita bastante a leitura e
a compreensão. Observe que, mesmo nesta nova versão, o comando RANDOMIZA continua 
sendo necessário no início do script.

Também podemos juntar as duas atribuições numa só:

n := 1 + rand (6)

Assim, o lançamento de dois dados e a soma dos pontos pode ser implementada pela 
seguinte sequência de três atribuições:

dado1  := 1 + rand (6)
dado2  := 1 + rand (6)
pontos := dado1 + dado2

Você conseguiria reduzir ainda mais esta sequência de comandos, condensando-a numa
única atribuição?

Notou que apareceu uma novidade nas expressões?  Até a aula anterior, elas
eram compostas de operandos e de operadores. Agora apareceu um novo elemento, que 
funciona ao mesmo tempo como operando e como operador: a chamada da função RAND.

Esta chamada funciona como operador se raciocinarmos da seguinte forma: o valor
que é informado à função como limite (no caso 6) é usado para produzir como resultado
um número. Portanto, a função RAND vai agir sobre este limite, provavelmente fazendo
algumas contas onde ele aparece, para produzir o valor desejado.
Lembra-se do que fazia um operador? Ele agia sobre os operandos, combinando seus valores.

E por que dissemos que a chamada da função RAND funciona também como operando? Porque
o valor calculado pela função (o número entre 0 e 5) é o valor que vai ser somado com o
inteiro 1, para produzir o número de 1 a 6 que desejamos. Então, este valor retornado 
pela função funciona exatamente como um operando na expressão.

Os operadores que vimos até agora (soma, multiplicação, subtração, divisão, resto da
divisão e concatenação) agem sempre sobre dois operandos e, por isso, são denominados
OPERADORES BINÁRIOS. Mas a chamada da função RAND, no exemplo acima, age sobre um único
operando e, por isso, é denominada um OPERADOR UNÁRIO.  Que outro operador unário você
conhece desde o ensino fundamental, em matemática?

Mas existem funções que agem sobre dois operandos, três, quatro, etc..., funcionando
como operadores BINÁRIOS, TERNÁRIOS, QUATERNÁRIOS, etc... Teremos oportunidade, em aulas
futuras, de mostrar algumas delas.

Voltando então ao nosso exemplo, examinemos como funciona exatamente a atribuição

dado1 := 1 + rand (6)

O ScriptVox realizará a seguinte sequência de passos quando se deparar com este comando
para interpretar:

- primeiramente ele percebe que se trata de um comando de atribuição, em que a variável
  a ser modificada é "dado1", por estar à esquerda do sinal de atribuição ":=";
- em seguida, o ScriptVox precisa avaliar a expressão que aparece ao lado direito do sinal
  ":="; esta expressão consiste no inteiro 1 somado ao valor retornado pela função RAND
  agindo sobre o inteiro 6;
- portanto, o próximo passo, será CHAMAR a função RAND, informando a ela o inteiro 6;
- a função RAND é uma função interna do ScriptVox: de posse do inteiro 6, ela fará algumas
  contas e produzirá como resultado um número de 0 a 5;
- este número é, portanto, o valor RETORNADO pela função;
- o ScriptVox, então, prossegue na avaliação da expressão, somando 1 com o valor retornado
  pela função RAND;
- por fim, o valor resultante desta soma será atribuído à variável "dado1"; se havia algum
  valor anteriormente armazenado em "dado1", ele é perdido.

Existem analogias entre chamadas de funções e algumas situações da vida quotidiana que
podem ser proveitosas ao entendimento. Suponha que você se dirige ao caixa do banco, tendo
em mãos uma conta para pagar e o dinheiro exato do valor da conta. Como funcionará o caixa
do banco? Para você, usuário, isto é totalmente irrelevante. O que importa é que você informou
o valor a ser pago e forneceu o dinheiro. Quantos botões o caixa precisará apertar, quantas
gavetas ele irá abrir e fechar, nada disso lhe interessa. O que importa para você é o resultado:
o recibo dizendo que a conta está quitada.

Neste caso, o caixa do banco funciona exatamente como uma função: quando chamada, ela espera
receber algumas informações (no caso, o papel contendo o valor da conta e o dinheiro) e deve
emitir como resposta algo previsível, ou seja, que esteja dentro de certas especificações
(no caso, o recibo quitado e, possivelmente, algum troco). O que o caixa fez,
se ele pensou muito ou pouco, se foi difícil ou fácil processar o seu pagamento, não interessa.
Você quer é o recibo dizendo que pagou a conta.

Repare em mais um ponto: o que você fica fazendo enquanto o caixa do banco processa o seu
pagamento? Bom, é claro, você pode falar no celular, assobiar, reclamar que está demorando,
pensar na morte da bezerra, etc... Mas, olhando sua ida ao banco como parte de um script,
você simplesmente ficará esperando o caixa trabalhar e só prosseguirá após ter em mãos o 
recibo quitado.

O mesmo ocorre na avaliação de uma expressão quando uma chamada de função é encontrada:
a avaliação deve esperar até que a função termine o seu processamento e forneça o valor
de retorno. Só então a avaliação pode prosseguir, usando como operando o valor retornado pela 
função.

Quando se chama uma função, podemos informar a ela alguns valores, que serão necessários 
ao seu processamento. Estes valores são chamados PARÂMETROS para a função.
Valores diferentes podem acarretar processamentos também diferentes por parte da função. 

Imagine de novo o exemplo do caixa: os parâmetros são a conta a pagar e o dinheiro. 
Há três possibilidades: se o dinheiro está contadinho, volta só o recibo; se o dinheiro 
é superior ao valor da conta, voltam recibo e troco; se o dinheiro não é suficiente, 
o caixa lhe dirá que não pode processar o pagamento.

Portanto, o processamento que a função realiza varia com os parâmetros que ela recebe.
Reparou que o retorno também pode variar? No primeiro caso, só volta o recibo; no segundo, 
voltam recibo e troco; no terceiro caso, volta uma informação: "tá faltando, meu camarada".

O que é importante informar ao programador que irá utilizar uma função no seu programa? 
Certamente, o que ela faz é importante. Mas além disso, devemos informar também como chamá-la 
(isto é, quais parâmetros ela espera) e o que ela retorna. Por exemplo, de nada adiantaria
você dirigir-se ao caixa do banco e entregar-lhe um romance de Paulo Coelho: ele não teria
o que fazer com o livro.

No entanto, existem funções que não necessitam de informação alguma para processar. São funções
que não esperam nada e retornam algo. Você consegue imaginar alguma?

Um último ponto: nomes de comandos e nomes de funções são PALAVRAS RESERVADAS na nova
versão do ScriptVox. Isto significa que não é permitido ter nomes de variáveis iguais a 
nomes de funções ou iguais a nomes de comandos. Para isto, sempre que for inventar um nome
para uma variável, lembre-se que os comandos que temos até agora são os seguintes:

        ABRE ACIONA  
		BIPA BUSCA
        CAPTURA CHAMA CHECA CLICA CMD CONCATENA CONECTA CONTINUA COPIA COR
        DEBUG DESVIA DIGITA DIR DIVIDE
        ENQUANTO ESCREVE ESPERA EXECUTA
		FALA FECHA FIM FUNCAO FUNDO
        IMAGEM IMPORTA INTERNET IP
        JANELA
        LE
        MCI MENU MSAA MULTIPLICA
        OBSERVA
        PARA
        QUEBRA
        RANDOMIZA REMOVE RENOMEIA REPETE REPLICA RETORNA
        SE SEJA SENAO SENSOR SERVE SIMBOLOS SOMA SUBSTITUI SUBTRAI
        TELA TERMINA TOCA TRANSFERE

e as funções são:
		
        CHAVES
        DATA DIA DIRETORIO DUPLICA
        FALSE FALSO FDA FIMARQ
        HORA
        INDEFINIDO INSERE INTEIRO
        JUNTA JUSTIFICA
        MAIUSC MAP
        NAO NOT
        ORDENA
        PALAVRA POS
        RAND RETIRA
        SEPARA
        TAMANHO TEMPO TIPO TOKENS TRIM TRUE
        VERDADEIRO

Não, você não precisa decorar esta lista! Quando, por esquecimento, você usar um desses nomes,
o ScriptVox acusará erro e você deverá trocar por outro. Se você realmente quiser batizar alguma
variável com um desses nomes, é simples: use o velho cifrão na frente; desta forma, o nome ficará
distinto de qualquer PALAVRA RESERVADA.

Como o interpretador ainda está em fase de ajustes, esta lista de palavras reservadas pode sofrer
modificações. No manual final, a lista estará completa e será definitiva.


EXERCÍCIOS

Perguntas de Estudo Dirigido:

1) Invente uma outra analogia entre chamadas de funções e uma situação da vida quotidiana.
   Lembre-se de deixar claro quais são os parâmetros e o que a função retorna.
   
2) No texto, descrevemos uma situação em que apenas uma chamada de função ocorre durante
   a avaliação de uma expressão. O que acontece, por exemplo, se tivermos a seguinte expressão:
   
      pontos := 2 + rand (6) + rand (6)
   
   onde aparecem duas chamadas à função RAND?
   
3) Um programador inteligente pensaria em substituir a atribuição

      pontos := 2 + rand (6) + rand (6)
   
   na qual a chamada da função RAND aparece duas vezes seguida, com o mesmo parâmetro (o inteiro 6),
   pela seguinte atribuição:
   
      pontos := 2 + 2 * rand (6)
   
   Explique ao programador por que as duas atribuições não são equivalentes, embora pareçam.

4) Uma função que espera um parâmetro funciona como operador unário. Se esperar dois, funcionará como
   operador binário; três, ternário, etc... E quando a função não requer parâmetros, ela funciona como
   um operador de que tipo?


Exercícios de Programação

1) Na resolução da segunda questão do teste de autoavaliação, você deve ter usado um comando do tipo

   Seja p POS ":" $linha

   para saber em que posição ocorre o caractere ":" na cadeia armazenada em $linha.

   No ScriptVox versão 6.0, este comando pode ser reescrito como uma atribuição:

   p := POS (":", $linha)

Modifique o script que você escreveu para resolver a questão, usando atribuições.

2) A função PALAVRA espera duas informações: um número inteiro e uma cadeia de caracteres.
   O que ela faz é extrair da cadeia a palavra de ordem especificada pelo número. Assim
   
   p := PALAVRA (2, "o gato subiu no telhado")
   
   a função PALAVRA irá procurar a segunda palavra na cadeia "o gato subiu no telhado" e
   irá retornar a cadeia "gato", que será atribuída à variável "p".
   
   Escreva um pequeno script que lê do teclado uma frase sem pontuação e escreve na tela todas 
   as palavras da frase, uma por linha.
   
3) Todas as opções do comando SEJA, como DATA, HORA, TEMPO, etc... estão disponíveis na versão 6.0
   também como chamadas de funções. Assim, podemos escrever

   bomdia := "Rio de Janeiro, " + data() + ", " + hora()

   Experimente esta atribuição no novo interpretador e observe o conteúdo da variável "bomdia"
   (mande escrever na tela). Pode trocar "Rio de Janeiro" pelo nome da sua cidade.

   Observou que "data()" e "hora()" são chamadas de funções?  Elas não precisam de nenhuma
   informação para processar, apenas retornam a data e a hora correntes. Isto é indicado pelo
   abre e fecha parênteses sem nada dentro. Experimente tirar o abre e fecha parênteses.
   Funciona também?
   
   Agora pense um pouco: por que o ScriptVox não implementa data e hora como variáveis em vez
   de funções?
   
4) Descanse.


Exercício de Avaliação (envie até meio-dia de 23 de janeiro para scriptvox@gmail.com):

Escreva o script que implementa o joguinho descrito no início do texto desta aula. Procure criar
subrotinas e usar o comando CHAMA em vez de repetir as mesmas linhas em diversos
pontos do código. Seja gentil com o usuário: pergunte seu nome, diga bom dia, informe a data,
a hora, tudo arrumadinho. Opcionalmente, forneça as regras do jogo (se o usuário assim o desejar).

Lembre-se que a extensão a partir de agora é ".PRO", para o nome do script.

Bom estudo!

Oswaldo Vernet







