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

Vamos examinar mais atentamente, nesta aula, o percurso em listas.

Em primeiro lugar, precisamos entender o que significa exatamente percorrer uma lista.
Um PERCURSO em uma lista consiste em executar uma certa ação sobre cada um dos elementos, 
na ordem em que eles aparecem na lista.

A primeira ideia que surge para implementar um percurso é caminhar pelos elementos da lista
um por um, executando a ação desejada. Podemos, portanto, utilizar uma variável inteira
que, começando em 0, vá assumindo sucessivamente os valores 0, 1, 2, e assim por diante.
Cada valor assumido pela variável corresponderá a uma posição da lista e, portanto,
a variável será usada para INDEXAR a lista. Porém, o valor desta variável não deve crescer
indefinidamente: é preciso parar quando alcançamos a última posição. Das aulas anteriores, 
já sabemos que a última posição tem por índice o número de elementos da lista (ou seja, 
sua cardinalidade) menos um, já que o primeiro elemento é o da posição zero.

Se utilizarmos o comaando REPETE, devemos ter um cuidado: a variável de controle deste
comando varia de um até o limite especificado. Entretanto, a lista começa na posicão
zero e não na posição um. Mas isto é simples de contornar: é só usar como índice o
valor da variável de controle menos um.

Assim, imaginando que a variável "texto" armazene uma lista cujos elementos são cadeias,
gostaríamos de escrever na tela cada uma dessas cadeias. O comando REPETE poderia ser usado
da seguinte forma:

repete i TAMANHO(texto)
	escreve texto[i-1]          // Usa o valor de "i" menos um para indexar a lista
fim repete

A variável de controle do comando REPETE é a variável "i", que assumirá os valores
1, 2, etc... até TAMANHO(texto). Lembre que a função TAMANHO, aplicada a uma lista,
retorna o número de elementos da lista. Porém, na hora de utilizar a variável como
índice da lista, subtraímos um, para começar da posição 0 e ir até a última posição
da lista.

Neste caso, a ação executada é simplesmente escrever o elemento. Mas poderia ser
qualquer outra, tão complexa quanto se queira.

Você já se perguntou como a função POS é implementada?  Esta função, quando aplicada
a listas, espera dois parâmetros: um dado a ser procurado e uma lista. Ela retorna
a posição do dado na lista, caso ele corresponda a algum elemento da lista, ou 
menos um, caso o dado não apareça em nenhuma posição.

Vamos mostrar como esta função poderia ser implementada. Para não haver conflito 
com a função POS nativa, vamos chamá-la de PROCURA. Segue-se o código:

* início da função PROCURA
função PROCURA (dado, lista) : i
	se TIPO(lista) <> "lista" retorna -1
	repete i TAMANHO(lista)
		se dado = lista[i-1] retorna i-1
	fim repete
	retorna -1
fim função
* fim da função PROCURA

A função PROCURA recebe dois parâmetros: o "dado" a ser procurado e a "lista" na qual
se dará a procura. A variável "i" é local à função.

O primeiro teste é apenas por cautela: usamos a função TIPO para conferir se
realmente o segundo parâmetro armazena uma lista. Esta função retorna uma cadeia
informando o tipo do que é passado como parâmetro para ela. 

Em seguida, o comando REPETE é usado, tendo "i" como variável de controle.
Esta variável "i" vai assumir sucessivamente os valores 1, 2, e assim por diante,
até o limite TAMANHO(lista). Para indexar a lista, devemos usar o valor de "i"
menos um.

A ação propriamente dita consiste apenas em perguntar se o conteúdo da variável
"dado" é igual ao conteúdo da posição "i" menos um da lista. Se for igual, é porque o
dado se encontra nesta posição; neste caso, retornamos a posição em que ele foi
encontrado.

Observe o último comando da função: 

retorna -1

Este comando só será executado se todas as comparações forem falsas, ou seja,
se o "dado" não estiver em nenhuma posição da lista. Por isto, retornamos menos
um, já que este valor não corresponde a nenhuma posiçao válida.

Uma pergunta: já que uma lista pode conter elementos repetidos, o que acontece
se o dado aparecer em mais de uma posição da lista?  Da maneira como foi programada, 
a função PROCURA só informará a posição correspondente à primeira ocorrência do dado.

Para que a função PROCURA nos informe todas as ocorrências, devemos mudar o tipo
de retorno que ela fornece: em vez de uma posição, a função passaria a retornar uma
lista de posições, informando todas as ocorrências de "dado" em "lista". Se "dado"
não for encontrado em "lista", podemos retornar a lista vazia. As alterações
ficariam assim:

* início da função PROCURA
função PROCURA (dado, lista) : posições, i
	posições := []
	se TIPO(lista) <> "lista" retorna posições
	repete i TAMANHO(lista)
		se dado = lista[i-1] posições[] := i - 1
	fim repete
	retorna posições
fim função
* fim da função PROCURA

A variável local "posições" armazenará uma lista de todas as posições em
que "dado" ocorrer na lista. Portanto, antes de começarmos o percurso, ela é
inicializada com a lista vazia.

Se o tipo do segundo parâmetro não for lista, a função retorna o valor da
variável "posições", que, neste momento, é a lista vazia. 

Durante o percurso, quando o teste 

dado = lista[i-1]

for verdadeiro, isto significa que "dado" foi encontrado na posição "i" menos um; 
portanto, esta posição é anexada à lista de posições. Se, terminado o percurso,
o dado nunca for encontrado, a variável "posições" permanecerá em seu estado
inicial, armazenando a lista vazia durante todo o percurso.

Acabado o percurso, a lista armazenada em "posições" é retornada.

Como esta nova versão da função PROCURA deve ser chamada no script?
Considere o exemplo a seguir:

* início do exemplo de como usar a nova função PROCURA
L := [2, 3, 4, 2, 4, 6, 8, 0, 4, 6]

escreve "Dado a buscar: " &
lê dado
enquanto dado <> ""
	p := PROCURA (dado, L)
	escreve p
	escreve "Dado a buscar: " &
	lê dado
fim enquanto
* fim do exemplo de como usar a nova função PROCURA

A lista "L" é inicializada com alguns elementos inteiros. Se digitarmos o
valor "4" como dado a buscar, deverá aparecer na tela a seguinte linha:

[2, 4, 8]

que é exatamente a lista das posições em que "dado" ocorre em "L". 
Se for necessário ter acesso a essas posições, basta armazenarmos a lista 
retornada pela função PROCURA em alguma variável e, depois, percorrê-la.
Segue-se o código:

* início do exemplo
onde := PROCURA (dado, L)

para p em onde
	escreve "A informação " dado " ocorre na posição " p " da lista L"
fim para
* fim do exemplo

Neste último exemplo, utilizamos outra forma de percorrer uma lista: 
através do comando PARA, já introduzido em aulas anteriores. Qual a diferença 
entre as duas maneiras de percorrer uma lista?

Quando usamos o comando REPETE, estamos percorrendo a lista através de suas
posições, começando em zero e avançando até a cardinalidade da lista menos um.
É através do valor da variável de controle menos um que temos acesso aos 
elementos, usando a INDEXAÇÃO.

Quando usamos o comando PARA, temos acesso direto aos elementos da lista na
ordem em que eles aparecem, sem precisar INDEXAR a lista. A variável de controle 
do comando PARA denomina-se um ITERADOR e ela assume sucessivamente os valores de
cada um dos elementos da lista.

Então, como decidir qual comando usar?  É simples: se apenas os elementos da
lista nos interessam, devemos usar o comando PARA; porém, se, além dos elementos,
as posições também nos interessam, devemos usar o comando REPETE, tomando o valor
da variável de controle menos um como índice para a lista. 

No exemplo anterior, as posições é que são importantes; por isso, o comando REPETE
foi o escolhido.


EXERCÍCIOS

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

Faça um script que lê, de um arquivo, os nomes e as notas dos alunos de
uma turma. Para evitar decimais, considere as notas de 0 a 100. Os nomes 
e notas devem ser guardados em uma única lista de alunos, sendo cada aluno, 
é uma variável compartimentada, consistindo de dois compartimentos: o nome e a nota.

Ao final, o script deverá informar a média da turma (não se importe com decimais)
e os nomes e notas dos alunos superiores ou iguais à média.     

Bom estudo! 

Oswaldo Vernet
