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

Nesta quinta aula, vamos falar um pouco de cadeias de caracteres ("strings")
e das novidades que foram introduzidas para sua manipulação na versão 6.0 do
ScriptVox.

As cadeias, até a versão 5.0, eram o único tipo de dados suportado pelo
interpretador. Isto significava que os valores que as variáveis armazenavam
eram sempre cadeias. O tratamento era feito através de alguns comandos,
como CONCATENA, COPIA e SUBSTITUI.

Mas vamos analisar um pouco mais a fundo alguns aspectos das cadeias de
caracteres. O primeiro fato que nos chama a atenção é que as cadeias,
ao contrário dos nomes das variáveis e dos rótulos, são sensíveis a letras
maiúsculas e minúsculas. Assim, as cadeias "UMA FRASE" e "uma frase" são
consideradas distintas pelo interpretador, embora possuam o mesmo significado.
Isto causa um problema que não existe, por exemplo, com os números inteiros:
na hora de comparar duas cadeias, devemos ter a opção de considerar distintas
ou não as letras maiúsculas das letras minúsculas.

Um outro fato importante sobre cadeias: elas são sensíveis a permutações
dos caracteres. Isto significa que as posições que os caracteres ocupam na
cadeia são importantes e, se trocarmos dois caracteres de posição numa cadeia, 
produziremos outra cadeia, distinta da original. Por exemplo: se na cadeia "bolo" 
trocarmos o primeiro com o terceiro caracteres, produziremos "lobo", que é distinta 
da primeira.

Surge então uma primeira pergunta: como podemos ter acesso aos caracteres da
cadeia, individualmente?  Por exemplo, desejamos saber qual é o terceiro caractere
da cadeia "casa bonita". 

Nas versões antigas do ScriptVox, conseguíamos isto através do comando COPIA.
Supondo que a variável $cadeia armazene a cadeia em questão, fazíamos
assim:

copia c $cadeia 3 3

Este comando atribui à variável "c" a subcadeia ("substring") que começa
e termina no terceiro caractere da cadeia armazenada na variável $cadeia.
Assim, a variável "c" armazenará uma cadeia constituída por apenas um caractere,
que vem a ser exatamente o terceiro da cadeia armazenada em $cadeia.

O acesso a caracteres e subcadeias de uma cadeia ficou mais fácil na versão 6.0
e não necessita mais de um comando específico. Então, o equivalente ao comando
copia do exemplo anterior é a seguinte construção, que denominamos INDEXAÇÃO:

c := $cadeia[3]

Esta atribuição irá armazenar na variável "c" o caractere da cadeia armazenada
em $cadeia que está na posição indicada entre colchetes; no caso, o terceiro.
É claro que esta construção só irá funcionar se a variável $cadeia realmente 
armazenar uma cadeia e se esta cadeia tiver, pelo menos, três caracteres. 
Se $cadeia armazenar um inteiro, por exemplo, o ScriptVox acusará um erro, já
que não faz sentido indexar um inteiro.

Muito bem. E, se em vez do terceiro caractere, quisermos saber qual o caractere
da posição "i", onde "i" é uma variável que armazena um número inteiro?  Muito 
simples: usamos a mesma construção, colocando "i" entre colchetes, em vez de 3.
Assim:

c := $cadeia[i]

É claro, para esta atribuição funcionar, além de a variável $cadeia armazenar 
uma cadeia, a variável "i" deverá armazenar um inteiro compreendido entre 1 e o 
comprimento da cadeia (quer dizer, o número total de caracteres que compõem a cadeia).

Vamos, então, usar a indexação dentro no bloco de um comando repete, que terá por
finalidade "percorrer" uma cadeia, examinando todos os seus caracteres. Assim:

le cadeia
repete i TAMANHO(cadeia)
    escreve "O caractere da posição " i " é " cadeia[i]
fim repete

Observou como fizemos para descobrir o comprimento da cadeia?  Usamos como limite
do comando REPETE o valor de retorno da função TAMANHO, que espera como parâmetro
uma cadeia. Esta função retorna quantos caracteres a cadeia possui. Desta maneira,
a variável "i", que controla o comando REPETE, assumirá sucessivamente os valores
1, 2, etc... até chegar ao tamanho da cadeia.  No bloco do comando REPETE, temos
um comando ESCREVE, que mostrará na tela o caractere em cada uma das posições da 
cadeia.

Da mesma forma que podemos obter o caractere de uma dada posição em uma cadeia,
podemos também modificar o caractere em uma dada posição, usando uma atribuição da
seguinte forma:

exemplo := "lodo"
exemplo[2] := "a"

Com a segunda atribuição, modificamos o segundo caractere da cadeia armazenada na variável
"exemplo", que era a letra "o" minúscula, substituindo-o pela letra "a" também minúscula.

Uma cadeia de comprimento zero, isto é, sem nenhum caractere, é denominada CADEIA VAZIA.
Indicamos a cadeia vazia num script abrindo e fechando aspas em seguida. Assim:

c := ""

Em aulas anteriores, já observamos que o operador "+" aplicado a cadeias tem o sentido de
concatenação. Concatenar duas cadeias significa gerar uma terceira cadeia cujos caracteres
são os caracteres da primeira cadeia seguidos dos caracteres da segunda cadeia. Fica claro,
portanto, que a ordem é importante numa operação de concatenação, pois o resultado normalmente
muda quando trocamos a cadeia que fica à esquerda do sinal "+" com a cadeia que fica à
direita. Portanto, a concatenação de cadeias não é uma operação COMUTATIVA.

O que acontece quando um dos operandos da concatenação é a cadeia vazia ?  Observe este fato 
no seguinte script (observe quer dizer execute o script no interpretador e analise o que acontece):

frase := ""          // inicializamos "frase" com a cadeia vazia
le $palavra
enquanto $palavra <> ""
    frase := frase + " " + $palavra
	le $palavra
fim enquanto
escreve frase

Percebeu o que este pequeno script faz?  Começamos com a variável "frase" armazenando a
cadeia vazia. Depois lemos sucessivamente palavras e vamos anexando as palavras ao final da frase,
sempre colocando um branco antes. O processo se interrompe quando a palavra lida for a
cadeia vazia. Isto se consegue simplesmente teclando ênter quando o comando "Lê" estiver
esperando que digitemos uma palavra. Por que colocamos um cifrão no nome da variável $palavra?

Este script, porém, tem um pequeno problema: ele sempre coloca um branco entre o final da frase
já existente e a palavra seguinte que é lida e anexada. Isto funciona bem em todas as iterações,
exceto na primeira: fica um branco sobrando no início da frase. Mas o conserto é simples.
Uma possibilidade seria ler a primeira palavra e já começar a frase por ela. Assim:

le $palavra           // Lê a primeira palavra
frase := $palavra     // A frase começa com a primeira palavra, em vez de começar vazia
le $palavra
enquanto $palavra <> ""
    frase := frase + " " + $palavra
    le $palavra 
fim enquanto
escreve frase

Uma outra possibilidade (mais porquinha) seria manter o script como estava e dar uma
"consertada" na frase no final. Esta "consertada" consistiria em remover o branco que ficou
sobrando na primeira posição. Assim:

frase := ""
le $palavra
enquanto $palavra <> ""
    frase := frase + " " + $palavra
	le $palavra
fim enquanto
frase[1] := ""
escreve frase

Notou como a remoção é feita?  Simplesmente trocamos o caractere da primeira posição
(que era o branco indesejado) pela cadeia vazia. Trocar um caractere por zero caracteres
é o mesmo que remover o caractere.

E se quisermos, em vez de remover um caractere, encaixar uma cadeia dentro de outra, a
partir de uma certa posição?  Por exemplo, seja a atribuição:

frase := "Vamos fazer algo muito louco"

Desejamos incluir a palavra "agora" entre as palavras "Vamos" e "fazer". Bom, sabemos que
a palavra "Vamos" termina na quinta posição da cadeia, onde está o caractere "s". Então é
simples: trocamos o "s" por "s agora":

frase := "Vamos fazer algo muito louco"
frase[5] := "s agora"
escreve frase

O efeito da segunda atribuição é "engordar" a cadeia, porque trocamos um caractere por uma
cadeia de comprimento maior que um. Mas observe que o caractere "s" da quinta posição deve
permanecer na cadeia e, por isso, ele teve que ser incluído no início da cadeia que está
sendo "inserida". Uma outra maneira de escrever isto seria:

frase := "Vamos fazer algo muito louco"
frase[5] := frase[5] + " agora"
escreve frase

Como o ScriptVox encara a segunda atribuição?  Basta seguir a regra que já conhecemos para
processar atribuições. Primeiramente, a expressão do lado direito é avaliada. Esta expressão
consiste em "frase[5]" concatenado com " agora". Mas frase[5] significa o quinto caractere da
cadeia armazenada em "frase", que é o caractere "s". Portanto, o resultado da avaliação da 
expressão é a concatenação entre a cadeia "s" e a cadeia " agora", produzindo a cadeia "s agora".
Esta cadeia é atribuída à posição cinco de frase. Isto tem o efeito de trocar o caractere "s"
que está na posição cinco pela cadeia "s agora". Ou seja, de qualquer forma, na frase final,
haverá um "s" na posição cinco.

A vantagem em escrever da segunda maneira é clara para você?  Da primeira maneira, temos que
saber que o quinto caractere é "s". Da segunda maneira, funcionará seja qual for o quinto
caractere da cadeia. Ela é, portanto, mais geral.

Você deve estar se perguntando, já que é possível trocar um caractere por uma cadeia, tendo o
efeito de encolher ou esticar, se não seria também possível trocar vários caracteres seguidos
por uma outra cadeia, o que teria o efeito de substituir pedaços de uma cadeia por outra. 
Mas isto será assunto para nossa próxima aula.
	

EXERCÍCIOS

Perguntas de Estudo Dirigido

1)  Qual o número da posição inicial em toda e qualquer cadeia para o ScriptVox?
2)  O que quer dizer comprimento de uma cadeia?
3)  Como obtemos o comprimento de uma cadeia?
4)  Mostre, com um exemplo, por que a concatenação entre cadeias não é uma operação comutativa.
5)  Qual o efeito de atribuir uma cadeia ao caractere em uma certa posição de outra cadeia?
6)  O que significa INDEXAR uma cadeia?


Exercícios de programação

1) Teste o seguinte script no novo ScriptVox e explique detalhadamente o que ele faz (explicar
   detalhadamente significa comentar linha por linha):

* início do script
    le frase
    escreve surpresa (frase)

    função surpresa (cadeia) : i, aux
       aux := ""
       repete i TAMANHO(cadeia)
	       aux := cadeia[i] + aux
       fim repete
       retorna aux
    fim função
* fim do script

2) Faça o mesmo com o seguinte script:

* início do script
    le $palavra
	$palavra := $palavra + $palavra
	escreve $palavra
* fim do script

3) Faça um script que lê duas cadeias do teclado e verifica se a segunda cadeia aparece em
   algum lugar da primeira (em outras palavras, verifica se a segunda cadeia é uma subcadeia 
   da primeira). Caso apareça, informe a posição.
   
4) Faça um script que lê um arquivo e troca todas as ocorrências da letra "X" pela letra "Y",
   escrevendo o resultado na tela.
   
5) Agora generalize o exercício anterior: faça uma função que recebe três parâmetros:
   uma cadeia, o caractere original e o caractere substituto. A função retorna uma cadeia que nada mais é
   do que a cadeia que foi dada trocando o caractere original pelo caractere substituto. Não use
   variáveis globais no bloco da função.
   
6) Faça uma função que recebe como parâmetros dois nomes de arquivos contendo textos. A função 
   deverá verificar se os arquivos têm o mesmo conteúdo, retornando 1 se tiverem ou 0 se não tiverem. 
   Dica: compare linha a linha os dois arquivos. Não use variáveis globais no bloco da função.
   O programa principal deverá apenas ler os nomes dos dois arquivos e chamar a função. Se ela
   retornar 1, deverá ser escrito na tela:  "os arquivos têm o mesmo conteúdo"; se ela retornar 0,
   deverá ser escrito:  "os arquivos têm conteúdos distintos". 


Exercício de avaliação (enviar para scriptvox@gmail.com até o meio-dia de 2 de fevereiro de 2012)

Um palíndromo é uma palavra que permanece igual quando lida ao contrário. Exemplos: as palavras
"OVO" e "REVIVER" são palíndromos.

Faça uma função que recebe como parâmetro uma palavra e verifica se ela é um palíndromo.
A função deverá retornar 1 se a palavra for um palíndromo e 0 se não for.

Teste sua função escrevendo um programa principal que fica lendo palavras (até que o usuário tecle ênter)
e testando se cada uma delas é um palíndromo. No bloco da função, não use variáveis globais.

    
Bom estudo!

Oswaldo Vernet







