Sie sind auf Seite 1von 23

Especificação do Portugol

(release 1.0, agosto de 2009)

Selan R. dos Santos David Dérharbe André M. C. Campos Marcelo Siqueira



c 2009, Departamento de Informática e Matemática Aplicada — DIMAp

13 de agosto de 2009

Sumário
1 Introdução 2

2 Tipos básicos, variáveis e constantes 2


2.1 Tipos básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Variáveis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.3 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.4 Definição de novos tipos básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

3 Blocos, funções e procedimentos 5


3.1 Escopo e visibilidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.2 Passagem de parâmetro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.3 Passando arranjos por parâmetro . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

4 Operadores 7

5 Estruturas de controle 8
5.1 Estruturas condicionais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
5.2 Laços . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.2.1 Para . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.2.2 Repita-Até . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.2.3 Enquanto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

6 Tipos avançados 13
6.1 Tipos referência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6.2 Estruturas ou registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6.3 Tuplas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
6.4 Tipos função ou funcionais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

7 Tipo abstratos de dados (TAD) 17

8 Exemplos 20
8.1 Algoritmo de ordenação por flutuação . . . . . . . . . . . . . . . . . . . . . . . . 20
8.2 Algoritmo identificar caminho raiz-para-folha de uma árvore ternária . . . . . . . 22

1
Especificação do Portugol v1.0, julho de 2009

1 Introdução
Este documento apresenta uma especificação da linguagem Portugol. O objetivo da linguagem
apresentada neste documento é definir um padrão de representação algorı́tmica a ser adotado
nos três blocos didáticos oferecidos pelo DIMAp: “Introdução a Técnicas de Programação”,
“Estruturas de Dados Básicas” e “Estruturas de Dados Avançadas”.
Toda linguagem possui idiomas próprios (formas padronizadas de resolver determinados pro-
blemas). Elas possuem caracterı́sticas e particularidades, fazendo com que a implementação de
uma determinada ideia siga uma abordagem ou outra. Com o Portugol não será diferente. Po-
rém, procuramos especificar uma linguagem mais genérica possı́vel. A seguir serão apresentados
os componentes da linguagem através de uma definição genérica, seguida de exemplos.
A geração dos algoritmos foi realizada via LATEX, utilizando-se o pacote algorithm2e1 .

2 Tipos básicos, variáveis e constantes


Na descrição dos algoritmos, é possı́vel utilizar tipos básicos (ou primitivos), tipos compostos ou
heterogêneos e tipos abstratos de dados (TADs). Nesta seção, serão apresentados apenas os tipos
básicos. Os demais, por envolver conceitos ainda não abordados, serão descritos posteriormente.
Esta seção descreve igualmente como utilizar os tipos básicos para declarar variáveis e constantes.

2.1 Tipos básicos


Os tipos básicos são semelhantes à maioria das linguagens de alto nı́vel atuais. São eles:

. Números inteiros: inteiro


. Números reais ou ponto flutuante: real
. Caractere: caractere
. Cadeia de caracteres: texto
. Lógico ou Booleano: booleano
. Sequências ou arranjos de um tipo: arranjo de

Os arranjos correspondem a agrupamentos de variáveis do mesmo tipo básico, como em


vetores (arranjos unidimensionais) e matrizes (arranjos bidimensionais). Esse tipo de dado
suporta acesso indexado à posição de cada elemento, através do operador ‘[ ]’ (por exemplo,
arranjo[ı́ndice]). Vale, entretanto, salientar que a indexação inicia com o valor 0 (zero), ou seja
o primeiro elemento de um arranjo se encontra no ı́ndice 0. O tamanho de um arranjo pode ser
consultado com o operador tam.
O tipo texto pode ser considerado como arranjo de caractere (cadeias de caracteres). Assim,
pode-se igualmente acessar elementos do texto (caractere) através de ı́ndices, bem como consultar
o tamanho de um texto através do operador tam. Porém, diferentemente de um arranjo, que
possui um tamanho fixo definido na sua declaração, um texto possui tamanho variado.

2.2 Variáveis
A declaração de variáveis dos tipos básicos é realizada através da palavra reservada var. Mais
de uma variável pode ser declarada ao mesmo tempo, contando que elas sejam do mesmo tipo.
Após declarar uma variável (e seu tipo), pode-se atribuir apenas os valores pertinentes ao tipo
em questão. A atribuição é realizada através do operador ‘←’. A atribuição pode ser feita a
qualquer momento após e/ou durante a declaração da variável. Porém, para evitar confusão
1
http://www.ctan.org/tex-archive/macros/latex/contrib/algorithm2e/

Página 2
Especificação do Portugol v1.0, julho de 2009

de valores, não será permitida a atribuição conjugada com inicialização para situações onde são
declaradas duas ou mais variáveis simultaneamente.
Os exemplos apresentados no Quadro 1 ilustram a declaração de variáveis utilizando os tipos
básicos apresentados anteriormente.

var i : inteiro # 1o : declarando uma única variável inteira ‘i’ (sem inicializaç~ao)
i←0 # 2o : inicializando a variável inteira ‘i’ com zero
# alternativamente podemos combinar as duas operaç~oes acima. Veja:
var i : inteiro ← 0
var x, y : real # declarando duas variáveis reais ‘x’ e ‘y’
var achou : booleano ← falso # declarando e inicializando uma variável lógica ‘achou’
var nome : texto ← “Portugol” # declarando e inicializando uma cadeia de caracteres
var c : caractere ← nome[1] # declarando e inicializando uma variável caractere com ‘o’
var vet : arranjo de 10 real # declarando e fazendo uso de arranjos
var j : inteiro ← tam vet − 1 # ‘j’ é inicializado com o ı́ndice da última posiç~ao de ‘vet’
var z : real ← vet[i] # ‘z’ é inicializado com o valor do último elemento de ‘vet’
# Arranjo unidimensional de 10 inteiros
var V et : arranjo de 10 inteiro
# Arranjo bidimensional de caracteres com 25 linhas e 10 colunas (ordem linha-coluna )
var M at : arranjo de 25 × 10 caractere
# arranjo unidimensional de cadeia de caracteres com 2 posiç~oes, já inicializadas
var MOpcoes : arranjo de 2 texto ← [“Inserir”, “Sair”]

Quadro 1: Declaração de variáveis do tipo primitivo ou básico do Portugol.

2.3 Constantes
Constantes são variáveis que são declaradas e inicializadas simultaneamente, cujo valor não pode
mais ser modificado. Veja exemplos deste tipo de declaração a seguir, no Quadro 2. É importante
ressaltar que, na definição de valores constantes, não é usado o operador de atribuição ‘←’ (como
ocorre na atribuição de variáveis), e sim o operador de definição ‘≡’. Note, em vermelho na linha
5, uma definição incorreta para uma constante, devido à falta da inicialização.

# Exemplos de definiç~oes corretas de constantes.


1 cte PI : real ≡ 4 × arctan(1) # uma constante real ‘P I’
2 cte E : real ≡ 2.718281828 # uma constante real ‘E’
3 cte MSG : texto ≡ “Não encontrado” # uma cadeia de caracteres constante ‘M SG’
4 cte SIM : caractere ≡ ‘Y’ # uma constante caractere ‘SIM ’
# Exemplo de definiç~ao INCORRETA de constante.
5 cte NAO : caractere # INCORRETO! faltou a inicializaç~ao para a constante ‘N AO’

Quadro 2: Declaração de constantes do tipo primitivo ou básico do Portugol.

2.4 Definição de novos tipos básicos


O tipo de uma variável especifica que possı́veis valores ela pode receber. Podemos, então, definir
novos tipos se especificarmos o conjunto de valores que uma variável pode receber. A definição

Página 3
Especificação do Portugol v1.0, julho de 2009

de um tipo é realizada através da palavra reservada tipo, seguida do nome do tipo a ser definido,
do separador ‘≡’ seguido de uma expressão de tipo. Após definido, um novo tipo pode ser usado
de maneira similar aos tipos primitivos de Portugol.
A expressão de tipo pode ser um conjunto de valores predefinidos ou construtores de tipos.
O Quadro 3 ilustra a construção, declaração e uso de um novo tipo, bem como a definição de
novos tipos baseados em outros já existentes.

# Um exemplo de declaraç~ao de tipo.


tipo naipe ≡ { ouro, paus, espada, copas }
var carta: naipe # declarando a variável ‘carta’ do tipo recém definido naipe
..
.
carta ← ouro # inicializaç~ao com um dos possı́veis valores do tipo recém definido
carta ← copas # outra inicializaç~ao similar
# Tipo arranjo unidimensional de booleanos com 8 posiç~oes
tipo Byte ≡ arranjo de 8 booleano
# Tipo arranjo unidimensional de booleanos com um número indeterminado de posiç~oes
tipo VetorBit ≡ arranjo de booleano
# Tipo arranjo unidimensional de inteiros com um número programável de posiç~oes
tipo VetorR ≡ arranjo de N real

Quadro 3: Exemplo de uma definição de tipo naipe e outros tipos baseados em arranjos.

Página 4
Especificação do Portugol v1.0, julho de 2009

3 Blocos, funções e procedimentos


As unidades básicas de organização do Portugol são o bloco principal (ponto inicial do programa,
identificado pela palavra programa), funções e procedimentos. O programa principal é o ponto
de entrada de um programa, enquanto as funções e procedimentos são unidades de modulari-
zação. A principal diferença entre módulos definidos como procedimentos ou funções é que na
primeira categoria não há retorno de valor, enquanto na segunda há. O Quadro 4 apresenta
exemplos dos três tipos de blocos.

programa
# declaraç~ao de variáveis locais ao programa principal
1 var nota1: real ← 1.5
2 var nota2: real ← 6.7
3 var nota3: real ← 8.9
4 var media: real
# calcula a média ponderada do aluno e imprime
5 media ← calculaMedia(nota1, nota2, nota3 )
6 imprime(“Fulano Filho”, media)
fim
# calcula a média ponderada de 3 valores reais, retornando o resultado, um real
7 função calculaMedia(nota1: real; nota2: real; nota3: real): real
8 var mediaP onderada: real
9 mediaP onderada ← (4 × nota1 + 5 × nota2 + 6 × nota3)/15
10 retorna mediaP onderada
11 fim
# imprime o nome do aluno e sua média. Repare que n~ao há valor de retorno
12 procedimento imprime(nome: texto; media: real)
13 escreva ( “A média de ”, nome, “ é ”, media)
14 fim

Quadro 4: Declaração de programa, funções e procedimentos.

3.1 Escopo e visibilidade


As variáveis que são declaradas dentro de um bloco ou que são parâmetros de uma função ou
procedimento possuem escopo e visibilidade locais ao bloco onde tais variáveis foram declara-
das. Variáveis globais devem ser declaradas fora de qualquer bloco, inclusive fora do programa
principal, preferencialmente logo antes do programa principal.

3.2 Passagem de parâmetro


Cada parâmetro de função ou procedimento deve ter seu tipo identificado individualmente.
Assim, não será aceitável declaração conjunta de variáveis, como por exemplo:

Página 5
Especificação do Portugol v1.0, julho de 2009

# declarando par^ametros de maneira errada, ao declarar simult^aneamente 3 variáveis


função calculaMedia(nota1, nota2, nota3: real): real
..
.
retorna mediaP onderada
fim

3.3 Passando arranjos por parâmetro


A declaração de parâmetro correspondente a um arranjo é feita sem indicar explicitamente seu
tamanho. Para recuperar esta informação, basta invocar o comando tam que retorna um valor
inteiro correspondente ao tamanho do arranjo. O mesmo vale para arranjos de duas (matriz) ou
mais dimensões. No Quadro 6 são apresentados alguns exemplos de passagem de arranjos por
parâmetro para funções.

# imprime o tamanho, o primeiro e o último elemento de um arranjo


procedimento consultaVetor(V et: arranjo de inteiro)
escreva ( “O tamanho do arranjo é ”, tam V et)
escreva ( “A primeiro elemento do arranjo é ”, V et[0])
escreva ( “A último elemento do arranjo é ”, V et[tam V et − 1])
fim
# Exemplo de como passar uma matriz por par^ametro
# A funç~ao abaixo realiza a transposiç~ao de uma matriz
procedimento transpor(M arranjo de ref inteiro)
var aux: inteiro # ajudar a troca de elementos
var i, j: inteiro # controladores de laço para linha e coluna
var nLin, nCol: inteiro # armazena, respectivamente, o número de linhas e colunas da matriz
nLin ← tam M # recupera a primeira dimens~ao do arranjo
nCol ← tam M [0] # recupera a segunda dimens~ao do arranjo
para i ← 0 até nLin − 2 faça # laço para percorrer as linhas
para j ← i + 1 até nCol − 1 faça # laço para percorrer as colunas
aux ← M [i, j]
M [i, j] ← M [j, 1]
M [j, i] ← aux
fim
fim
fim

Quadro 5: Passando arranjos por parâmetro para funções e procedimentos.

Página 6
Especificação do Portugol v1.0, julho de 2009

4 Operadores
Os operadores do Portugol, devido à sua flexibilidade, podem ser definidos à medida que forem
sendo necessários. Os operadores matemáticos têm precedência sobre os relacionais, que por sua
vez têm maior precedência sobre os operadores lógicos. A seguir são apresentados os operadores
mais básicos do Portugol.
As tabelas 1, 2 e 3 apresentam os operadores matemáticos, relacionais e lógicos básicos,
respectivamente. Eles são listados em ordem de precedência, do maior para o menor.

Tabela 1: Tabela de operadores matemáticos.


Operador Representação Exemplo Associação
menos unário − a ← −a; ←−
potenciação ˆ ou representação direta a ← aˆ5 ou a ← a5 ←−
multiplicação ∗ ou × a ← a ∗ 5 ou a ← a × 5 −→
divisão / a ← a/2; −→
módulo % a ← a%2 −→
adição + a←a+b −→
subtração − a←a−b −→

Tabela 2: Tabela de operadores relacionais.


Operador Representação Exemplo Associação
comparação menor < a < 10 . . . −→
comparação menor-igual ≤ a ≤ 10 . . . −→
comparação maior > a > 10 . . . −→
comparação maior-igual ≥ a ≥ 10 . . . −→
comparação igual = a = 10 . . . −→
comparação diferente 6= a 6= 10 . . . −→

Tabela 3: Tabela de operadores lógicos.


Operador Representação Exemplo Associação
negação não não achou . . . −→
E lógico e a ≤ 10 e b ≥ 20 . . . −→
OU lógico ou a ≤ 10 ou b ≥ 20 . . . −→

Página 7
Especificação do Portugol v1.0, julho de 2009

5 Estruturas de controle
5.1 Estruturas condicionais
As estruturas condicionais controlam o fluxo de execução de acordo com a avaliação de uma
expressão. Existem vários tipos de estruturas condicionais, que devem ser selecionados de acordo
com o algoritmo. O Quadro 7 ilustra exemplos de uma estrutura condicional simples, uma
composta e uma encadeada.

se a > 10 então # Exemplo de condicional simples


x ← x + 20
fim
se a > 10 então # Exemplo de condicional composta
x ← x + 20
senão
y ←y−x
fim
se a > 10 então # Exemplo de condicional encadeada
x ← x + 10
...
senão se a < 20 então
y ← y + 20
...
senão se a < 30 então
z ← z + 30
...
senão
x←y+z
...
fim

Quadro 6: Estruturas condicionais usando se.

A estrutura condicional se encadeada permite definir vários blocos, onde apenas um será
executado. Os testes são efetuados do primeiro ao último bloco. O bloco cujo teste se averiguar
verdadeiro primeiro será executado, e os demais desprezados. Uma outra forma de realizar essa
escolha de blocos é através da estrutura de seleção múltipla, ou caso. A diferença é que esta
prevê uma comparação de igualdade entre uma variável e cada um dos casos definidos. Ela
é, portanto, mais fácil de se ler nesses casos especı́ficos. Opcionalmente, pode-se definir uma
última opção que será executada, caso nenhuma igualdade seja validada. O Quadro 8 apresenta
um exemplo desta estrutura de controle.

Página 8
Especificação do Portugol v1.0, julho de 2009

# Exemplo de seleç~ao de múltipla escolha


1 caso tempo seja
2 igual a “nublado”: # se tempo tem o valor ‘nublado’
3 amanha ← “chovendo”
4 ...
5 igual a “chovendo”: # se tempo tem o valor ‘chovendo’
6 amanha ← “ensolarado”
7 senão # se nenhuma das opç~oes forem satisfeitas...
# ... entramos neste último bloco, que é *opcional*!
8 amanha ← “chovendo”
9 ...
10 fim
11 fim

Quadro 7: Controlador de múltiplas escolhas com a estrutura ‘caso’.

Página 9
Especificação do Portugol v1.0, julho de 2009

5.2 Laços
Os laços são estruturas de controle que executam um bloco de comandos um certo número de
vezes. O controle sobre a quantidade de vezes que o bloco é executado pode ser:

. de acordo com uma certa condição testada no inı́cio do bloco (executa o bloco de 0 à N
vezes);
. de acordo com uma certa condição testada no fim do bloco (executa o bloco de 1 à N
vezes);
. por um número pré-determinado de vezes (executa o bloco de 0 à N vezes); ou
. para cada ocorrência em um conjunto de entradas.

5.2.1 Para
Existem várias formas de se definir um laço através da estrutura para. O mais comum possui
um número de execuções pré-determinado, definido numericamente. Este tipo de controle de
laço é ideal, por exemplo, quando se deseja acessar variáveis indexadas, como arranjos e textos.
Neste caso, o ı́ndice utilizado para controlar a iteração do laço pode ser usado como indexador
do arranjo (usado, por exemplo, para implementar um vetor ou matriz).
Outra forma de utilizar a estrutura para é percorrer os elementos de um conjunto (arranjos
ou tipos abstratos de dados, a serem explicados mais a frente; por hora, considere um conjunto
como um elemento matemático abstrato). De fato, quando se deseja percorrer elementos em um
conjunto, o laço para é a melhor opção por manter um bom nı́vel de abstração (não interessa
saber, no momento, como cada um dos elementos do conjunto é acessado). Neste caso, a forma
de utilizá-lo é, entretanto, ligeiramente diferente. Utiliza-se a expressão para todo seguida de
uma variável que assumirá o valor de um elemento do conjunto a cada iteração do laço e a
expressão de pertinência a um conjunto.
O Quadro 9 apresenta vários exemplos de uso desse tipo de laço.

5.2.2 Repita-Até
Outro tipo comum de laço realiza o teste de parada ao final de cada iteração do bloco. O
comando ‘repita-até’ é a estrutura de controle com esta caracterı́stica. Nessa estrutura, o laço
termina quando a expressão no final do bloco se torna verdadeira. O uso dessa estrutura é
recomendado quando se deseja que o bloco seja executado pelo menos uma vez. O Quadro 10
provê um exemplo desta estrutura de controle.

5.2.3 Enquanto
Outra estrutura de controle de laço bastante comum é o enquanto, que realiza o teste de
continuidade do laço antes de cada iteração do bloco. Nessa estrutura, o laço termina quando a
condição especificada no inı́cio do bloco passa a ser falsa. Diferentemente da estrutura repita-
até, o uso dessa estrutura é recomendado quando o bloco pode não ser executado. Ou seja, se a
expressão condicional no inı́cio do bloco for falsa, não haverá iteração alguma e a sequência de
instruções passa para o comando após o bloco. O Quadro 11 fornece alguns exemplos de laço
enquanto.

Página 10
Especificação do Portugol v1.0, julho de 2009

s←0
para i de 1 até 100 faça # a cada iteraç~ao, i assume o valor de 1 a 100
se i%2 = 0 então
s ← 1/i
senão
s ← −(1/i)
fim
fim
escreva ( “O resultado da série é:”, s )
# ainda pode ir de trás pra frente, alterando-se o ‘passo’.
para j de 10 até 1 com passo −1 faça # j vai decrementando de 10 a 1
escreva (“Mensagem #”, j, “impressa!”)
fim
# acessando elementos de um vetor
produtoInterno ← 0
para k ← 0 até 2 faça
produtoInterno ← produtoInterno + (V et1[k] × V et2[k])
fim
para todo i ∈ S1 faça # a cada iteraç~ao, i assume um elemento do conjunto S
remova i de S1
insira i no conjunto S2
fim

Quadro 8: Exemplos diversos de laço ‘para’.

# exemplo de uso do laço repita-até


var xmin : real ← −100
var xmax : real ← 100
var xmed : real
var raiz: real
repita # o laço só termina quando a raiz de f () for encontrada
xmed ← (xmin + xmax )/2
raiz ← f (xmed )
se raiz < 0 então
xmin ← xmed
senão
xmax ← xmed
fim
até raiz = 0

Quadro 9: Exemplo de laço ‘repita-até’.

Página 11
Especificação do Portugol v1.0, julho de 2009

var achou: booleano ← falso


var tentativas: inteiro ← 0
enquanto não achou faça
varrerEmBusca(valor)
se valor = P ROCU RADO então
x ← x2
achou ← verdadeiro
senão
tentativas ← tentativas + 1
fim
fim

Quadro 10: Exemplo do laço ‘enquanto’.

Página 12
Especificação do Portugol v1.0, julho de 2009

6 Tipos avançados
6.1 Tipos referência
Uma referencia2 é um tipo que contém o endereço (daı́ o nome, referência) de uma variável de
um tipo qualquer. A declaração de variáveis do tipo referencia é realizada através do operador
ref seguido do tipo da variável que será referenciada.
Além do operador ref, usado na declaração da variável, há dois outros operadores associados
às referências. O primeiro é o operador de endereço (end) que, quando aplicado a uma variável,
retorna o endereço (referência) onde o valor desta variável está sendo armazenado na memória.
O segundo operador é chamado o operador de desreferência, que age no sentido inverso ao pri-
meiro operador. Ou seja, ele tem o papel de consultar o conteúdo de uma variável através de sua
referência. Para acessar o conteúdo, usamos o operador de acesso ‘[ ]’. Por fim, existe também
uma palavra reservada para representar o valor de uma referência vazia (que não está endere-
çando variável alguma), também chamada de referência (ou ponteiro) nulo ou representado pelo
sı́mbolo ⊥. O Quadro 12 ilustra o uso desses operadores.

# Exemplo de uso do operador de endereço e de desrefer^encia (acesso ao conteúdo)


var cnt : inteiro ← 10 # Declaraç~ao de uma variável regular
var ref Int : ref inteiro # ref Int é uma refer^encia para uma variável do tipo inteiro
ref Int ← end x # ref Int aponta para o endereço de x
escreva (@{ref Int}) # imprime o conteúdo de ref Int, que é 10
# Exemplo de uso da refer^encia nula
var ref V al : ref inteiro ← nulo # declaraç~ao de refer^encia com inicializaç~ao
var ptHead : ref inteiro ← ⊥ # declaraç~ao de refer^encia com inicializaç~ao alternativa

Quadro 11: Exemplo de uso do tipo referência.

Uso de referência na passagem de parâmetros de uma função


A passagem de parâmetro em uma função ou procedimento apresentada na Seção 3.2 é realizada,
por default, por valor. Isso significa que, quando uma variável é passada, seu valor é copiado
a uma variável local da função e, se modificada, o valor da variável original não é alterado.
Esse comportamento pode ser mudado se o parâmetro da função for uma referência para a
variável. Passagem de parâmetros por referência deve ser indicada com a palavra reservada ref,
precedendo o identificador da variável, conforme exemplificado no Quadro 13.

6.2 Estruturas ou registros


Estruturas os registros são tipos heterogêneos de dados. Eles definem um tipo de dado como
sendo a composição de vários tipos, sejam eles básicos, composições homogêneas (arranjos e
textos) ou mesmo outros tipos heterogêneos. Usamos a palavra reservada estrutura para espe-
cificar a composição do tipo. O Quadro 14 oferece alguns exemplos de declaração de estruturas e
sua posterior instanciação. Nesse exemplo, declaramos a estrutura como um novo tipo, chamado
de Aluno.
2
Também conhecido como ponteiro ou apontador, pois “aponta” para um endereço de memória.

Página 13
Especificação do Portugol v1.0, julho de 2009

# calcula a média ponderada de 3 valores reais, retornando o resultado, um real


procedimento troca(val1: ref inteiro; val2: ref inteiro)
var aux: inteiro
aux ← @{val1}
@{val1} ← @{val2}
@{val2} ← aux
fim
programa # programa principal
var a: inteiro ← 3 # inicializa a com 3
var b: inteiro ← 5 # inicializa b com 5
troca(a, b) # troca os valores de a e b, passando-os por referencia a troca
escreva (a) # imprime 5
escreva (b) # imprime 3
fim

Quadro 12: Exemplo de passagem de parâmetros por referência para funções.

# declaraç~ao de estrutura Aluno, utilizada em lista encadeada


tipo Aluno ≡ estrutura
mat: inteiro # matrı́cula do estudante
nome: texto # cadeia de caracteres para o nome do estudante
IRA: real # ı́ndice de rendimento acad^emico
notas : arranjo de 4 real # quatro notas da disciplina
prox: ref Aluno # refer^encia para próximo nó da lista
fim
# uma variável de tipo Aluno
var estudante : Aluno
# inicializaç~ao dos campos da estrutura
estudante.mat ← 2008123456
estudante.nome ← “Zé Ninguém”
estudante.IRA ← 9.2
estudante.notas ← [ 8.7; 7.7; 9.1; 8.2 ]
estudante.prox ← nulo

Quadro 13: Declaração de tipo heterogêneo (estrutura), sua instanciação e utilização.

Página 14
Especificação do Portugol v1.0, julho de 2009

# Um exemplo de declaraç~ao de tipo.


tipo Racional ≡ estrutura
num: inteiro # numerador
den: inteiro # denominador
fim
cte RacZero : Racional ≡ [0, 1] # uma constante do tipo Racional
# Uso de uma express~ao de tipo ‘direta’ ou ‘an^onima’ na definiç~ao de constante.
cte RacZero : estrutura
num: inteiro # numerador
den: inteiro # denominador
fim
≡ [0, 1]

Quadro 14: Exemplos de inicialização de um tipo estrutura.

6.3 Tuplas
Tipos tuplas são tipos compostos heterogêneos (como os tipos registros). Porém, ao invés de
selecionar os componentes pelo nome, a seleção é realizada por posição, como em um arranjo.
Tipos tuplas também podem ser interpretados como o equivalente computacional do produto
cartesiano da matemática. Cada componente de uma tupla pode ser acessado em leitura ou em
escrita individualmente. O Quadro 16 ilustra seu uso.

# declaraç~ao do tipo tupla NotasParc


tipo NotasParc ≡ tupla h real, real, real i
# uma variável de tipo NotasParc, com inicializaç~ao
var notasEst : NotasParc ← h 0.0, 0.0, 0.0 i
notasEst.1 ← 8.4 # Escrita da 1a componente da tupla notasEst
# Eis uma instruç~ao com leitura e escrita dos componentes de uma tupla
notasEst.3 ← (notasEst.1 + notasEst.2)/2

Quadro 15: Declaração de tipo heterogêneo (tupla), e de uma variável desse tipo com inicialização.

6.4 Tipos função ou funcionais


Mesmo linguagens imperativas permitem ter variáveis e parâmetros com tipos funcionais3 . Em
Portugol, podemos também lançar mão desse recurso para definir, por exemplo, um algoritmo de
ordenação que tem como parâmetro uma função de comparação dos valores a serem ordenados.
Vale salientar que as funções atribuı́das às variáveis do tipo função devem corresponder às
assinaturas do tipo. Ou seja, o número de parâmetros e seus tipos, bem como o tipo de retorno
devem ser iguais. Caso contrário, a atribuição é inválida. Observe o Quadro 17 que contém
exemplos de declaração e uso de tipos funcionais.

3
Um exemplo clássico é a função qsort fornecida pela biblioteca padrão de C.

Página 15
Especificação do Portugol v1.0, julho de 2009

# Definiç~ao de funç~oes regulares, todas com a mesma assinatura.


função compara1aAval(alunoA: NotasParc; alunoB: NotasParc): booleano
retorna alunoB.1 > alunoA.1
fim
função compara2aAval(alunoA: NotasParc; alunoB: NotasParc): booleano
retorna alunoB.2 > alunoA.2
fim
função compara3aAval(alunoA: NotasParc; alunoB: NotasParc): booleano
retorna alunoB.3 > alunoA.3
fim
# declaraç~
ao e uso do tipo funç~ao CompareNotas
tipo ComparadorDeNotas ≡ função (NotasParc, NotasParc) −→ booleano;
var comparador : ComparadorDeNotas ← compara1aAval

Quadro 16: Declaração de 3 funções com a mesma assinatura (parâmetros e retorno), seguida da declaração de
um tipo função com a mesma assinatura. Assim variáveis do tipo função podem assumir o valor de qualquer uma
das 3 funções definidas anteriormente.

Página 16
Especificação do Portugol v1.0, julho de 2009

7 Tipo abstratos de dados (TAD)


Tipo abstrato de dados (TAD) é a representação do modelo matemático que envolve a definição
de uma estrutura heterogênea juntamente com suas operações de manipulação. Os campos da
estrutura são encapsulados, ou seja, somente serão acessı́veis via operações de manipulação defi-
nidas pelas operações da TAD. O Quadro 18 apresenta a definição de uma TAD correspondente
a uma pilha de inteiros.
Alternativamente, é possı́vel definir uma TAD separando-se a interface de sua implemen-
tação. Esta é uma prática comum em linguagens de programação por permitir uma melhor
organização do código. Assim, passamos a ter três elementos separados4 : a interface da TAD, a
implementação da TAD e o uso da TAD pelo código cliente. O Quadro 19 apresenta a mesma
TAD PilhaInt definida anteriormente, porém com a separação entre interface e implementação,
acrescida do código cliente. Vale a pena ressaltar que a forma de uso pelo cliente de uma TAD de-
clarada desta forma permanece a mesma, ou seja, o mesmo código cliente do Quadro 18 poderia
ser utilizado.

4
Normalmente estes elementos, quando traduzidos para uma linguagem de programação, são armazenados em
arquivos separados.

Página 17
Especificação do Portugol v1.0, julho de 2009

# declaraç~ao de uma TAD correspondente a uma pilha de inteiros


tad PilhaInt
cte SIZE: inteiro ≡ 50 # tamanho máximo da pilha
var top: inteiro # controlador do topo da pilha
var storage: arranjo de SIZE inteiro # área de armazenamento
# construtor é chamado automaticamente quando uma inst^ancia é criada.
construtor ()
top ← 0
fim
função push(val: inteiro): booleano
se top < SIZE então
storage[top] ← val
top ← top + 1
retorna verdadeiro
senão
retorna falso
fim
fim
função pop(val: ref inteiro): booleano
se top > 0 então
top ← top − 1
[val] ← storage[top]
retorna verdadeiro
fim
retorna falso
fim
fim
# aqui começa o código cliente (em azul) que usa o TAD recém criado.
programa
var c : inteiro
var pilha: PilhaInt
# insere elementos da pilha, nestas ordem: 5,8,2,1
pilha.pop(5)
pilha.pop(8)
pilha.pop(2)
pilha.pop(1)
# escreve os elementos da pilha, na ordem inversa que foram inseridos: 1,2,8,5
para i de 0 até pilha.SIZE − 1 faça
pilha.pop(c)
escreva (c)
fim
fim

Quadro 17: Declaração de tipo TAD, sua instanciação e utilização.

Página 18
Especificação do Portugol v1.0, julho de 2009

# 1o parte: declaraç~ao da interface da TAD correspondente a uma pilha de inteiros


tad PilhaInt
cte SIZE: inteiro ≡ 50 # tamanho máximo da pilha
var top: inteiro # controlador do topo da pilha
var storage: arranjo de SIZE inteiro # área de armazenamento
# apenas declarando o construtor.
construtor ≡ função ( )
# similarmente, declaramos apena a assinatura dos métodos push e pop.
push ≡ função (inteiro) −→ booleano
pop ≡ função (ref inteiro) −→ booleano
# é possı́vel misturar os dois estilos de declaraç~ao, sem problemas.
função isEmpty( ): booleano
retorna top = 0
fim
fim
# 2o parte: implementaç~ao da TAD pilha de inteiros.
# Note o uso do operador ‘.’ associado ao nome da TAD para indicar que a funç~ao
# push faz parte da TAD PilhaInt e, portanto, tem acesso aos seus campos internos.
função PilhaInt.push(val: inteiro): booleano
se top < SIZE então
storage[top] ← val
top ← top + 1
retorna verdadeiro
senão
retorna falso
fim
fim
# a ordem de implementaç~ao n~ao precisa seguir a de declaraç~ao.
PilhaInt. construtor ()
top ← 0
fim
função PilhaInt.pop(val: ref inteiro): booleano
se top > 0 então
top ← top − 1
[val] ← storage[top]
retorna verdadeiro
fim
retorna falso
fim
# 3o parte: uso da TAD pilha de inteiros.
programa # assumindo que uma pilha foi criada e preenchida e a variável c declarada.
..
.
enquanto não pilha.isEmpty() faça # escreve os elementos da pilha.
pilha.pop(c)
escreva (c)
fim
fim

Quadro 18: Declaração de tipo TAD, com interface separada da implementação e do código cliente.
Página 19
Especificação do Portugol v1.0, julho de 2009

8 Exemplos
Nesta seção apresentaremos alguns exemplos de algoritmos completos, ilustrando o máximo
possı́vel de estruturas de controle através de exemplos apresentados na disciplina de Algoritmos
e Estruturas de Dados I.
Os exemplos aqui exibidos possuem uma diferença sutil para os exemplos apresentados em
seções anteriores. Utilizando-se de uma caracterı́stica de configuração do pacote algorithm2e,
é possı́vel substituir a palavra fim que marca o fim de um bloco de comandos por uma linha
simples. Com isso, obtém-se um código mais compacto, ideal, por exemplo, para slides de aula.

8.1 Algoritmo de ordenação por flutuação


O exemplo apresentado no Quadro 20 consiste na implementação do algoritmo de ordenação
por flutuação, conhecido também como bubble sort. Os valores a serem dispostos e ordem não
decrescente são números inteiros, armazenados em uma lista encadeada simples.

Página 20
Especificação do Portugol v1.0, julho de 2009

# Estrutura de nó de uma lista encadeada simples.


1 tipo NoSLista ≡ estrutura
2 data: inteiro # campo para armazenamento do número inteiro
3 link: ref NoSLista # apontador para o próximo elemento da lista
4 fim
Entrada: Lista L com elementos em uma ordem qualquer
Saı́da: Lista L com elementos em ordem não decrescente
5 função bubbleSort(L: ref NoSLista): ref NoSLista
6 var p0, p1, p2: ref NoSLista # Ponteiros usado para realizar as trocas
7 var swapped : booleano ← falso # flag p/ indicar ocorr^encia de troca
8 se L = ⊥ então # 1: lista vazia, nada a fazer
9 retorna L # retornar lista original
10 senão se @{L}.prox = ⊥ então # 2: lista com 1 elemento, nada a fazer
11 retorna L # retornar lista original
12 senão
# 3: demais casos - lista com 2 ou mais elementos
13 repita # percorrer novamente se houve alguma troca
14 p2 ← L # ponteiro de trabalho: inicia no 1o da lista
15 p0 ← p1 ← ⊥ # apontadores auxiliares: iniciam fora da lista
16 swapped ← falso # ‘desligar’ flag indicativo de troca
17 repita # repetir até final da lista
18 p0 ← p1 # avançar o anterior do anterior do ponteiro de trabalho
19 p1 ← p2 # avançar o anterior do ponteiro de trabalho
20 p2 ← @{p2}.prox # avançar o ponteiro de trabalho
21 se @{p1}.data > @{p2}.data então # testar se é preciso trocar
22 se p0 = ⊥ então
23 L ← p2 # caso especial: trocando 1o com 2o
24 senão
25 @{p0}.pro ← p2 # caso normal: bypass o maior do par
26 @{p1}.prox ← @{p2}.prox # bypass o menor, 2a fase da troca
27 @{p2}.prox ← p1 # fim: menor aponta p/ maior, concluindo troca
28 swapped ← verdadeiro # indicar que houve uma troca
29 p1 ← p2 # consertar ponteiros trabalho após troca: p0,p2,p1
30 p2 ← @{p2}.prox # ordem restabelecida: p0,p1,p2
31 até @{p2}.prox = ⊥
32 até não swapped
33 retorna L # retornar a lista em ordem n~ao decrescente
34 fim

Quadro 19: Exemplo de algoritmo que realiza a ordenação por flutuação (bubble sort) sobre uma lista encadeada
simples. Os elementos a serem ordenados são inteiros e serão dispostos em ordem não decrescente pelo algoritmo.

Página 21
Especificação do Portugol v1.0, julho de 2009

8.2 Algoritmo identificar caminho raiz-para-folha de uma árvore ternária


Definimos um “caminho raiz-para-folha” como sendo a seqüência de nós de uma árvore consi-
derados a partir do nó raiz até uma das folhas da árvore. Assume-se que uma árvore vazia
não contém nenhum caminho raiz-para-folha. Portanto, para a árvore ternária abaixo, existem
exatamente seis caminhos raiz-para-folha:

5
/ | \
4 7 8
/ | / | \
11 13 4 6 21
/ \ \ /
7 2 1 3

Os caminhos raiz-para-folha são:

caminho 1: 5 4 11 7
caminho 2: 5 4 11 2
caminho 3: 5 7 13
caminho 4: 5 8 4 1
caminho 5: 5 8 6
caminho 6: 5 8 21 3

Forneça a estrutura correspondente ao nó de uma árvore ternária e escreva a função impri-
meCaminhos() que, dado uma árvore ternária, imprime todos seus caminhos raiz-para-folha, um
por linha. O Quadro 21 apresenta uma solução para este problema.

Página 22
Especificação do Portugol v1.0, julho de 2009

# Estrutura de nó de uma árvore ternária.


1 tipo NoAT ≡ estrutura
2 dado : inteiro # tipo de dado armazenado no nó de árvore
3 lef t : ref NoAT # apontador para subárvore esquerda
4 middle : ref NoAT # apontador para subárvore do meio
5 right : ref NoAT # apontador para subárvore direita
6 fim
7 tipo VetPath ≡ arranjo de M AX IN T inteiro # novo tipo arranjo
Entrada: Árvore ternária de inteiros
Saı́da: Impressão de todos os caminhos internos da árvore ternária
8 procedimento imprimeCaminhos(root: ref NoAT)
9 var path : VetPath ; # armazena o caminho
# par^ametros: raiz, o caminho, seu tamanho atual e o ident. de caminhos gerados
10 imprimeCaminhosRec( root, path, 1, 1 )
11 fim
# importante: path é passado por valor
12 procedimento imprimeCaminhosRec( root: ref NoAT; path: VetPath; pathLen:
inteiro; pathId: inteiro)
13 se root = ⊥ então # caso base, nada a fazer
14 retorna
15 path[ pathLen ] ← @{root}.data; # adicionar este nó ao caminho
16 pathLen ← pathLen + 1; # aumentar o tamanho do caminho
17 se @{root}.lef t = ⊥ e @{root}.right = ⊥ então # é folha...
18 exibeCaminho(path, pathLen, pathId); # ... imprimimos caminho até aqui
19 pathId ← pathId + 1; # incrementamos o contador de caminhos gerados
20 senão
# caso contrário, descer recursivamente pelas subárvores
21 imprimeCaminhosRec(@{root}.lef t, path, pathLen); # esquerda, ...
22 imprimeCaminhosRec(@{root}.middle, path, pathLen); # do meio e ...
23 imprimeCaminhosRec(@{root}.right, path, pathLen); # direita.
24 fim
25 procedimento exibeCaminho(path: VetPath; pathLen: inteiro; pathId: inteiro)
26 var i: inteiro
27 escreva ( “Caminho ”, pathId , “: ” )
28 para i ← 1 até pathLen faça # percorrer o vetor e imprimir
29 escreva ( path[ i ], “ ” ); # imprime caminho separado por espaço
30 escreva ( “\n” ); # salta uma linha
31 fim

Quadro 20: Algoritmo que identifica os caminhos para raiz de uma árvore ternária.

Página 23

Das könnte Ihnen auch gefallen