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



Na aula passada, utilizamos algumas funções internas do Scriptvox, como RAND, POS

e PALAVRA. O objetivo desta terceira aula é aprender a construir nossas próprias funções.



Vimos que uma chamada de função se comporta tanto como operador quanto como operando durante

a avaliação de uma expressão. As informações que passamos para a função para que

ela realiza seu processamento são chamadas de parâmetros. O valor que a função retorna é chamado

valor de retorno. Este valor é usado como operando na avaliação da expressão 

onde a chamada da função aparece.



Nesta aula, vamos aprender a criar nossas próprias funções. Como de hábito, vamos

partir de um problema concreto a ser resolvido.



Suponhamos que, em diversos pontos de um script, precisamos fazer uma pergunta ao

usuário. A pergunta pode variar, dependendo do lugar em que é feita. A ideia

é que a pergunta seja escrita na tela, seguida de uma interrogação, e que o usuário 

digite uma resposta, que será uma tecla lida sem eco. Esta tecla poderá ser "S" (resposta sim),

"N" (resposta não), ou "R" (o usuário pede que a pergunta seja repetida).

Qualquer tecla diferente destas três deve ser ignorada e, neste caso, uma outra

tecla deve ser aguardada. O usuário tem, no máximo, três chances de errar.

Após isso, ele deve ser insultado e a resposta "não" será assumida.



Se a resposta final for "sim", a variável "resposta" deverá armazenar o inteiro 1

(que vai significar "SIM"); se a resposta for "não", a "variável" resposta deverá

armazenar o inteiro 0 (que vai significar "NÃO"). Portanto, quem chama a função,

deve saber que o retorno 1 significa sim e o retorno 0 significa não.



Com o que conhecemos até agora de ScriptVox, poderíamos rascunhar esta ideia da

seguinte maneira (lembre-se que tudo o que vem após um par de barras é comentário):



pergunta := "Você deseja continuar"             // pergunta pura, sem a interrogação no fim



@inicio

chances := 0

escreve pergunta "?"                            // na hora de escrever é que colocamos a interrogação



@leitura

chances := chances + 1

se chances > 3                                  // se chances for maior que três, o usuário já errou o quanto podia

escreve 

escreve "Sua besta, era pra teclar S, N ou R."  // acrescente aqui os xingamentos que você quiser

resposta := 0                                   // assume a resposta "não", que é zero para nós

desvia @fim                                     // desvia para o final

fim se

 

escreve "Tecle S, N ou R: " &

lê tecla &                                      // Lê a tecla, sem ecoar

se tecla = "S"                                  // Lida a tecla, temos que ver o que o usuário digitou

resposta := 1

senão

se tecla = "s"

resposta := 1

senão

se tecla = "N"

resposta := 0

senão

se tecla = "n"

resposta := 0

senão

se tecla = "R" 

desvia @inicio                                  // o usuário pediu para repetir; volta lá pro início

senão

se tecla = "r"

desvia @inicio

senão

desvia @leitura                                 // a tecla não é nenhuma das esperadas; volta a ler uma tecla

fim se

@fim



* Aqui apenas conferimos a resposta

se resposta = 0                                 // Decodifica a resposta, conforme seja 0 ou 1

escreve "A resposta é NÃO"

senão

escreve "A resposta é SIM"

fim se





O funcionamento é simples e dispensa maiores explicações. Teste no interpretador e veja 

se está funcionando.



Antes de transformarmos este trecho de script numa função, vamos melhorá-lo um pouco.



Primeiramente, os testes duplos incomodam: testar se tecla é igual a "S" (maiúsculo) 

e, caso não seja, testar em seguida se tecla é igual a "s" (minúsculo) é trabalho demais. 

Na versão 6.0, introduzimos uma novidade no comando "Lê": o modificador MAIUSC, que já converte 

a cadeia lida para maiúsculas. Utilizando este modificador, nosso trecho se simplifica e

fica assim:



pergunta := "Você deseja continuar"



@inicio

chances := 0

escreve pergunta "?"



@leitura

chances := chances + 1

se chances > 3

escreve 

escreve "Sua besta, era pra teclar S, N ou R."

resposta := 0 

desvia @fim

fim se

 

escreve "Tecle S, N ou R: " &

lê MAIUSC tecla &

se tecla = "S"

resposta := 1

senão

se tecla = "N"

resposta := 0

senão

se tecla = "R" 

desvia @inicio

senão

desvia @leitura

fim se

@fim



* Aqui a resposta é usada

se resposta = 0

escreve "A resposta é NÃO"

senão

escreve "A resposta é SIM"

fim se





Teste este pequeno script no interpretador e veja se continua funcionando.



Agora sim, vamos transformar o código em uma função. Primeiramente precisamos dar um nome

para a função e pensar como ela será usada no script. Já que a função faz uma pergunta,

vamos chamá-la de FazPergunta. Mas qual pergunta deverá ser feita?  Já que a 

função poderá ser utilizada em diversos pontos diferentes do script, é razoável supor

que a pergunta irá variar de acordo com o que queremos perguntar ao usuário em cada ponto

do script. Portanto, a pergunta a ser feita será um parâmetro para a função, 

ou seja, a função receberá como parâmetro uma cadeia de caracteres contendo a pergunta. 

Portanto, uma maneira de usar a função seria a seguinte:



resposta := FazPergunta ("Deseja continuar")

se resposta = 1

* o usuário deseja continuar

senão

* o usuário não deseja continuar

fim se



Ou, mais resumidamente, podemos até eliminar a variável "resposta", usando diretamente

na comparação o valor retornado pela função. Assim:



se FazPergunta ("Deseja continuar") = 1

* o usuário deseja continuar

senão

* o usuário não deseja continuar

fim se



Muito bem. Já sabemos exatamente como usar a funçao; falta, agora, criá-la. Para isto, 

usaremos o comando FUNÇÃO, que é uma novidade da versão 6. De modo semelhante a outros 

comandos, ele agrega linhas em um bloco, que deve ser terminado pelo comando FIM FUNÇÃO.

Assim:



função FazPergunta

*

* Aqui vem o BLOCO da função

*

fim função



Indicamos que a função receberá um parâmetro colocando entre parênteses, logo após

o nome da função, o nome de uma variável que receberá o parâmetro no momento da chamada.

Assim:



função FazPergunta (pergunta)

*

* Aqui vem o BLOCO da função

*

fim função



Como o ScriptVox interpreta estas linhas?  Ao se deparar com o comando FUNÇÃO, ele entende

que, até aparecer o comando FIM FUNÇÃO, estaremos definindo uma função nossa, que nós

programamos. O nome da função, através do qual ela será chamada em expressões, aparece 

logo depois do comando FUNÇÃO. No exemplo, a função se chama FazPergunta.



Em seguida, o Scriptvox procura, na mesma linha do comando FUNÇÃO, se existem nomes de

variáveis entre parênteses. Se existirem, significa que a função receberá parâmetros e que

os valores destes parâmetros estarão armazenados nas variáveis que aparecerem entre parênteses.

No nosso caso, a função recebe apenas um parâmetro e precisamos apenas da variável "pergunta".



Agora sim, podemos encaixar no bloco da função FazPergunta os comandos que rascunhamos no

início da aula:



* Aqui começa a função FazPergunta

função FazPergunta (pergunta)

@inicio

chances := 0

escreve pergunta "?"          // Aqui usamos o valor da variável "pergunta", que é um parâmetro



@leitura

chances := chances + 1

se chances > 3

escreve 

escreve "Sua besta, era pra teclar S, N ou R."

resposta := 0 

desvia @fim

fim se

 

escreve "Tecle S, N ou R: " &

lê MAIUSC tecla &

se tecla = "S"

resposta := 1

senão

se tecla = "N"

resposta := 0

senão

se tecla = "R" 

desvia @inicio

senão

desvia @leitura

fim se

@fim

fim função

* Aqui termina a função FazPergunta



Estamos quase lá. Faltam ainda alguns detalhes. O combinado é que a função retorne 0 ou 1,

dependendo do que o usuário teclou. Mas ela não está fazendo isso. O que ela está fazendo 

é armazenar na variável "resposta" o valor de retorno. Como fazemos, então, para que a função 

retorne o valor, em vez de armazená-lo em uma variável?  É simples: usando o comando RETORNA, 

que já existia nas versões anteriores do ScriptVox. Só que, agora, o comando RETORNA deve 

especificar o valor que será retornado. Nossa função, quase pronta, ficará assim:



* Aqui começa a função FazPergunta

função FazPergunta (pergunta)

@inicio

chances := 0

escreve pergunta "?"



@leitura

chances := chances + 1

se chances > 3

escreve 

escreve "Sua besta, era pra teclar S, N ou R."

retorna 0 

fim se

 

escreve "Tecle S, N ou R: " &

lê MAIUSC tecla &

se tecla = "S"

retorna 1

senão

se tecla = "N"

retorna 0

senão

se tecla = "R" 

desvia @inicio

senão

desvia @leitura

fim se

fim função

* Aqui termina a função FazPergunta



Observou que o rótulo @fim e o comando Desvia @fim desapareceram? Para que eles serviam,

na versão original?  Serviam para causar um desvio para o final do processamento, caso o

número de chances se esgotasse. O comando RETORNA já faz isso: retornar de uma função

significa encerrar as atividades desta função, disponibilizando a quem chamou

o valor de retorno. Então, não precisamos mais daquele Desvia @fim que havia antes.



Experimente, então, executar o seguinte script no novo interpretador:



se FazPergunta ("Você é homem") = 1

escreve "O usuário é homem"

senão

escreve "O usuário é mulher"

fim se



se FazPergunta ("Você é maior de idade") = 1

escreve "O usuário é maior de idade"

senão

escreve "O usuário é menor de idade"

fim se



termina



*  Aqui começa a função FazPergunta

função FazPergunta (pergunta)

@inicio

chances := 0

escreve pergunta "?"

@leitura

chances := chances + 1

se chances > 3

escreve 

escreve "Sua besta, era pra teclar S, N ou R."

retorna 0 

fim se

escreve "Tecle S, N ou R: " &

lê MAIUSC tecla &

se tecla = "S"

retorna 1

senão

se tecla = "N"

retorna 0

senão

se tecla = "R" 

desvia @inicio

senão

desvia @leitura

fim se

fim função

* Fim da função FazPergunta



Repare que a função FazPergunta é usada em duas linhas diferentes do script antes mesmo 

de ser definida. Esta ordem é irrelevante. Alguns programadores preferem

colocar todas as suas funções no início do script e o código principal depois delas.

No ScriptVox versão 6.0, tanto faz: você pode escolher qual estilo mais lhe agrada.



Vamos, agora, acrescentar mais um detalhe muito importante à nossa função FazPergunta.

Você observou quantas e quais são as variáveis que esta função usa, além da variável 

"pergunta", que armazena o parâmetro para a função?  Examine o código e conte quantas são.



São duas variáveis: "chances" e "tecla", correto? Observe que estas duas variáveis

só são necessárias enquanto a função FazPergunta está sendo executada. Quando a função

termina, não interessa para quem chamou quais são os valores armazenados nelas. O

que interessa é o valor 0 ou 1 retornado.



Quando isto acontece com alguma variável, devemos informar ao ScriptVox que ela é

LOCAL à função. Variáveis locais são criadas sempre que uma função é chamada e são

destruídas quando a função retorna.



Para dizer quais variáveis são locais, basta colocar, na linha do comando FUNÇÃO, 

depois do fecha parênteses que finaliza a lista de parâmetros, o caractere dois

pontos ":" seguido dos nomes das variáveis que queremos tornar locais à função,

separados por vírgulas. No nosso exemplo, então, a função FazPergunta ficaria assim:



* Aqui começa a função FazPergunta

função FazPergunta (pergunta) : chances, tecla

@inicio

chances := 0

escreve pergunta "?"

@leitura

chances := chances + 1

se chances > 3

escreve 

escreve "Sua besta, era pra teclar S, N ou R."

retorna 0 

fim se

escreve "Tecle S, N ou R: " &

lê MAIUSC tecla &

se tecla = "S"

retorna 1

senão

se tecla = "N"

retorna 0

senão

se tecla = "R" 

desvia @inicio

senão

desvia @leitura

fim se

fim função

* Fim da função FazPergunta



A linha:



função FazPergunta (pergunta) : chances, tecla



é denominada CABEÇALHO da função. O Scriptvox, quando se depara com este cabeçalho,

entende que ali começa a definição da função FazPergunta Ele entende também que 

esta função espera um único parâmetro, cujo valor será atribuído à variável "pergunta" 

no momento em que a função for chamada. Por fim, ele saberá que as variáveis "chance" 

e "tela" são locais à função FazPergunta, isto é, elas só vão existir enquanto FazPergunta 

estiver sendo executada.



Você deve estar se perguntando: e o que acontece se não informarmos ao Scriptvox

que estas variáveis são locais? Muito simples: elas serão consideradas variáveis 

GLOBAIS, como todas as outras. Isto significa que, quando a função FazPergunta retornar,

elas estarão lá, armazenando os últimos valores que foram atribuídos a elas.

Muitas vezes, é isso que queremos: ter acesso às variáveis usadas pela função

mesmo quando a função já terminou; mas outras vezes, não.



Sempre que você for programar uma função, identifique cuidadosamente quais as variáveis 

que devem sobreviver após o término da função e quais delas só interessam enquanto 

a função estiver executando. Estas últimas devem ser declaradas como LOCAIS.



Só para encurtar um pouco mais o nosso exemplo, a função FazPergunta ainda pode

ser mais otimizada. Veja a seguir:



* Aqui começa a função FazPergunta

função FazPergunta (pergunta) : chances, tecla

@inicio

chances := 0

escreve pergunta "?"

@leitura

chances := chances + 1

se chances > 3

escreve 

escreve "Sua besta, era pra teclar S, N ou R."

retorna 0 

fim se

escreve "Tecle S, N ou R: " &

lê MAIUSC tecla &

se tecla = "S" retorna 1

se tecla = "N" retorna 0

se tecla = "R" desvia @inicio

desvia @leitura

fim função

* Fim da função FazPergunta



Observou que não precisamos dos comandos SENÃO que apareciam antes? Trocamos tudo

por comandos SE de uma linha. Agora sim, podemos dizer que nossa função está pronta,

o que não significa que você não deva tentar melhorá-la ainda mais!



Mesmo sem esta novidade das funções, nas versões anteriores do ScriptVox já era

possível fazer algo semelhante com o comando "CHAMA". Lembra-se dele?  Pois, neste

curso, o comando CHAMA deve ser abandonado e as funções devem ser utilizadas

em seu lugar.



Para fixarmos bem o que acontece quando uma função é chamada, façamos uma outra

analogia com a vida diária. Imagine que você está estudando e, de repente, aparece

em um texto uma palavra cujo significado você desconhece. Se você é um aluno

aplicado e quer aprender, o que você faz?  Interrompe momentaneamente a leitura do

texto, anota a palavra num pedacinho de papel, pega o dicionário, procura a palavra 

desconhecida, lê a explicação, compreende o seu significado e retorna ao texto, 

agora com a palavra esclarecida. Uma chamada de função funciona exatamente assim. 

No caso, o parâmetro é a palavra que foi anotada no pedacinho de papel e o valor de

retorno é o significado que está no dicionário. A função, na verdade, é a busca

que você faz, por ordem alfabética, folheando as páginas até encontrar a palavra.



Podem existir funções que não precisam de parâmetros para funcionar. Um exemplo

bem comum seria reunir em uma função todo aquele procedimento de boas vindas que

costumamos colocar no início de alguns scripts. Assim:



função BemVindo ()

escreve "Olá. Digite seu nome: " &

Lê nome

escreve "Seja bem vindo, " nome ". Hoje é " dia () ", dia " data () " e são " hora () "."

fim função



Observe que esta função não recebe parâmetros e nem retorna valores. Esquisita ela, não?

Na verdade, ela termina suas atividades quando o comando "FIM FUNÇÃO" é atingido,

já que não existe nenhum comando "RETORNA".



Reparou que ela usa a variável "nome"?  Ela é local ou global?  Se for local, ela

desaparecerá quando a função terminar. Se for global, teremos acesso ao valor armazenado

nela mesmo depois do término da função. Experimente, então, o seguinte script no

interpretador:



BemVindo()            // Aqui a função BemVindo é chamada

escreve "O nome que você digitou foi " nome 



função BemVindo ()    // Aqui a função BemVindo é definida

escreve "Olá. Digite seu nome: " &

Lê nome

escreve "Seja bem vindo, " nome ". Hoje é " dia () ", dia " data () " e são " hora () "."

fim função



Após as boas vindas, aparecerá escrita na tela a frase "O nome que você digitou foi X",

imaginando que você tenha digitado X quando a pergunta apareceu. Logo, a variável "nome"

é global: ela é inicializada dentro da função BemVindo, pelo comando "Lê", mas seu valor

continua acessível após a função BemVindo terminar.



Agora modifique e execute o script declarando a variável "nome" como local à função BemVindo.

Assim:



BemVindo()                           // Aqui a função BemVindo é chamada

escreve "O nome que você digitou foi " nome 



função BemVindo () : nome            // Aqui a função BemVindo é chamada

escreve "Olá. Digite seu nome: " &

Lê nome

escreve "Seja bem vindo, " nome ". Hoje é " dia () ", dia " data () " e são " hora () "."

fim função



Observou o que aconteceu? O ScriptVox reclamou que a variável "nome" não foi inicializada

e a frase que aparecerá na tela será a seguinte: "O nome que você digitou foi inválido".

Você consegue deduzir o que houve? É simples: a variável "nome" que a função BemVindo

utilizou era local e desapareceu quando a função terminou. Daí, o interpretador prossegue

a execução na linha seguinte àquela onde a função BemVindo foi chamada. Nesta linha,

mandamos escrever 



"O nome que você digitou foi " nome



Só que esta variável "nome" é global, não é a mesma usada pela função BemVindo!

E esta variável nunca foi inicializada antes e, por isso, o ScriptVox protesta.



Experimente mais uma modificação:



nome := "João Ninguém"           // Inicializa a variável global "nome" com a cadeia "João Ninguém"

BemVindo()

escreve "O nome que você digitou foi " nome 



função BemVindo () : nome

escreve "Olá. Digite seu nome: "

Lê nome

escreve "Seja bem vindo, " nome ". Hoje é " dia () ", dia " data () " e são " hora () "."

fim função



Notou a mudança?



Moral da História. Podemos ter uma variável global e uma variável local a alguma

função com o mesmo nome. O valor de uma não interfere no valor da outra.



Você também deve ter reparado no comando "Escreve", dentro do BLOCO da função BemVindo.

Há três chamadas de funções neste comando, percebeu?  Isto fica bem claro quando colocamos

o abre e fecha parênteses após os nomes das funções "dia", "data" e "hora".

 

Para finalizar, vamos convencionar algumas regras para usar funções ao longo deste curso.



Primeiramente, sempre que chamarmos uma função, mesmo que ela não tenha parâmetros, vamos

colocar o abre e fecha parênteses depois do nome da função. Assim, fica bem claro que se

trata de uma chamada de função e não de uma variável comum.



Além disso, quando definimos uma função com o comando FUNÇÃO, mesmo que ela não receba

parâmetros, ainda assim vamos colocar o abre e fecha parênteses após seu nome, para ficar

coerente com a maneira de chamar a função.



Se a função necessitar receber mais de um parâmetro, devemos separar os nomes dos parâmetros

por vírgulas, dentro do abre e fecha parênteses.



Se a função utilizar variáveis locais, os nomes destas variável devem aparecer separados

por vírgulas logo depois do caractere dois pontos ":" que colocamos após o fecha parênteses

da lista de parâmetros. 





EXERCÍCIOS



Perguntas de Estudo Dirigido:



1)  Há alguma diferença entre a chamada de uma função interna do ScriptVox e a chamada de uma

    função criada pelo programador e definida no script?  Se houver, qual é ela?

2)  Como se define uma função num Script?  Qual o comando utilizado e como ele deve ser usado?

3)  Como se declaram os parâmetros na definição de uma função?

4)  O que é uma variáveis LOCAL e qual a principal diferença entre uma variável LOCAL e uma variável

    GLOBAL?

5)  Uma variável LOCAL pode ter o mesmo nome de uma variável GLOBAL?  Há confusão nos valores que

    elas armazenam?

6)  Como fazemos para uma função retornar um determinado valor?

7)  Como funciona o comando RETORNA?  

8)  Quais são as duas maneiras de uma função terminar sua execução?

9)  Quando uma função termina de executar, onde prossegue o fluxo de execução?  Ou seja, em que ponto

    do script o ScriptVox continua a execução?

10) O que é o cabeçalho de uma função?

11) Faz alguma diferença definir uma função antes de usá-la ou definir a função depois de usá-la?





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



Sua tarefa, desta vez, será implementar um script de questionário. Ele funciona assim:

no início, você dá as boas vindas ao usuário, pergunta seu nome, fala gentilezas, etc... 

Depois, pergunta também o nome do arquivo contendo as perguntas do questionário.

Cada pergunta deve ocupar apenas uma linha do arquivo.



Cada pergunta lida do arquivo deverá ser escrita na tela e o script deverá aguardar que o usuário responda.

As respostas do usuário deverão ser guardadas em um arquivo cujo nome é o nome do usuário seguido

da extensão ".resp". Assim, se o usuário se chama Guto, o arquivo com as respostas será

"Guto.resp".



O importante é trabalhar com funções neste script. Portanto, as boas vindas devem ser dadas

em uma função, que deve retornar o nome do usuário que foi perguntado. A leitura de cada pergunta, 

sua escrita na tela, a espera pela resposta, a leitura da resposta e sua escrita no arquivo

deverão ser feitos em outra função. Esta função deverá retornar o número de perguntas feitas.

Por fim, o script deve informar quantas perguntas foram feitas ao usuário.



Se quiser, invente mais funções. Mas essas duas que citamos são obrigatórias: uma que dá as boas

vindas e outra que processa as perguntas e as respostas.



Pense com calma quais serão os parâmetros, quais serão os valores retornados e quais serão 

as variáveis locais e globais.



Seu script deve ser testado com as perguntas do estudo dirigido (copie-as para o arquivo que será

lido pelo script, colocando uma em cada linha, sem quebrar).



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



Bom estudo!



Oswaldo Vernet















