Académique Documents
Professionnel Documents
Culture Documents
CAMPUS UNIVERSITARIO
DE PALMAS
CURSO DE CIENCIA
DA COMPUTAC
AO
- NOTAS DE AULA
LINGUAGENS DE PROGRAMAC
AO
Tiago Almeida
Palmas
Marco de 2016
Sum
ario
1 Introduc
ao
1.1 A arte do projeto de linguagens . . . . . . .
1.2 Spectrum das linguagens de programacao . .
1.3 Por que estudar linguagens de programacao?
1.4 Por que estudar linguagens de programacao?
1.5 Compilacao e interpretacao . . . . . . . . .
1.6 Uma revisao sobre a compilacao . . . . . . .
1.6.1 Fase de Analise . . . . . . . . . . . .
1.6.2 Fase Sntese . . . . . . . . . . . . . .
1.7 Definicoes basicas . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
1
4
4
6
8
8
10
11
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
26
26
29
33
33
4 An
alise sem
antica
34
4.1 Regras de analise semantica . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.2 Gramatica de atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.3 Avaliacao de atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5 Fluxo de controle
5.1 Fluxo de controle . . . . . . . . . . .
5.2 Avaliacao de expressoes . . . . . . . .
5.3 Fluxo estruturado e nao estruturado
5.4 Sequencia . . . . . . . . . . . . . . .
5.5 Selecao . . . . . . . . . . . . . . . . .
5.6 Iteracao . . . . . . . . . . . . . . . .
5.7 Nao determinancia . . . . . . . . . .
6 Tipos de dados
6.1 Tipos de dados . . . . . . . .
6.2 Checagem de tipos . . . . . .
6.3 Layout da memoria . . . . . .
6.4 Dimensoes, limites e Alocacao
.
.
.
.
.
.
.
.
.
.
.
.
ii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
42
42
43
49
51
51
54
57
.
.
.
.
58
58
58
61
63
7 Abstraco
es: Subtorinas e Controle
7.1 Sequencia de chamadas . . . . . .
7.2 Passagem de parametros . . . . .
7.3 Subrotinas e modulos . . . . . . .
7.4 Manipulacao de excecao . . . . .
7.5 Co-rotinas . . . . . . . . . . . . .
7.6 Eventos . . . . . . . . . . . . . .
da
. .
. .
. .
. .
. .
. .
8 Abstrac
ao de dados e orientac
ao `
a
8.1 Programacao orientada `a objetos
8.2 Encapsulamento e heranca . . . .
8.3 Inicializacao e finalizacao . . . . .
8.4 Metodo de vinculacao dinamica .
8.5 Heranca m
ultipla . . . . . . . . .
8.6 Revisao da orientada a` objeto . .
objetos
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
9 Linguagens funcionais
9.1 Linguagens funcionais . . .
9.2 -expressoes . . . . . . . . .
9.3 Linguagem Haskell . . . . .
9.4 Funcoes . . . . . . . . . . .
9.5 Tipos de dados . . . . . . .
9.6 Metodologia de programacao
9.7 O tipo Lista . . . . . . . . .
9.8 Classes de tipos . . . . . . .
9.9 Tipos algebricos . . . . . . .
10 Linguagens l
ogicas
10.1 Linguagens logicas . . .
10.2 Linguagem Prolog . . . .
10.3 Fatos, regras e consultas
10.4 Listas . . . . . . . . . .
10.5 Aritmetica . . . . . . . .
10.6 Corte de fluxo . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Refer
encias Bibliogr
aficas
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Abstrac
ao
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
67
67
69
72
74
76
77
.
.
.
.
.
.
79
79
82
84
85
87
88
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
89
89
91
94
96
97
100
103
110
112
.
.
.
.
.
.
115
. 115
. 116
. 121
. 129
. 132
. 134
.
.
.
.
.
.
.
.
.
.
.
.
137
iii
1 Introdu
c
ao
1.1
1.2
Muitas lnguas existentes podem ser classificados em famlias com base em seu modelo
de computacao. A Figura ?? mostra um conjunto comum de famlias. A divisao de
nvel superior distingue entre as linguagens declarativas, em que o foco e sobre o que o
computador esta a fazer, e as linguagens imperativas, em que o foco e sobre a forma como
o computador deve faze-lo.
Declarativas
Funcional
Fluxo de Dados
Logica, baseada em restricoes
Baseada em Template
Imperativas
Von Neumann
Script
Orientada `a objetos
Figura 1.1
linguagens declarativas sao de alguma nvel mais elevadosentido; eles estao mais em
sintonia com o ponto de vista do programador, e menos com o ponto de vista do implementador. linguagens imperativas predominam, no entanto, principalmente por razoes de
desempenho. Ha uma tensao no projeto de linguagens declarativas entre o desejo de ficar
longe de irrelevantesdetalhes de implementacao, bem como a necessidade de permanecer
perto o suficiente para os detalhes para, pelo menos, controlar o esboco de um algoritmo.
O projeto de algoritmos eficientes, afinal, e o que grande parte da ciencia da computacao e
sobre. Ainda nao esta claro ate que ponto e em que problema domnios, podemos esperar
que os compiladores descobrir bons algoritmos para problemas considerados de muito alto
nvel de abstracao. Em qualquer domnio no qual o compilador nao consegue encontrar
um bom algoritmo, o programador precisa ser capaz de especificar uma forma explcita.
Dentro das famlias declarativas e imperativas, existem varios importantes
linguagens funcionais empregar um modelo computacional baseado na definicao recursiva de funcoes. Eles tomam a sua inspiracao a partir do calculo lambda, um
modelo computacional formal, desenvolvido por Alonzo Church em 1930. Em essencia, um programa e considerado uma funcao de entradas e sadas, definida em
termos de funcoes mais simples atraves de um processo de refinamento. Lnguas
nesta categoria incluem Lisp, ML e Haskell.
Dataflow computacao modelo lnguas como o fluxo de informacao (fichas) entre os
nos funcionais primitivas. Eles fornecem um modelo inerentemente paralelo: nodos
sao desencadeados pela chegada de sinais de entrada, e pode operar em simultaneo.
Id e Val sao exemplos de linguagens de fluxo de dados. Sisal, um descendente de
Val, e mais frequentemente descrito como uma linguagem funcional.
lnguas Logic- ou constrangimento a` base de tomar a sua inspiracao de logica de
predicados. Eles modelo computacional como uma tentativa para encontrar valores
2
1.3
1.4
Simular caractersticas u
teis em linguagens que nao as possuem. Em dialetos mais
antigos do Fortran, por exemplo, programadores familiarizados com construcoes
modernas de controle pode usar comentarios e auto-disciplina para escrever codigo
bem estruturado. Em linguagens sem constantes nomeadas ou tipos de enumeracao,
variaveis que sao inicializadas uma vez e nunca mudam podem tornar o codigo muito
mais legvel e de facil manutencao.
Fazer melhor uso da tecnologia da linguagem onde quer que apareca. A maioria dos
programadores nunca vai projetar ou implementar uma linguagem de programacao
convencional, mas a maioria tera a tecnologia da linguagem para outras tarefas
de programacao. Um computador pessoal tpico contem arquivos em dezenas de
formatos estruturados, abrangendo o conte
udo da web, processamento de texto,
planilhas, apresentacoes, vetores graficos, m
usica, vdeo, bancos de dados e uma
grande variedade de outros domnios de aplicacao.
4
Caracterstica
Legibilidade
Criterios
Capacidade de Escrita
Confiabilidade
Simplicidade/ortogonalidade
Estruturas de Controle
Tipos de dados e estruturas
Projeto da sintaxe
Suporte para abstrac
ao
Expressividade
Verificac
ao de tipos
Manipulac
ao de excec
oes
Apelido (aliasing) restrito
Legibilidade
As linguagens devem possuir u
nica e exclusivamente elementos de facil entendimento e nao ambguos.
Simplicidade e ortogonalidade a linguagem deve prover um n
umero reduzido
de elementos basicos. A linguagem deve conter um n
umero mnimo de primitivas que possam ser combinadas.
Instrucoes de controle que nao comprometam a clareza dos programas as
instrucoes de controle devem ser reduzidas ao conjunto estritamente essencial
para o controle de fluxo de execucao dos programas.
Facilidade para representacao de tipos e estruturas de dados - a linguagem deve
fornecer facilidades para a representacao de dados comuns para a resolucao de
problemas.
Sintaxe limpa e concisa cada instrucao deve representar de forma u
nica e
intuitiva o seu significado.
Capacidade de escrita
Programas facilmente entendidos sao provenientes da facilidade de escrita fornecidas pela linguagem.
O suporte a abstracoes podem devem ser fornecidos para facilitar a programacao.
Funcoes em linguagens imperativas sao formas abstratas de representar solucoes
nas quais os parametros abstraem os dados reais a serem manipulados.
Confiabilidade
Espera-se que solucoes dadas aos problemas sejam confiaveis.
desejavel que os programadores nao facam operacoes com tipos conflitantes.
E
A manipulacoes de excecoes tem sido cada vez mais utilizadas nas linguagens
de programacao atuais.
Custo
O custo associado a uma linguagem vai desde o custo de desenvolvimento ate
sua amortizacao proveniente da aceitacao no mercado e os custos relativos ao
seu uso.
Linguagens legveis, de facil escrita e confiaveis tendem a ter um custo mais
baixo de treinamento e sao mais aceitas no mercado.
1.5
Compilac
ao e interpreta
c
ao
Compilaldor
Entrada
Programa Alvo
Sada
Figura 1.2
Programa Fonte
Interpretador
Sada
Entrada
Figura 1.3
Programa Fonte
Tradutor
Programa
Intermediario
Maquina Virtual
Sada
Entrada
Figura 1.4
acontece por padrao no Java. Nos ainda dizer que uma lngua e compilado se o tradutor analisa-lo completamente (ao inves de efetuar alguma transformacao mecanica), e
se o programa intermediario nao tem uma forte semelhanca com a fonte. Essas duas
caractersticas-minuciosa analise e transformacao nontrivial-sao as marcas de compilacao.
1.6
Uma revis
ao sobre a compila
c
ao
A partir do diagrama na proxima pagina, voce pode ver, existem duas principais fases
do processo de compilacao: analise e sntese. A etapa de analise rompe-se o programa
de origem em pedacos, e cria um (linguagem independente) representacao intermediaria
generico do programa. Em seguida, a fase de sntese constroi o programa alvo desejado
a partir da representacao intermedia. Normalmente, uma fase de analise compilador e
chamado de sua extremidade dianteira ea sntese palco para a sua extremidade traseira.
Cada uma das fases e dividido em um conjunto de fases que lidam com diferentes partes
das tarefas. (Por que voce acha que compiladores tpicos separar o processo de compilacao
em frente e back-end fases?)
1.6.1
Fase de An
alise
Programa Fonte
Analise Lexica
Analise Sintatica
Analise
Analise Semantica
Geracao de Codigo
Intermediario
C
odigo Intermediario
Tabela de Smbolos
Otimizacao de C.I.
Sntese
Geracao de
C
odigo Objeto
Otimizacao de C.O.
Programa Alvo
Figura 1.5
1.6.2
Fase Sntese
10
A fase de otimizacao pode realmente abrandar um compilador, por isso a maioria dos
compiladores permitir que esse recurso a ser suprimido ou desativado por padrao. O
compilador pode ate ter graos pequenos controles que permitem que o desenvolvedor
para fazer a melhor combinacao entre o tempo gasto a compilacao versus qualidade
de otimizacao.
No exemplo mostrado acima, o otimizador foi capaz de eliminar uma adicao a`
zero e uma reavaliacao da mesma expressao, permitindo que o original cinco TAC
declaracoes de ser reescrita em apenas tres declaracoes e usar menos duas variaveis
temporarias.
2. Objeto Geracao de Codigo: Este e o lugar onde o programa de destino e gerado. A
sada desta fase e geralmente o codigo de maquina ou codigo de montagem. Locais de
memoria sao selecionados para cada variavel. As instrucoes sao escolhidos para cada
operacao. O codigo de tres endereco e convertido em uma seq
uencia de montagem
ou de linguagem de maquina instrucoes que executam as mesmas tarefas.
3. Objeto Optimization cupom: Tambem pode haver uma outra passagem de otimizacao que se segue a geracao de codigo, desta vez transformando o codigo objeto em
mais apertado, codigo objeto mais eficiente. Este e o lugar onde nos consideramos
caractersticas do proprio hardware para fazer uso eficiente do processador (s) e registradores. O compilador pode tirar proveito de linguagens especficas de maquina
(instrucoes especializadas, pipelining, previsao de desvios, e outras otimizacoes peephole) na organizacao e racionalizacao do proprio codigo de objeto. Tal como acontece
com otimizacao IR, esta fase do compilador geralmente e configuravel ou pode ser
ignorada por completo.
1.7
Definic
oes b
asicas
Nao-numericos
Uma variavel b booleana e uma c caractere sao representadas por:
1 var b : Boolean ;
2
c : Character ;
Nao-numericos
Na linguagem Pascal, um apontador para um valor inteiro pode ser definido como:
1 var in tP : Integer ;
Enumerados
Em Pascal, por exemplo, podemos criar um tipo (conjunto de valores) para representar os meses do ano:
1 type MesesP = ( jane , f e v e , marc , a b r i , maio , junh , j u l h , agos , s e t e
, outu , nove , d e z e ) ;
12
Produto cartesiano
O produto cartesiano do conjunto S pelo conjunto T e definido por
C = S T = {(x, y)|x S, y T }
(1.1)
O n
umero de elementos (cardinalidade) do conjunto C, denotado por #C corresponde a multiplicidade da cardinalidade de S pela de T
#C = #S #T
(1.2)
Produto cartesiano
Em Pascal:
1 type DataP = record
2
m: MesesP
3
d : DiasP
4
end ;
Em C:
1 struct DataC {
2
MesesC m
3
DiasC d
4
};
Uniao Disjunta
A uniao disjunta dos conjuntos S e T , denotada por S + T , e definida por
(1.3)
#C = #S + #T
Uniao disjunta
Em Pascal:
13
(1.4)
Em C:
1 union NumeroC {
2
int i v a l ;
3
float rval ;
4
};
Mapeamentos
O mapeamento do conjunto S para o conjunto T , denotado por S T , e definido
m : S T = {m|x S m(x) T }
(1.5)
(1.6)
Em Pacal:
1 var mapintP : array [ 0 . . 1 5 ] of Integer ;
Em C:
1 int mapintC [ 1 6 ] ;
Conjuntos potencia
O conjunto potencia de um dado conjunto S, denotado por P(S), e definido como
segue:
P(S) = {s|s S}
Ou seja, todos os subconjuntos podem ser formados pelas elementos de S
14
(1.7)
#(P(S)) = 2#S
(1.8)
Uma lista deve ser vista como um tipo recursivo e nao como um mapeamento.
Um tipo lista de inteiros pode ser definida pela sua unidade base, a lista vazia, juntamente com um inteiro seguido de uma lista de inteiros
IntList = U nit + (Integer IntList)
(1.9)
(1.10)
Desmembrando em:
nil
(1.11)
(1.12)
(1.13)
Em Pascal:
1 type I n t L i s t P = IntNode ;
2 IntNode = record v a l o r : Integer ;
3
prox : I n t L i s t P
4
end ;
Em C:
1 typedef struct N o I n t L i s t I n t L i s t C ;
2 struct N o I n t L i s t {
3
int v a l o r ;
4
I n t L i s t C prox ;
5
};
15
number integer|real
integer digit digit
real integer exponent|decimal(exponent|)
decimal digit (. digit|digit .)digit
exponent (e|E)(+||)integer
digit 0|1|2|3|4|5|6|7|8|9
16
As expressoes regulares funcionam para definir tokens. Eles sao incapazes, no entanto,
de especificar construcoes aninhadas, que sao centrais para as linguagens de programacao.
Considere a expressao de exemplo para estrutura de uma expressao aritmetica:
expr
expr
op
expr
id
id
op
expr
id
expr
expr
op
id
expr
expr
op
expr
id
id
17
expr
2.2
expr
addop
term
term
f actor
f actor
number(3)
number(4)
number(5)
Vinculac
oes
Existem:
As variaveis que tem sua alocacao antes que se inicie a execucao dos blocos de
comando dos programas: globais e locais;
As variaveis que sao criadas em tempo de execucao: heap;
As variaveis que existem mesmo depois da execucao: persistente.
A vinculacao e uma associacao entre duas coisas, como um nome e a coisa que com
p nome. Tempo de vinculacao e o momento em que e criada uma vinculacao ou, mais
geralmente, o momento em que e feita qualquer decisao de implementacao (podemos
pensar nisso como ligar a uma resposta a uma pergunta). Mais sucintamente:
Uma caracterstica importante das variaveis esta relacionada a ativacao da variavel
ao longo da execucao do programa.
Quando os valores das variaveis podem ser acessados.
Uma variavel existe desde o momento em que e associada a uma celula de memoria (alocacao) e termina a sua existencia quando o dado espaco e disponibilizado
(deslocacao)
Existem diferentes tempos em que as decisoes de vinculacao podem ser tomadas:
Tempo de projeto da linguagem: Na maioria das linguagens, as construcoes de
controle de fluxo, o conjunto de tipos fundamentais (primitivos), os construtores
disponveis para a criacao de tipos complexos, e muitos outros aspectos da semantica da linguagem sao escolhidos quando linguagem e projetada.
18
5. Destruicao de vinculacoes
6. Destruicao de objetos
Ciclo de vida dos objetos geralmente correspondem a um dos tres mecanismos de
alocacao e armazenamento principais, usados para gerenciar o espaco do objeto:
1. Objetos estaticos sao dados um endereco absoluto que e mantida ao longo da execucao do programa.
2. Objetos pilha sao alocados e desalocados como u
ltimo a entrar, primeiro a sair,
geralmente em conjunto com as chamadas de sub-rotinas e retornos.
3. Objetos Heap pode ser alocada e desalocada de maneira arbitrarias. Eles exigem
um algoritmo mais geral (e caro) de gerenciamento de armazenamento.
1
2
3
4
5
6
7
8
9
10
11
var r : Integer ;
procedure P ;
var v : Integer ;
begin
r := @v ;
end ;
begin
P;
r := 1 ;
end
Objetos alocados estaticamente sao valores que nao devem mudar durante a execucao
do programa, e sao muitas vezes alocados de maneira protegida, memoria so de leitura,
de modo que qualquer tentativa inadvertida para escrever nesses espacos ira causar uma
interrupcao de processador, permitindo que o sistema operacional anuncie um erro de
tempo de execucao. Exemplos:
Constants;
Variaveis globais;
Instrucoes de controle
Junto com as variaveis locais e constantes de tempo de elaboracao, o compilador normalmente armazena uma variedade de outras informacoes associadas com a sub-rotina,
incluindo:
Argumentos e valores de retorno. Compiladores modernos mantem essas informacoes
em registros, sempre que possvel, mas a`s vezes espaco na memoria e necessario.
20
Temporarios. Estes sao geralmente valores intermedios produzidos em calculos complexos. Mais uma vez, um bom compilador ira mante-los em registros sempre que
possvel.
Informacoes de contabilidade. Isso pode incluir os endereco de retorno de subrotinas, uma referencia ao quadro de pilha do chamador (tambem chamada de vnculo dinamico), informacoes de depuracao, e outros valores.
A heap e uma regiao de armazenamento no qual sub blocos podem ser alocados e
desalocados de maneira arbitrarias.
Heaps sao necessarios para as pecas alocada dinamicamente de estruturas de dados
ligados, e por objetos como strings totalmente gerais de caracteres, listas e conjuntos, cujo
tamanho pode mudar como resultado de uma instrucao de atribuicao ou outra operacao
de atualizacao.
22
1
2
3
4
5
6
7
const N = 1 0 ;
...
procedure f o o ;
const
M = N; // s t a t i c s e m a n t i c e r r o r !
...
N = 2 0 ; // l o c a l c o n s t a n t d e c l a r a t i o n ; h i d e s t h e o u t e r N
1
2
3
4
5
6
7
8
const N = 1 0 ;
...
procedure f o o ;
const
M = N; // s t a t i c s e m a n t i c e r r o r !
var
A : array [ 1 . .M] of integer ;
N : r e a l ; // h i d i n g d e c l a r a t i o n
1 class A {
2
const int N = 1 0 ;
3
void f o o ( ) {
4
const int M = N; // u s e s i n n e r N b e f o r e i t i s d e c l a r e d
5
const int N = 2 0 ;
1
2
3
4
5
6
7
8
9
10
struct manager ; // d e c l a r a t i o n o n l y
struct employee {
struct manager b o s s ;
struct employee n e x t e m p l o y e e ;
...
};
struct manager { // d e f i n i t i o n
struct employee f i r s t e m p l o y e e ;
...
};
1
2
3
4
5
6
7
8
9
10
11
12
void l i s t t a i l ( f o l l o w s e t f s ) ; // d e c l a r a t i o n o n l y
void l i s t ( f o l l o w s e t f s )
{
switch ( i n p u t t o k e n ) {
case i d : match ( i d ) ; l i s t t a i l ( f s ) ;
...
}
void l i s t t a i l ( f o l l o w s e t f s ) // d e f i n i t i o n
{
switch ( i n p u t t o k e n ) {
case comma : match (comma) ; l i s t ( f s ) ;
...
23
13 }
n : integer // g l o b a l d e c l a r a t i o n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
program main ;
var x : integer ;
procedure sub1 ;
var x : integer ;
begin
...x...;
end ;
// sub1
procedure sub2 ;
begin
sub1 ;
...x...;
end ;
// sub2
begin
...
end .
// main
procedure f i r s t
n := 1
procedure s e c o n d
n : integer // l o c a l d e c l a r a t i o n
f i r s t ()
n := 2
i f read integer ( ) > 0
second ( )
else
f i r s t ()
write integer ( n )
1 $var = G l o b a l ;
2 sub i n n e r {
24
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
print i n n e r :
$var \n ;
}
sub c h a n g e l o c a l {
my $var = L o c a l ;
print c h a n g e l o c a l :
$var \n ;
&i n n e r
}
sub changedynamic {
l o c a l $var = Dynamic ;
print changedynamic : $var \n ;
&i n n e r
}
&i n n e r
&c h a n g e l o c a l
&changedynamic
25
double sum , s u m o f s q u a r e s ;
...
void accumulate ( double& x ) // x i s p a s s e d by r e f e r e n c e
{
sum += x ;
s u m o f s q u a r e s += x x ;
}
...
accumulate ( sum ) ;
Este tipo de erro foi uma das principais motivacoes para fazer sub-rotinas com
escopo fechado.
A sobrecarga e o uso de um mesmo identificador para operacoes diferentes, ou seja,
um mesmo identificador pode denotar comportamento distintos.
Sobrecarga independente de contexto: a abstracao a ser aplicada depende
dos tipos dos argumentos, os quais devem corresponder aos parametros da
abstracao.
1
2
3
4
5
6
int a , b ;
float x ;
...
x = a / b;
...
x = ( float ) a / ( float ) b ;
Listing 3.2: C
27
struct complex {
double r e a l , i m a g i n a r y ;
};
enum b a s e { dec , bin , oct , hex } ;
int i ;
complex x ;
void print num ( int n ) { . . .
void print num ( int n , b a s e b ) { . . .
void print num ( complex c ) { . . .
print num ( i ) ; // u s e s t h e f i r s t f u n c t i o n a bo v e
print num ( i , hex ) ; // u s e s t h e second f u n c t i o n a bo ve
print num ( x ) ; // u s e s t h e t h i r d f u n c t i o n a bo ve
Monomorfismo:
<2-> Uma linguagem tem um sistema de tipos monomorficos quando cada elemento
declarado na linguagem possui um u
nico tipo.
<3-> A maioria das linguagens imperativas possui um sistema de tipos monomorficos.
Polimorfismo:
<4-> Em um sistema de tipos polimorficos, as abstracoes operam de maneira uniforme sobre argumentos de famlias de tipos relacionados.
<5-> O polimorfismo pode ser aplicado a diferentes tipos de abstracoes das linguagens. Podemos ter o polimorfismo sobre abstracoes de processos (funcoes genericas),
polimorfismo sobre tipos (tipos parametrizados ou politipos).
Monomorfismo:
1 type s t a c k i n t = i n t n o d e ;
2 i n t n o d e = record
3
elem : integer ;
4
next : s t a c k i n t ;
5
end ;
28
6
7 type s t a c k c h a r = charnode ;
8 charnode = record
9
elem : char ;
10
next : s t a c k c h a r ;
11
end ;
Polimorfismo:
1 set of r e a l
2 array [ 0 . . 5 ] of Integer
Polimorfismo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
generic
type T i s private ;
with function <( x , y : T) return Boolean ;
function min ( x , y : T) return T ;
function min ( x , y : T) return T i s
begin
i f x < y then return x ;
e l s e return y ;
end i f ;
end min ;
function s t r i n g m i n i s new min ( s t r i n g , <) ;
function date min i s new min ( date , d a t e p r e c e d e s ) ;
3.2
A vinculac
ao do ambiente de referenciamento
Uma questao adicional que ainda nao tenha considerado surge em linguagens que
permitem criar uma referencia a uma sub-rotina, por exemplo, passando-a como um
parametro.
29
Quando alguma regra de escopo deveria ser aplicada a essa tal sub-rotina: quando
a referencia e criado pela primeira vez, ou quando a rotina e finalmente chamada?
A resposta e particularmente importante para as linguagens com escopo dinamico,
embora veremos que importa mesmo em linguagens com escopo estatico.
Esta ligacao tardia do ambiente de referenciamento de uma sub-rotina que foi passado como um parametro e conhecido como vinculac
ao superficial (shallow).
geralmente o padrao em linguagens com escopo dinamico.
E
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
type p e r s o n = record
. . .
age : integer
. . .
t h r e s h o l d : integer
people : database
function o l d e r than t h r e s h o l d ( p : p e r s o n ) : boolean
r e t u r n p . age >= t h r e s h o l d
procedure p r i n t p e r s o n ( p : p e r s o n )
// C a l l a p p r o p r i a t e I /O r o u t i n e s to p r i n t record on s t a n d a r d output .
// Make u s e of n o n l o c a l v a r i a b l e l i n e l e n g t h to format data in
columns .
. . .
procedure p r i n t s e l e c t e d r e c o r d s ( db : d a t a b a s e ; p r e d i c a t e , p r i n t r o u t i n e
: procedure )
l i n e l e n g t h : integer
i f d e v i c e type ( s t d o u t ) = t e r m i n a l
l i n e l e n g t h := 80
e l s e // Standard output i s a f i l e or p r i n t e r .
l i n e l e n g t h := 132
f o r e a c h record r in db
// I t e r a t i n g o v e r t h e s e may a c t u a l l y be
// a l o t more c o m p l i c a t e d than a f o r l o o p .
if predicate ( r )
print routine ( r )
// main program
. . .
t h r e s h o l d := 35
p r i n t s e l e c t e d r e c o r d s ( p e o p l e , o l d e r than t h r e s h o l d , p r i n t p e r s o n )
Vinculac
ao profunda (deep) e implementada atraves da criacao de uma representacao explcita de um ambiente de referencia (geralmente aquela em que a
sub-rotina porderia executar se chamada no momento presente) e juntamente com
uma referencia para a sub-rotina.
30
31
19 end .
i n t e r f a c e IntFunc {
public int c a l l ( int i ) ;
}
c l a s s PlusX implements IntFunc {
f i n a l int x ;
PlusX ( int n ) { x = n ; }
public int c a l l ( int i ) { return i + x ; }
}
...
IntFunc f = new PlusX ( 2 ) ;
System . out . p r i n t l n ( f . c a l l ( 3 ) ) ; // p r i n t s 5
32
3.3
Expans
ao de macros
Antes do desenvolvimento de linguagens de programacao de alto nvel, os programadores de linguagem de montagem poderiam encontrar-se escrevendo codigo altamente repetitivo.
Para aliviar a carga, muitas montadores forneceram sofisticadas implementacoes de
expans
ao de macros.
Considere a tarefa de carregar um elemento de uma matriz bidimensional da memoria
em um registrador. Esta operacao pode facilmente exigir uma meia d
uzia de instrucoes, com detalhes dependendo do conjunto de instrucoes de hardware; o tamanho
dos elementos da matriz; e se os ndices sao constantes, valores na memoria, ou valores em registradores.
Em muitos montadores pode-se definir uma macro que ira substituir uma expressao
com a sequencia de multi-instrucao apropriada, como
ld2d (reg_alvo, nome_de_matriz, linha, coluna,
tamanho_de_linha, tamanho_do_elemento)
Em um programa numerico contendo centenas ou milhares de operacoes de acesso
de matriz, esta macro pode revelar-se extremamente u
til.
1
2
3
4
5
#define
#define
// t r u e
#define
#define
LINE LEN 80
DIVIDES ( a , n ) ( ! ( ( n ) % ( a ) ) )
i f f n has z e r o remainder modulo a /
SWAP( a , b ) { int t = ( a ) ; ( a ) = ( b ) ; ( b ) = t ; }
MAX( a , b ) ( ( a ) > ( b ) ? ( a ) : ( b ) )
Listing 3.11: C
3.4
Compilac
ao separada
Como a maioria dos programas grandes sao construdos e testados de forma incremental,
e uma vez que a compilacao de um grande programa pode ser uma operacao de
muitas horas,
qualquer linguagem projetada para suportar grandes programas devem ter suporte
para a compilacao separada.
33
4 An
alise sem
antica
4.1
Regras de an
alise sem
antica
<1-> Muitos compiladores que geram codigo por verificacoes dinamicas fornecem a
opcao de desativa-los se desejar.
costume em algumas organizacoes permitir verificacoes dinamicas durante
<2-> E
o desenvolvimento e teste do programa, e em seguida desativa-los para uso em
producao, para aumentar a velocidade de execucao.
<3-> Os erros podem ser menos provaveis em producao do que em teste, mas as
consequencias de onde um erro e detectado sao significativamente piores.
Os programadores podem escrevem afirmacoes logicas sobre os valores de dados do
programa.
Algumas linguagens de programacao fazem estas afirmacoes uma parte da sintaxe
da linguagem.
O compilador gera codigo para verificar as afirmacoes em tempo de execucao.
Uma afirmacao e uma declaracao de que uma condicao especificada e esperada ser
verdade quando a execucao atinge um certo ponto no codigo. Em Java pode-se
escrever
1 a s s e r t denominator != 0 ;
Por exemplo, em C:
1 a s s e r t ( denominator != 0 ) ;
Listing 4.2: C
Afirmacoes, e claro, poderiam ser usadas para cobrir os outros tipos de controles,
mas nao de forma tao clara ou sucinta.
Invariantes, pre-condicoes e pos-condicoes sao uma parte proeminente do cabecalho
do codigo a que se aplicam, e pode cobrir um n
umero potencialmente grande de
lugares onde uma afirmacao seria exibida de outra forma.
34
4.2
Gram
atica de atributos
35
E E+T
E ET
ET
T T F
T T /F
T F
F F
F (E)
F const
E1 E2 + T
. E1.val := sum(E2 .val, T.val)
E1 E2 T
. E1 .val := dif f erence(E2 .val, T.val)
ET
. E.val := T.val
T1 T2 F
. T 1.val := product(T2 .val, F.val)
T1 T2 /F
. T 1.val := quotient(T2 .val, F.val)
36
T F
. T.val := F.val
F1 F2
. F1 .val := additive
inverse(F2 .val)
F (E)
. F.val := E.val
F const
. F.val := const.val
<1-> Funcoes semanticas deve ser escritas em alguma notacao ja existente, porque
gramaticas de atributos realmente nao especificam o significado de um programa;
<2-> Em vez disso, eles fornecem uma maneira de associar um programa com outra
coisa que, presumidamente, tem um significado.
<3-> Nem a notacao para funcoes semanticas nem os tipos de atributos (ou seja,
o domnio dos valores passados e retornados de funcoes semanticas) e intrnseca a`
Gramatica de Atributos.
<4-> Se estivessemos interessados em definir o significado de uma linguagem de
programacao de uma forma independente de maquina, nossos atributos pode ser de
domno de denotaco
es teoricas (estes sao a base da sem
antica denotacional).
<5-> Se estivessemos interessados em provar teoremas sobre o comportamento dos
programas em nossa linguagem, nossos atributos podem ser formulas logicas (esta e
a base da sem
antica axiom
atica).
37
4.3
Avaliac
ao de atributos
<1-> Uma gramatica de atributos em que todos os atributos sao sintetizados e dito
para ser S-atribuda.
<2-> Os argumentos para funcoes semanticas em uma gramatica S-atribuda sao
sempre atributos dos smbolos no lado direito da producao, e o valor de retorno e
sempre colocado em um atributo do lado esquerdo da producao.
<3-> Tokens tem muitas vezes propriedades intrnsecas (por exemplo, a representacao de caracteres de um identificador ou o valor de uma constante numerica);
<4-> Em um compilador estes sao atributos sintetizados inicializados pelo analisador sintatico.
<1-> Em geral, podemos imaginar (e de fato tem necessidade de) atributos cujos
valores sao calculados quando o smbolo esta no lado direito da producao atual.
<2->Tais atributos sao chamados de herdados.
<3-> Eles permitem que a informacao contextual flua de um smbolo de cima ou
de lado, para que as regras da producao possam ser aplicadas de formas diferentes
(ou gerar valores diferentes), dependendo do contexto.
<4-> Informacoes sao geralmente passadas smbolo por smbolo por meio de atributos herdados.
<5-> Atributos herdados da raiz da arvore de analise tambem pode ser usado para
representar o ambiente externo (caractersticas da maquina de alvo, os argumentos
de linha de comando para o compilador, etc.).
38
<1-> Dizemos uma gramatica de atributos e bem definida se as suas regras determinam um conjunto u
nico de valores para os atributos de cada arvore de analise
possvel.
<2-> Uma gramatica de atributos e n
ao circular se nunca leva a uma arvore de
analise em que ha ciclos de atributo no fluxo grafico, isto e, se nenhum atributo, em
qualquer arvore de analise, ja depende de si mesmo.
<3-> A gramatica pode ser circular e ainda ser bem definida se os atributos garantem convergir para um valor u
nico.
<4-> Como regra geral, gramaticas de atributos tendem a ser nao circular.
<1-> Um algoritmo que decora arvore de analise sintatica, invocando as regras de
uma gramatica de atributos em uma ordem consistente com o fluxo de atributo da
arvore e chamado de um esquema de traduc
ao.
<2-> Talvez o esquema mais simples e aquele que passa repetidas vezes ao longo
de uma arvore, chamar qualquer funcao semantica cujos argumentos foram todos
definidos, e quando completar uma passada em que os valores nao mudam.
39
<3-> Tal esquema e dito ser esquecido (oblivious), no sentido de que ele nao
explora um conhecimento especial da arvore de analise ou a gramatica.
<4-> Ele ira parar somente se a gramatica esta bem definida.
<1-> Um compilador que intercala analise semantica e geracao de codigo e dito ser
um compilador de uma passagem.
<2-> Nao e possvel afirmar se intercalacao analise semantica com analise sintatica
faz um compilador mais simples ou mais complexo.
<3-> Se a geracao de codigo intermediario e intercalada com a analise, nao e preciso
construir uma arvore sintatica para todas as situacoes (a nao ser, e claro, a arvore
de sintaxe e o codigo intermediario).
<4-> Alem disso, muitas vezes e possvel escrever o codigo intermediario para um
local de sada em tempo real, em vez de acumular-lo nos atributos da raiz da arvore
de analise.
<5-> A economia de espaco resultantes foram importantes para as geracoes anteriores de computadores, que tinham muito pouca memoria principal.
Considere o seguinte operacao: (1 + 3) 2:
40
41
5 Fluxo de controle
5.1
Fluxo de controle
42
5.2
Avaliac
ao de express
oes
Uma expressao consiste geralmente em qualquer objeto simples (por exemplo, uma
constante literal ou uma chamada de variavel ou constante) ou um operador ou
funcao aplicado a um conjunto de operandos ou argumentos, cada um dos quais por
sua vez e uma expressao.
comum usar o termo operador para funcoes de uso especial, sintaxe simples, e
E
usar o termo operando para um argumento de um operador.
Na maioria das linguagens imperativas, chamadas de funcao consistem de um nome
de funcao seguido por um paramentro, lista de argumentos separada por vrgula,
como em
1 my func (A, B, C)
Em geral, uma linguagem pode especificar que as chamadas de funcao que empregam
prefixo, infixo, ou notacao posfixa.
Estes termos indicam, respectivamente, se o nome da funcao aparece antes, entre
ou apos seus varios argumentos:
1 p r e f i x : op a b
2 i n f i x : a op b
3 p o s t f i x : a b op
ou
op ( a , b )
43
ou
( op a b )
ou
1 a + ( ( ( b c ) d ) ( e / f ) )
ou
1 a + ( ( b ( c ( d e ) ) ) / f )
A menos que A, B, C e D sao todos do tipo booleano, o que e improvavel, este codigo
ira resultar em um erro de semantico, uma vez que as regras de precedencia fazer
com que grupo como A <(B e C) < D. ( e mesmo se todos os quatro operandos
sao do tipo booleano, o resultado e quase certo que sera algo diferente do que o
programador pretendia.)
A maioria das linguagens evita este problema, dando aos operadores aritmeticos
maior precedencia do que operadores relacionais, que por sua vez tem maior precedencia que os operadores logicos.
As excecoes notaveis incluem APL e Smalltalk, em que todos os operadores sao de
igual precedencia (parenteses deve ser usado para especificar agrupamento).
Em uma linguagem puramente funcional, expressoes sao os blocos de construcao de
programas, e computacao consiste inteiramente de avaliacao de expressao.
O efeito de qualquer expressao individual sobre o calculo global e limitada ao valor
que a expressao fornece ao seu contexto atual.
44
Tabela 5.1
Fortran
Pascal
**
not
*,/
+, - (unario e
binario)
C
++, (pos-inc.,
dec.)
++, (pre-inc.,
dec.), +, (unario), &, *
(endereco,
conte
udo), !,
(logico, not bit a
bit)
* (binario), /, %
(modulo)
*, /, div, mod,
and
+, - (unario e
binario), or
+, - (binario)
<<, >>
(deslocamento de
bit a direita e
esquerda)
<, <=, >, >=
(teste
desigualdade)
==, != (teste
igualdade)
& (and bit a bit)
(xor bit a bit)
| (or bit a bit)
.not.
.and.
.or.
.eqv., .neqv.
(comparacoes
logicas)
|| (or logico)
?: (if . . . then. .
. else)
=, +=, -=, *=,
/=, %=, >>=,
<<=, &=, =,
|= (atribuicao)
, (sequencia)
45
Ada
abs (valor
absoluto), not, **
*, /, mod, rem
+, - (unario)
+, - (binario),&
(concatenacao)
=, /= , <, <=,
>, >=
int x = 1 0 , y = 2 0 ;
int fun1 ( ) {
x = x + 30;
return 1 0 ;
}
void main ( ) {
y = y + fun1 ;
...
i f ( ( x > 5 0 ) && ( ( fun1 + y ) > 7 0 ) )
...
}
Estas diferencas sao muitas vezes invisvel, porque eles nao afectam o comportamento de programas simples.
Considere o seguinte atribuicao em C:
1 d = a;
2 a = b + c;
ou pior
1 b. c [3].d = b. c [3].d e ;
Em varias linguagens, incluindo Clu, ML, Perl, Python e Ruby, tambem e possvel
escrever
47
1 a, b = c , d;
Alem disso, multiplas formas de atribuicao permite que as funcoes retornem tuplas,
bem como valores individuais:
1 a , b , c = foo (d , e , f ) ;
2. Melhora do c
odigo: A ordem de avaliacao das subexpressoes tem um impacto tanto na alocacao de registradores e ordenamento de instrucoes. Na
expressao a b + f (c), e provavelmente desejavel chamar f antes de avaliar a b,
porque o produto, se calculado em primeiro lugar, precisaria ser salvos durante
a chamada de f , e f pode usar todos os registradores em que ele seria salvo.
Na mesma linha, considere a sequencia
1 a := B [ i ] ;
2 c := a 2 + d 3 ;
Listing 5.1: C
1 p := m y l i s t ;
2
3 while ( p <> n i l ) and ( p . key <> v a l ) do // ouch !
4
p := p . next ;
5.3
Fluxo estruturado e n
ao estruturado
function s e a r c h ( key : s t r i n g ) : s t r i n g ;
var r t n : s t r i n g ;
procedure s e a r c h f i l e ( fname : s t r i n g ) ;
begin
f o r . . . // i t e r a t e o v e r l i n e s
i f found ( key , l i n e ) then begin
r t n := l i n e ;
goto 1 0 0 ;
end ;
end ;
...
begin // s e a r c h
f o r . . . // i t e r a t e o v e r f i l e s
s e a r c h f i l e ( fname ) ;
50
15 1 0 0 : r e t u r n r t n ;
16 end ;
5.4
Sequ
encia
5.5
Selec
ao
Instrucoes de selecao na maioria das linguagens imperativas empregam alguma variante da notacao if...then...else introduzido em Algol 60:
1
2
3
4
5
i f c o n d i t i o n then s t a t e m e n t
e l s e i f c o n d i t i o n then s t a t e m e n t
e l s e i f c o n d i t i o n then s t a t e m e n t
...
else statement
1
2
3
4
5
IF a = b THEN . . .
ELSIF a = c THEN . . .
ELSIF a = d THEN . . .
ELSE . . .
END
51
A B)
(...) )
A C)
(...) )
A D)
(...) )
(...) ))
r1
r2
r1
r2
r3
r2
r1
r2
r3
r2
:=
:=
:=
:=
:=
:=
:=
:=
:=
:=
A l o a d
B
r1 > r2
C
D
r2 > r3
r1 & r2
E
F
r 2 <> r 3
52
11
12
13 L1 :
14
15 L2 :
16 L3 :
r 1 := r 1 | r 2
i f r 1 = 0 goto L2
t h e n c l a u s e // l a b e l nao r e a l m e n t e usada
goto L3
else clause
r 1 := A
r 2 := B
i f r 1 <= r 2 goto L4
r 1 := C
r 2 := D
i f r 1 > r 2 goto L1
L4 : r 1 := E
r 2 := F
i f r 1 = r 2 goto L2
L1 : t h e n c l a u s e
goto L3
L2 : e l s e c l a u s e
L3 :
CASE . . .
1:
| 2 , 7:
| 3..5:
|
10:
ELSE
END
53
OF
14
15 }
break ;
5.6
Iterac
ao
Listing 5.14: C
54
1 {
2
3
4
5
6
7 }
i = first ;
while ( i <= l a s t ) {
...
i += s t e p ;
}
Listing 5.15: C
1
2
3
4
5
6
b i n t r e e <int> my tree = . . .
...
f o r ( b i n t r e e <int > : : i t e r a t o r n = my tree>b e g i n ( ) ;
n != my tree>end ( ) ; ++n ) {
c o u t << n << \n ;
}
Listing 5.19: C
1 o u t e r : while (>) { # i t e r a t e o v e r l i n e s o f i n p u t
2
foreach $c ( s p l i t / / ) { # i t e r a t e o v e r rema in ing c h a r s
3
l a s t o u t e r i f ( $c = $ ) ; # e x i t main l o o p i f we s e e a $ s i g n
4
consume char ( $c ) ;
5
}
6 }
55
56
Ocorre tambem na avaliacao booleana curto-circuito, parametros de chamada-pornome, e em certas linguagens funcionais.
A partir dos pontos de vista de clareza e eficiencia, avaliacao de ordem aplicada e
geralmente prefervel a uma avaliacao de ordem normal.
Em algumas circunstancias, no entanto, a avaliacao em ordem normal pode realmente levar a codigo mais rapido, ou para um codigo que funciona quando a avaliacao aplicada levaria a um erro de tempo de execucao.
Em ambos os casos, o que importa e que a avaliacao em ordem normal, por vezes,
nao vai avaliar um argumento como um todo, se o seu valor nunca e realmente
necessario.
Scheme preve a avaliacao em ordem normal opcionalmente na forma de funcoes
chamadas delay e force.
Estas fornecem uma implementacao da avaliac
ao preguicosa.
Na ausencia de efeitos colaterais, avaliacao preguicosa tem a mesma semantica como
a avaliacao de ordem normal, mas a implementacao mantem o controle das expressoes as quais ja foram avaliadas, para que ele possa reutilizar os seus valores se
forem necessarios mais de uma vez em um determinado ambiente de referenciamento.
5.7
N
ao determin
ancia
Nossa u
ltima categoria de fluxo de controle e nao determinancia.
Uma construcao nao determinista e aquele em que a escolha entre as alternativas
(isto e, entre os caminhos de controle) e deliberadamente nao especificado.
Ja vimos exemplos de nao determinancia na avaliacao das expressoes: na maioria
das linaguagens, argumentos de operadores ou sub-rotinas podem ser avaliadas em
qualquer ordem.
Algumas linguagens, como Algol 68 e varias linguagens similares, proporcionam
mecanismos mais amplos nao determinsticos.
57
6 Tipos de dados
6.1
Tipos de dados
6.2
Checagem de tipos
Na maioria das linguagens de tipagem estatica, cada definicao de um objeto (constante, variavel, sub-rotina, etc.) deve especificar um tipo.
Muitos dos contextos em que um objeto pode aparecer tambem sao digitados, no
sentido de que as regras da linguagem restrinjam os tipos que um objeto, nesse
contexto, pode validamente possuir.
No mnimo, o objeto pode ser utilizado se o seu tipo e o tipo esperado pelo contexto
sao equivalentes (isto e, o mesmo).
Em muitas llinguagens a compatibilidade e uma relacao mais flexvel do que a equivalencia: objetos e contextos sao frequentemente compatveis mesmo quando seus
tipos sao diferentes.
58
Logo
Conversao de tipo que altera um valor de um tipo em um de outro valor;
Coercao, que realiza uma conversao automaticamente em determinados contextos;
Tipos n
ao convertidos, que sao por vezes usados em programacao de sistemas
para interpretar os bits de um valor de um tipo como se representassem um
valor de algum outro tipo.
Em uma linguagem na qual o usuario pode definir novos tipos, existem duas principais
formas de definir o tipo de equivalencia.
Equivalencia estrutural baseia-se no teor de definicoes de tipo: a grosso modo, dois
tipos sao o mesmo se eles consistem dos mesmos componentes, juntos na mesma
equivalencia.
1
2
3
4
int i ;
f l o a t f1 , f 2 ;
...
f1 = i + f2 ;
Em uma linguagem na qual o usuario pode definir novos tipos, existem duas principais
formas de definir o tipo de equivalencia.
Equivalencia de nome baseia-se na ocorrencia lexica de definicoes de tipo: a grosso
modo, cada definicao introduz um novo tipo.
1 type i n t 1 = Integer ;
2 type i n t 2 = Integer ;
3 var v1 : i n t 1 ;
4
v2 : i n t 2 ;
Convers
ao. Dependendo dos tipos envolvidos, a conversao pode ou nao pode exigir
codigo a ser executado em tempo de execucao. Ha tres casos principais:
1. Os tipos poderiam ser considerados estruturalmente equivalente, mas a linguagem usa equivalencia de nome. Neste caso os tipos de empregar a mesma
representacao de baixo nvel, e tem o mesmo conjunto de valores. A conversao
e, portanto, uma operacao puramente conceitual; nenhum codigo tera que ser
executado em tempo de execucao.
2. Os tipos tem diferentes conjuntos de valores, mas os valores que se intersectam
sao representados da mesma maneira. Um tipo pode ser um sub-intervalo do
59
outro, por exemplo. Se o tipo fornecido tem alguns valores que o tipo esperado
nao tem, entao o codigo deve ser executado em tempo de execucao para garantir
que o valor atual esta entre aqueles que sao validos no tipo esperado.
Se a verificacao falhar, entao um erro semantico dinamico acontece.
Se a verificacao for bem sucedida, entao a representacao subjacente do
valor pode ser usado, sem alteracoes.
3. Os tipos tem diferentes representacoes de baixo nvel, mas nos ainda pode
definir algum tipo de correspondencia entre os seus valores.
Um n
umero inteiro de 32-bit, por exemplo, pode ser convertido para um
n
umero de ponto flutuante de precisao dupla IEEE sem perda de precisao.
Um n
umero de ponto flutuante pode ser convertido para um inteiro arredondando ou truncando, mas dgitos fracionarios serao perdidos.
Sempre que uma linguagem permite que um valor de um tipo a ser utilizado num
contexto que espera outro, a implementacao da linguagem de efetuar uma conversao
automatica, implcita para o tipo esperado.
Esta conversao e chamado um tipo de coercao.
A coercao pode exigir um codigo de tempo de execucao execute uma verificacao
semantica dinamica, ou converta entre representacoes de baixo nvel.
Vimos como a verificacao de tipo assegura que os componentes de uma expressao
(por exemplo, os argumentos de um operador binario) tenha os tipos apropriados.
Mas o que determina o tipo da expressao em geral?
Na maioria dos casos, a resposta e facil.
O resultado de um operador aritmetico tem geralmente o mesmo tipo que os
operandos.
O resultado de uma comparacao e geralmente booleano.
O resultado de uma chamada de funcao tem o tipo declarado no cabecalho da
funcao.
O resultado de uma atribuicao (nas lnguas em que as atribuicoes sao expressoes) tem o mesmo tipo que o lado esquerdo.
Em alguns casos, no entanto, a resposta nao e obvia. Em particular, as operacoes
em subintervalos e objetos compostos nao necessariamente preservam os tipos dos
operandos.
60
Sub-arranjos
1 type Atype = 0 . . 2 0 ;
2
Btype = 1 0 . . 2 0 ;
3
4 var a : Atype ;
5
b : Btype ;
qual e o tipo de a + b? Certamente nao e nem Atype nem Btype, uma vez que os
valores possveis variam de 10 para 40.
Pode-se imaginar que seja um novo tipo anonimo com 10 e 40 como limites. A
resposta usual em Pascal e dizer que o resultado de qualquer operacao aritmetica
em um Sub-arranjos tem o tipo base do Sub-arranjos, neste caso inteiro.
Se o resultado de uma operacao aritmetica e atribudo para uma variavel de um
tipo de sub-intervalo, em seguida, uma verificacao semantica dinamica pode ser
necessaria.
Para evitar a despesa de algumas verificacoes desnecessarias, um compilador pode
acompanhar em tempo de compilacao dos maiores e menores valores possveis de
cada expressao, em essencia calcular tipo 10 ... 40.
6.3
Layout da mem
oria
61
62
6.4
Dimens
oes, limites e Aloca
c
ao
63
Informaco
es de dimens
oes
Informaco
es de contabilidade (campos comprimento e cabecalho para manter
o controle dos blocos alocados)
1 f o r ( i = 0 ; i < N; i ++) { // rows /
2
f o r ( j = 0 ; j < N; j ++) { // columns /
3
. . . A[ i ] [ j ] . . .
4
}
5 }
1 do j = 1 , N ! columns
2
do i = 1 , N ! rows
3
. . . A( i , j ) . . .
4
end do
5 end do
64
1
2
3
4
5
6
7
char days [ ] [ 1 0 ] = {
Sunday , Monday , Tuesday ,
Wednesday , Thursday ,
Frida y , Saturday
};
...
days [ 2 ] [ 3 ] == s ; / i n Tuesday /
1
2
3
4
5
6
7
char days [ ] = {
Sunday , Monday , Tuesday ,
Wednesday , Thursday ,
Frida y , Saturday
};
...
days [ 2 ] [ 3 ] == s ; / i n Tuesday /
65
66
7 Abstra
c
oes: Subtorinas e Controle
da Abstra
c
ao
7.1
Sequ
encia de chamadas
67
3. <4-> restaura o fp eo sp
4. <5-> salta de volta para o endereco de retorno
Finalmente, o chamador
1. <6-> move o valor de retorno para onde for necessario
2. <7-> restaura registros do chamador, se necessario
7.2
Passagem de par
ametros
A maioria das sub-rotinas sao parametrizadas: elas tomam argumentos que controlam certos aspectos de seu comportamento, ou especificam os dados em que elas
estao operarando.
Os nomes dos parametros que aparecem na declaracao de uma sub-rotina sao conhecidos como par
ametros formais.
Variaveis e expressoes que sao passados para uma sub-rotina em uma chamada
particular, sao conhecidos como par
ametros atuais.
Temos sido referindo-se a parametros atuais como argumentos.
1 p(x) ;
69
70
Os programadores que mudar para C, apos alguma experiencia com Pascal, Modula,
ou Ada sao muitas vezes frustrados pela falta de parametros de referencia do C.
Pode-se modificar um objeto passando o seu endereco, mas, em seguida, o parametro
formal e um ponteiro, e deve ser desreferenciado explicitamente sempre que e usado.
C++ resolve este problema atraves da introducao de uma nocao explcita de uma
refer
encia.
Parametros de referencia sao especificadas precedendo o seu nome com um e comercial no cabecalho da funcao:
71
7.3
Subrotinas e m
odulos
Sub-rotinas fornecer um caminho natural para executar uma operacao para uma
variedade de valores diferentes de objetos (parametros).
Em grandes programas, a necessidade tambem surge muitas vezes para realizar uma
operacao para uma variedade de diferentes tipos.
Um sistema operacional, por exemplo, tende a fazer uso pesado de filas, para manter processos, descritores de memoria, buffers de arquivo, blocos de controle do
dispositivo, e uma serie de outros objetos.
As caractersticas da estrutura de dados de fila sao independentes das caractersticas
dos itens colocados na fila.
72
Em uma linguagem como Pascal ou Fortran, essa declaracao estatica do tipo de item
significa que o programador deve criar copias separadas da fila para cada tipo de
item, mesmo que todo o texto dessas copias (exceto os nomes de tipo nos cabecalhos
de procedimento ) e o mesmo.
Em algumas linguagens (C e um exemplo obvio) e possvel definir uma fila de ponteiros para objetos arbitrarios, mas o uso de uma tal fila requer troca do tipo e
abandonar a verificacao em tempo de compilacao.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
generic
type item i s private ;
max items : in i n t e g e r := 1 0 0 ;
package queue i s
procedure enqueue ( i t : in item ) ;
function dequeue return item ;
private
subtype i n d e x i s i n t e g e r range 1 . . max items ;
i t e m s : array ( i n d e x ) of item ;
n e x t f r e e , n e x t f u l l : i n d e x := 1 ;
end queue ;
package body queue i s
procedure enqueue ( i t : in item ) i s begin
i t e m s ( n e x t f r e e ) := i t ;
n e x t f r e e := n e x t f r e e mod max items + 1 ; end enqueue ;
function dequeue return item i s r t n : item := i t e m s ( n e x t f u l l ) ;
begin
n e x t f u l l := n e x t f u l l mod max items + 1 ;
return r t n ; end dequeue ;
end queue ;
...
package r e a d y l i s t i s new queue ( p r o c e s s ) ;
package i n t q u e u e i s new queue ( i n t e g e r , 5 0 ) ;
73
14
15
16
17
18
return r t n ; }
};
...
queue<p r o c e s s > r e a d y l i s t ;
queue<int , 50> i n t q u e u e ;
7.4
Manipulac
ao de exce
c
ao
74
5
...
6
c o u t << e v e r y t h i n g s ok \n ;
7
...
8 } catch ( m y e x c e p ti o n ) {
9
c o u t << oops \n ;
10 }
Na maioria das linguagens, um bloco de codigo pode ter uma lista de manipuladores
de excecao. Em C ++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
75
7.5
Co-rotinas
Como uma continuacao, um co-rotina e representado por um fechamento (um endereco de codigo e um ambiente de referencia), em que se pode saltar por meio de um
goto nao local. Neste caso, uma operacao especial conhecido como transfer
encia.
A principal diferenca entre as duas abstracoes e que a continuacao e uma constante
de que nao muda, uma vez criada uma co-rotina muda toda vez que e executado.
Quando uma continuacao GOTO, o nosso contador de programa antigo e perdido,
a menos que explicitamente crie uma nova continuacao para manter lo.
Enquanto que a transferencia de uma co-rotina para outra, o nosso contador de
programa antigo e salvo: na co-rotina estamos deixando atualizado para refletir
isso.
Assim, se executarmos GOTO ao mesmo tempo que multiplas continuacoes, cada
salto vai comecar precisamente no mesmo local, mas se executarmos a transferencia
para as mesmas co-rotina varias vezes, cada salto vai ocupar onde o anterior parou.
Porque eles sao concorrentes (isto e, simultaneamente iniciada mas nao concluda),
co-rotinas nao podem compartilhar uma u
nica pilha: as suas chamadas e retornos
de sub-rotinas, tomados como um todo, nao ocorrem no u
ltimo a entrar, primeiro a
sair.
Se cada co-rotina e declarada no nvel mais externo de aninhamento lexical , em
seguida, suas pilhas sao inteiramente disjuntos: os u
nicos objetos compartilhados
sao globais e, portanto, estaticamente alocado.
A maioria dos sistemas operacionais facilitam a atribuicao na pilha, e aumentam a
sua parte do espaco de enderecos virtuais, conforme necessario durante a execucao.
Normalmente nao e facil atribuir um n
umero arbitrario de tais pilhas; espaco para
co-rotinas e um desafio de implementacao.
A solucao mais simples e dar a cada co-rotina uma quantidade fixa de espaco de
pilha alocada estaticamente.
um erro de tempo de execucao se a co-rotina precisar de espaco adicional.
E
Se a co-rotina usa menos espaco do que e dado, o excesso e simplesmente desperdicado.
76
7.6
Eventos
77
Da mesma forma, se algum outro processo esta sendo executado quando a interrupcao ocorre, o kernel deve salvar o estado do primeiro processo no final da sua
u
ltima fatia de tempo.
78
8 Abstra
c
ao de dados e orienta
c
ao `
a
objetos
8.1
Programac
ao orientada `
a objetos
c l a s s l i s t e r r { // e x c e p t i o n
public :
char d e s c r i p t i o n ;
l i s t e r r ( char s ) { d e s c r i p t i o n = s ; }
};
class l i s t n o d e {
l i s t n o d e prev ;
l i s t n o d e next ;
l i s t n o d e head node ;
public :
int v a l ; // t h e a c t u a l d a t a i n a node
l i s t n o d e ( ) { // c o n s t r u c t o r
prev = next = head node = t h i s ; // p o i n t t o s e l f
v a l = 0 ; // d e f a u l t v a l u e
}
list node predecessor () {
i f ( prev == t h i s | | prev == head node ) return 0 ;
return prev ;
}
list node successor () {
i f ( next == t h i s | | next == head node ) return 0 ;
return next ;
}
bool s i n g l e t o n ( ) {
return ( prev == t h i s ) ;
}
void i n s e r t b e f o r e ( l i s t n o d e new node ) {
i f ( ! new node>s i n g l e t o n ( ) )
throw new l i s t e r r ( attempt t o i n s e r t node a l r e a d y on l i s t )
;
prev>next = new node ;
new node>prev = prev ;
new node>next = t h i s ;
prev = new node ;
new node>head node = head node ;
}
void remove ( ) {
if ( singleton () )
80
38
39
40
41
42
43
44
45
46
47 } ;
Quando criado um objeto com new, um objeto e alocado na pilha; quando criada
atraves de uma declaracao e atribuda estaticamente ou na pilha, dependendo do
tempo de vida.
Em ambos os casos, a criacao faz com que a invocacao de uma rotina de inicializacao
especificada pelo programador, conhecido como um construtor.
Em C++ e seus descendentes, Java e C#, o nome do construtor e a mesma que a
da propria classe.
C++ tambem permite que o programador especifique um metodo destrutor que
sera chamado automaticamente quando um objeto e destrudo, seja por acao programador ou pelo retorno da sub-rotina em que foi declarada.
O nome do processo de destruicao tambem e a mesma que da classe, mas com um
til ( ).
Destruitores sao comumente usados para gerenciamento de armazenamento e verificacao de erros.
O rotulo public na lista dos membros do list node separa membros exigidos pela
implementacao da abstracao de membros disponveis para os usuarios da abstracao.
Membros que aparecem antes do rotulo nao sao.
C++ tambem fornece um rotulo private, de modo que as partes visveis publicamente de uma classe pode ser listado em primeiro lugar.
Em muitas outras lnguas, dados p
ublicos e sub-rotinas membro (campos ou metodos) devem ser individualmente rotulado dessa forma.
Programas orientados a objetos tendem a fazer muitas mais chamadas de sub-rotinas
como programas imperativos comuns, e as sub-rotinas tendem a ser mais curtas.
81
Muitas coisas que seriam realizadas por acesso direto aos campos de registro em uma
linguagem de von Neumann tendem a ser escondido dentro de metodos de objetos
em uma linguagem orientada a objetos.
Muitos programadores de fato considera-lo um estilo ruim para declarar campos
p
ublicos, porque eles dao aos usuarios de uma abstracao acesso direto `a representacao interna.
1 class l i s t n o d e {
2
...
3
int v a l ; // v a l ( l o w e r c a s e v ) i s p r i v a t e
4
public int Val {
5
g e t { // p r e s e n c e o f g e t a c c e s s o r and o p t i o n a l
6
return v a l ; // s e t a c c e s s o r means t h a t Val i s a p r o p e r t y
7
}
8
set {
9
v a l = v a l u e ; // v a l u e i s a keyword : argument t o s e t
10
}
11
}
12
...
13 }
8.2
Encapsulamento e heran
ca
82
A filosofia basica por detras das regras de visibilidade de C++ pode ser resumido
como se segue:
Qualquer classe pode limitar a visibilidade de seus membros. Membros p
ublicos
sao visveis em qualquer lugar da declaracao da classe esta no escopo. Membros
privados sao visveis apenas dentro de metodos da classe. Membros protegidos
sao metodos dentro visveis da classe ou seus descendentes.
A filosofia basica por detras das regras de visibilidade de C++ pode ser resumido
como se segue:
Uma classe derivada pode restringir a visibilidade de membros de uma classe
base, mas nunca pode aumenta-lo. Membros privados de uma classe base nao
sao visveis em uma classe derivada. Membros protegidos e p
ublicos de uma
classe base p
ublica sao protegidos ou p
ublicos, respectivamente, em uma classe
derivada. Membros protegidos e p
ublicos de uma classe base protegida sao
membros protegidos de uma classe derivada. Membros protegido e p
ublicos de
uma classe base privada sao membros privados de uma classe derivada.
A filosofia basica por detras das regras de visibilidade de C++ pode ser resumido
como se segue:
Uma classe derivada que limita a visibilidade dos membros de uma classe base,
declarando que a classe base protegida ou privada pode restaurar a visibilidade
dos membros individuais da classe base, inserindo a declaracao using na parte
da declaracao da classe derivada protegida ou p
ublica.
Muitas linguagens permitem a declaracao de classes aninhardas.
Isso levanta uma questao imediata: se Inner e um membro da Outer, pode metodos
de Inner ver os membros do Outer, e em caso afirmativo, qual instancia que eles
veem?
A resposta mais simples, adotada em C++ e C #, e permitir o acesso apenas aos
membros estaticos da classe externa, uma vez que estes tem apenas uma u
nica
instancia.
Com efeito, aninhamento serve simplesmente como um meio de esconder informacao.
Java tem uma abordagem mais sofisticada. Ele permite que uma classe aninhada
(interna) para acessar membros arbitrarias de sua classe externa.
Cada instancia da classe interna deve, portanto, pertencer a uma instancia da classe
externa.
83
1 c l a s s Outer {
2
int n ;
3
class Inner {
4
public void bar ( ) { n = 1 ; }
5
}
6
Inner i ;
7
Outer ( ) { i = new I n n e r ( ) ; } // c o n s t r u c t o r
8
public void f o o ( ) {
9
n = 0;
10
System . out . p r i n t l n ( n ) ; // p r i n t s 0
11
i . bar ( ) ;
12
System . out . p r i n t l n ( n ) ; // p r i n t s 1
13
}
14 }
8.3
Inicializac
ao e finaliza
c
ao
8.4
M
etodo de vincula
c
ao din
amica
Uma das principais consequencias da extensao heranca / tipo e que uma classe
derivada D tem todos os membros de dados e sub-rotinas de sua classe base C.
Enquanto D nao esconde qualquer um dos membros visveis de C, faz sentido permitir que um objeto da classe D seja usado em qualquer contexto que espera um
objeto da classe C: qualquer coisa que pode fazer em um objeto da classe C tambem
podemos fazer para um objeto da classe D.
A capacidade de usar uma classe derivada em um contexto que espera sua classe
base e chamado polimorfismo de subtipo.
Se imaginarmos um sistema de computacao administrativa para uma universidade,
que pode derivar classes de aluno e professor da classe pessoa:
1
2
3
4
5
6
7
8
9
class person { . . .
c l a s s s t u d e n t : public p e r s o n { . . .
c l a s s p r o f e s s o r : public p e r s o n { . . .
...
student s ;
professor p;
...
p e r s o n x = &s ;
p e r s o n y = &p ;
85
Em Simula, C++ e C#, que usam metodo de vinculacao estatica por padrao, o programador pode especificar que metodos particulares devem usar ligacao dinamica,
rotulando-os como virtual.
Chamadas para metodos virtuais sao enviados para a aplicacao adequada em tempo
de execucao, com base na classe do objeto, em vez de o tipo de referencia.
Em C++ e C#, a palavra-chave virtual e usada como prefixos na declaracao de
sub-rotina.
Com metodo estatico de vinculacao (como em Simula, C++, C#, ou Ada 95), o
compilador pode sempre dizer qual versao de um metodo para chamar, com base no
tipo da variavel a ser usado.
Com o metodo de vinculacao dinamico, no entanto, o objeto referido por uma variavel de referencia ou ponteiro deve conter informacoes suficientes para permitir que
o codigo gerado pelo compilador encontre a versao correta do metodo em tempo de
execucao.
A implementacao mais comum representa cada objeto com um registro cujo primeiro
campo contem o endereco de uma tabela metodo virtual (vtable) para a classe do
objeto.
1 class foo {
2
int a ;
3
double b ;
4
char c ;
5 public :
6
v i r t u a l void k ( . . .
7
v i r t u a l int l ( . . .
8
v i r t u a l void m( ) ;
9
v i r t u a l double n ( . . .
10
...
11 } F ;
86
1 c l a s s bar : public f o o {
2
int w ;
3 public :
4
void m( ) ; // o v e r r i d e
5
v i r t u a l double s ( . . .
6
v i r t u a l char t ( . . .
7
...
8 } B;
8.5
Heranca m
ultipla
ser desejavel derivar classe estudante, tanto de pessoa e gp list node. Em C ++,
podemos:
1 c l a s s s t u d e n t : public person , public g p l i s t n o d e { . . .
Heranca m
ultipla tambem aparece em CLOS e Python.
Simula, Smalltalk, Objective-C, Modula-3, Ada 95 e Oberon tem heranca simples.
Java, C# e Ruby fornecem uma forma limitada mix-in de heranca m
ultipla, em
que apenas uma classe pai fica autorizada a ter campos.
8.6
Revis
ao da orientada `
a objeto
88
9 Linguagens funcionais
9.1
Linguagens funcionais
Programa em
linguagem de montagem (Opcode)
Programa
executavel
(Linguagem
de maquina)
89
Programa
Funcional
(Linguagem
funcional)
Programa em
linguagem
intermediaria
(Lambda-calculo)
Programa
executavel
(Linguagem
de maquina)
Programa
Funcional
(Linguagem
funcional)
Maquina abstrata
Programa na
linguagem C
Analisemos a expressao: z = (2 a y + b) (2 a y + c)
Otimizando:
t=2ay
(9.1)
z = (t + b) (t + c)
(9.2)
y =2ay+b
(9.3)
z =2ay+c
(9.4)
Sejam as expressoes:
90
Teremos:
t=2ay
(9.5)
y =t+b
(9.6)
z =t+c
(9.7)
9.2
-express
oes
A -expressao +3 4 e interpretada como (+3)4, ou seja, uma funcao(+3) que adiciona 3 ao argumento (4).
Esta propriedade se deve ao fato de que o resultado da aplicacao de uma funcao seja
tambem uma outra funcao.
Seja a -expressao (x. + x y)4.
Sabe-se que se trata da aplicacao de uma funcao sobre a variavel x, onde o corpo
da funcao e (+x y), a uma outra -expressao, que no caso e a constante 4
A variavel x pode ser pensada como um local onde o argumento 4 deve ser colocado.
Ja para a variavel y, este mesmo raciocnio nao pode ser aplicado por que nao existe
este local sinalizado por uma variavel y apos a letra .
Uma ocorrencia de uma variavel e ligada se existir uma -abstracao a qual esta
variavel esteja ligada. Em caso contrario ela e livre.
x. + ((y. + y z)7)x
-conversao
92
(9.8)
(x. x 1) (y. y 1)
(9.9)
(9.10)
x.axa b.aba
(9.11)
+ (3 4)(7 8)
(9.12)
+12(7 8)
(9.13)
+12 56
(9.14)
68
(9.15)
93
+ (3 4)(7 8)
(9.16)
+(3 4)56
(9.17)
+12 56
(9.18)
68
(9.19)
(x.xx)(x.xx)
(x.xx)(x.xx)
(x.xx)(x.xx)
...
(9.20)
9.3
Linguagem Haskell
94
1 { E s t e a r q u i v o eh um exemplo de um a r q u i v o . hs
2 ####################################}
3
4 v a l o r : : Int
5 v a l o r = 39
6 n o v a l i n h a : : Char
7 novalinha = \n
8 r e s p o s t a : : Bool
9 r e s p o s t a = True
10 maior : : Bool
11 maior = ( v a l o r > 7 1 )
12 quadrado : : Int > Int
13 quadrado x = xx
14 t o d o s I g u a i s : : Int > Int > Int > Bool
15 t o d o s I g u a i s n m p = ( n == m) && (m == p )
16
17 {###################################}
Arquivo .lhs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
E s t e a r q u i v o eh um exemplo de um a r q u i v o . l h s .
#############################
>
>
>
>
>
>
>
>
>
>
>
>
v a l o r : : Int
v a l o r = 39
n o v a l i n h a : : Char
novalinha = \n
r e s p o s t a : : Bool
r e s p o s t a = True
maior : : Bool
maior = ( v a l o r > 7 1 )
quadrado : : Int > Int
quadrado x = xx
t o d o s I g u a i s : : Int > Int > Int > Bool
t o d o s I g u a i s n m p = ( n == m) && (m == p )
###############################
95
Se uma linha comeca pelo menos uma coluna anterior do inicio da linha anterior, ela nao pertence a` mesma lista de definicoes.
Principais comandos
Comando
:?
:e
:e exemplo.hs
:l exemplo.hs
:a exemplo.hs
:q
A
c~
ao realizada
Aciona o help
Chama o script atual
Edita o arquivo exemplo.hs
Carrega o script exemplo.hs e limpa outros
arquivos carregados
Carrega o script exemplo.hs sem limpar os
outros arquivos
Termina a se
c~
ao
Letra mai
uscula se for um tipo;
Letra min
uscula se for uma variavel, constante ou funcao.
Lista de palavras reservadas:
case
else
class
hiding
data
if
default import
deriving
in
9.4
infix
module
type
infixl
of
where
infixr
renaming
instance
then
let
to
Func
oes
1
2
3
4
5
1
2
3
4
5
96
6
| x == y = x
7
| x > y = mdc xy y
8
| otherwise = mdc y x
9
10 div : : Int > Int > Int
11 div x y
12
| x == y = 1
13
| x > y = 1 + div xy y
14
| otherwise = 0
t o d o s I g u a i s ( quadrado 3 ) v a l o r ( quadrado 2 )
= ( ( quadrado 3 ) == v a l o r ) && ( v a l o r == ( quadrado 2 ) )
= ( ( 3 3 ) == v a l o r ) && ( v a l o r == ( quadrado 2 ) )
= ( 9 == v a l o r ) && ( v a l o r == ( quadrado 2 ) )
= ( 9 == 3 9 ) && ( 3 9 == ( quadrado 2 ) )
= False && ( 3 9 == ( quadrado 2 ) )
= False u t i l i z a n d o o mecanismo de a v a l i a c a o de c u r t o c i r c u i t o
1
2
3
4
5
6
7
9.5
Tipos de dados
O tipo inteiro
97
Operador
Tipo
Descri
c~
ao
+, *
adi
c~
ao e multiplica
c~
ao
exponencia
c~
ao
subtra
c~
ao (infixa) e
inversor de sinal (prefixa)
div
divis~
ao inteira (prefixa), ou div (infixa)
mod
m
odulo (prefixa), ou
mod (infixa)
abs
valor absoluto de um
inteiro
negate
>, >=, ==
operadores elacionais
operadores relacionais
Tipo
+, - *
/
^
**
==, /= <, >,<=, >=
abs
acos, asin, atan
ceiling, floor, round
cos, sin, tan
exp
fromInt
log
logBase
negate
read
pi
show
signum
sqrt
98
Tipo
Descri
c~
ao
Double
Float
Int
Int8
Int16
Int32
Int64
Integer
Rational
N
umeros racionais de precis~
ao arbitr
aria
Word
Inteiro n~
ao sinalizado de precis~
ao fixa
Word8
Inteiro n~
ao sinalizado de 8 bits
Word16
Inteiro n~
ao sinalizado de 16 bits
Word32
Inteiro n~
ao sinalizado de 32 bits
Word64
Inteiro n~
ao sinalizado de 64 bits
O tipo booleano
Fun
c~
ao
Nome
Tipo
&&
and
&& ::
||
or
|| ::
not
inversor
not ::
- aspas simples
- aspas duplas
fromEnum ::
9.6
Metodologia de programa
c
ao
Programacao Top-down
Fase de divisao.
menores.
Semana
Vendas
12
14
15
Total
41
M
edia
13.6667
100
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
\n | | T o t a l
| ++ r J u s t i f y 6 (
Programacao Bottom-up
1
2
3
4
fib
fib
fib
fib
fib 4
fib 3
fib 2
fib 1
fib 2
fib 1
fib 1
fib 0
101
fib 0
Programacao Bottom-up
1
2
3
4
5
6
7
8
9
As funcoes sobre tuplas, apesar de poderem ser definidas de varias formas, sao
comumente definidas por pattern matching.
1 somaPar : : ( Int , Int ) > Int
2 somaPar ( x , y ) = x + y
102
Definicoes locais
Haskell permite definicoes locais atraves da palavra reservada where. Por exemplo,
1 somaQuadrados : : Int > Int > Int
2 somaQuadrados n m = quadN + quadM
3
where
4
quadN = n n
5
quadM = m m
As definicoes locais podem incluir outras definicoes de funcoes e podem usar definicoes
locais a uma expressao, usando a palavra reservada let.
1 l e t x = 3 + 2 ; y = 5 1 in x 2 + 2 xy y
9.7
O tipo Lista
Ha, no entanto, que se diferenciar as listas homogeneas, que sao as listas onde todos
os valores sao do mesmo tipo, das listas heterogeneas, onde os componentes podem
ter mais de um tipo.
Haskell so admite listas homogeneas.
Por exemplo, [False, 2,Maria] nao e uma lista em Haskell, por ser heterogenea.
Em compensacao, podemos ter a lista [totalVendas, totalVendas], que tem o tipo
[Int -> Int] como tambem a lista [[12,1], [3,4], [4,4,4,4,4], [ ]] que tem o tipo [[Int]],
uma lista de listas de inteiros, alem de outras possibilidades.
Existem duas formas como as listas podem se apresentar:
a lista vazia, simbolizada por [ ], que pode ser de qualquer tipo. Por exemplo,
ela pode ser de inteiros, de booleanos, etc. Dito de outra forma, [ ] e do tipo
[Int] ou [Bool] ou [Int -> Int], significando que a lista vazia esta na intersecao
de todas as listas, sendo o u
nico elemento deste conjunto.
a lista nao vazia, simbolizada por (a : x), onde a representa um elemento da
lista, portanto tem um tipo, e x representa uma lista composta de elementos
do mesmo tipo de a. O elemento a e chamado de cabeca e x e a cauda da lista.
Algumas caractersticas importantes das listas em Haskell, sao:
A ordem em uma lista e importante, ou seja, [1,3] /= [3,1] e [False] /= [False,
False].
A lista [m .. n] e igual a` lista [m, m+1, ..., n]. Por exemplo, [1 .. 5] = [1, 2, 3,
4, 5]. A lista [3.1 .. 7.0] = [3.1, 4.1, 5.1, 6.1].
A lista [m,p .. n] e igual a` lista de m ate n em passos de p-m. Por exemplo,
[7,6 ..3] = [7, 6, 5, 4, 3] e [0.0, 0.3 ..1.0] = [0.0, 0.3, 0.6, 0.9].
A lista [m..n], para m>n, e vazia. Por exemplo, [7 .. 3] = [ ].
A lista vazia nao tem cabeca e nem cauda. Se tivesse qualquer destes dois
componentes, nao seria vazia.
A lista nao vazia tem cabeca e cauda, onde a cauda e tambem uma lista, que
pode ser vazia, ou nao.
1 somaLista : : [ Int ] > Int
2 somaLista [ ] = 0
3 somaLista ( a : x ) = a + somaLista x
104
O construtor de listas, chamado de cons e sinalizado por : (dois pontos), tem importancia fundamental na construcao de listas.
Ele e um operador que toma como argumentos um elemento, de um tipo, e uma
lista de elementos deste mesmo tipo e insere este elemento como a cabeca da nova
lista.
Por exemplo,
1 10 : [ ] = [ 1 0 ]
2 2 : 1 : 3 : [ ] = 2 : 1 : [ 3 ] = 2 : [1 ,3] = [2 ,1 ,3
Fun
c~
ao
Tipo
Exemplo
:
++
!!
concat
length
head
last
tail
init
replicate
take
drop
splitAt
reverse
zip
unzip
and
or
sum
3:[2,5]=[3,2,5]
[3,2]++[4,5]=[3,2,4,5]
[3,2,1]!!0=3
[[2],[3,5]]=[2,3,5]
length [3,2,1]=3
head [3,2,5]=3
last [3,2,1]=1
tail [3,2,1]=[2,1]
init [3,2,1]=[3,2]
replicate 3 a=[a,a,a]
take 2 [3,2,1]=[3,2]
drop 2 [3,2,1]=[1]
splitAt 2 [3,2,1]=([3,2],[1])
reverse [3,2,1]=[1,2,3]
zip[3,2,1][5,6]=[(3,5),(2,6)]
unzip [(3,5),(2,6)]=([3,2],[5,6])
and [True,False]=False
or [True,False]=True
sum [2,5,7]=14
sum [3.0,4.0,1.0]=8.0
product [1,2,3]=6
product [1.0,2.0,3.0]=6.0
product
Esta mesma funcao tambem pode ser codificada de outra forma, usando guardas:
1 p e r t e n c e b [ ] = False
2 pertence b (a : x)
3
| b == a = True
4
| otherwise = p e r t e n c e x b
105
Vamos definir uma funcao de ordenacao, ordena, que utiliza uma funcao auxiliar
insere, cuja tarefa e inserir cada elemento da lista no lugar correto.
1
2
3
4
5
6
7
8
9
Desta forma ex1 = [4,8,14]. Se quizermos encontrar a lista ex2 composta dos elementos de ex que sejam pares, podemos declarar
1 ex2 = [ a | a<ex , a mod 2 == 0 ]
a ideia de listas de funcoes ou de funcoes que retornam outras funcoes com resul E
tados.
Esta e uma caracterstica importante das linguagens funcionais.
A ideia central e a de que as funcoes sao consideradas com os mesmos direitos que
qualquer outro tipo de dado, dizendo-se, corriqueiramente, que elas sao cidadas de
primeira categoria.
106
Vamos imaginar uma funcao twice que, quando aplicada a uma outra funcao, por exemplo, f, produza, como resultado, uma outra funcao que aplicada a seu argumento
tenha o mesmo efeito da aplicacao da funcao f, duas vezes. Assim,
1 twice f x = f ( f x)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
107
Tipos variaveis
Quando uma funcao tem um tipo envolvendo um ou mais tipos variaveis, diz-se
que ela tem um tipo polimorfico.
Para explicitar esta caracterstica denota-se que [ ] :: [t], sendo t e uma variavel
que pode assumir qualquer tipo.
Assim, cons tem o tipo polimorfico (:) :: t -> [t] -> [t]. As seguintes funcoes,
algumas ja definidas anteriormente, tem os tipos:
1
2
3
4
5
A resposta a esta aplicacao e 16. O interesse maior aqui esta na definicao da funcao
compoe.
Ela toma um par de parametros, f e g (ambos funcoes) e retorna uma outra funcao,
h, cujo efeito de sua aplicacao e a composicao das funcoes f e g.
Esta forma de codificacao torna a composicao explcita sem a necessidade de aplicar
cada lado da igualdade a um argumento.
Como e sabido, nem todo par de funcoes pode ser composto.
O tipo da sada da funcao g tem de ser o mesmo tipo da entrada da funcao f. O
tipo de . e:
( . ) :: (u -> v) -> (t -> u) -> (t -> v).
A composicao e associativa, ou seja, f . (g . h) = (f . g) . h, que deve ser interpretado
como faca h, depois faca g e finalmente faca f.
As secoes sao operacoes parciais, normalmente relacionadas com as operacoes aritmeticas. Nas aplicacoes, elas sao colocadas entre parenteses. Por exemplo,
(+2) e a funcao que adiciona algum argumento a 2,
(2+) a funcao que adiciona 2 a algum argumento,
(>2) a funcao que retorna True se um inteiro for maior que 2,
(3:) a funcao que coloca o inteiro 3 na cabeca de uma lista,
(++\n) a funcao que coloca o caractere \n ao final de uma string.
Uma secao de um operador op coloca o argumento no lado que completa a aplicacao.
Por exemplo, (op a) b = b op a e (a op) b = a op b.
109
9.8
Classes de tipos
Os tipos sobre os quais uma funcao sobrecarregada p o de atuar formam uma colecao
de tipos chamada classe de tipos (type class) em Haskell.
Quando um tipo pertence a uma classe, diz-se que ele e uma instancia da classe.
As classes em Haskell permitem uma hierarquia entre elas, juntamente com um
mecanismo de heranca, de forma similar ao mecanismo de heranca encontrado nas
linguagens orientadas a objeto.
A funcao elem, definida a seguir, quando aplicada a um elemento e a uma lista de
valores dotipo deste elemento, verifica se ele pertence ou nao a` lista, retornando um
valor booleano.
1 elem : : t > [ t ] > Bool
2 elem x [ ] = False
3 elem x ( a : y ) = x == a | | elem x y
Analisando a definicao desta funcao aplicada `a lista nao vazia, verificamos que e
feito um teste para verificar se o elemento x e igual a cabeca da lista (x==a).
Isto implica que a funcao elem so pode ser aplicada a tipos cujos valores possam ser
comparados pela funcao ==.
Os tipos que tem esta propriedade formam uma classe em Haskell.
Em Haskell, existem dois operadores de teste de igualdade: == e /=.
Para valores booleanos, eles sao definidos da seguinte forma:
1 (/=) , (==) : : Bool > Bool > Bool
2 x == y = ( x and y ) or ( not x and not y )
3 x /= y = not ( x == y )
1 c l a s s Eq t where
2 (==) , (/=) : : t > t > Bool
Foi visto que o teste de igualdade (==) e sobrecarregado, o que permite que ele
seja utilizado em uma variedade de tipos para os quais esteja definido, ou seja, para
instancias da classe Eq.
Sera mostrado agora como as classes e instancias sao declaradas, por exemplo, a
classe Visible que transforma cada valor em um String e da a ele um tamanho.
Esta classe e necessaria porque o sistema de entrada/sada de Haskell so permite a
impressao de Strings, ou seja, para que qualquer valor em Haskell seja impresso, e
necessario que ele seja primeiro transformado em um String, caso ainda nao o seja
1 c l a s s V i s i b l e t where
2 t o S t r i n g : : t > String
3 s i z e : : t > Int
A definicao inclui o nome da classe (Visible) e uma assinatura, que sao as funcoes
que compoem a classe juntamente com seus tipos.
Um tipo t para pertencer a` classe Visible tem de implementar as duas funcoes da
assinatura, ou seja, coisas visveis sao coisas que podem ser transformadas em um
String e que tenham um tamanho.
Por exemplo, para se declarar que o tipo Char seja uma instancia da classe Visible,
deve-se fazer a declaracao
1 instance V i s i b l e Char where
2 t o S t r i n g ch = [ ch ]
3 size
= 1
Uma classe em Haskell pode herdar as propriedades de outras classes, como nas
linguagens orientadas a objetos.
Como exemplo, vamos observar a classe Ord que possui as operacoes >, >=, <,
<=, max, min e compare, alem de herdar a operacao == da classe Eq.
Sua definicao e feita da seguinte forma:
111
1
2
3
4
9.9
Tipos alg
ebricos
Haskell oferece uma forma especial para a construcao de novos tipos de dados
visando modelar situacoes, como:
as enumeracoes implementadas em algumas linguagens de programacao;
tipos cujos elementos sejam n
umeros ou Strings;
as estruturas de arvores;
as estruturas de grafos.
Para construir um novo tipo de dados, usamos a declaracao data que descreve como
os elementos deste novo tipo sao construdos.
Cada elemento e nomeado por uma expressao formulada em funcao dos construtores
do tipo.
Alem disso, nomes diferentes denotam elementos tambem distintos.
Atraves do uso de pattern matching sobre os construtores, projetam-se operacoes
que geram e processam elementos do tipo de dados escolhidos.
Os tipos assim descritos, nao as operacoes, sao chamados tipos concretos e sao
modelados em Haskell atraves dos tipos algebricos.
A sintaxe da declaracao de um tip o algebrico de dados e
112
...
| <Const Valn
onde Const tipo e o construtor do tipos e Const Val1, Const Val2, ... Const Valn
sao os elementos ou valores do tipo.
Cada valor do tipo e composto de um construtor de valor, tambem conhecido por
construtor de dado, seguido, ou nao, de componentes do valor.
Vamos mostrar um exemplo caracterizando cada um destes elementos.
1 data P u b l i c a c a o = L i v r o Int String [ String ]
2
| R e v i s t a String Int Int
3
| A r t i g o String String Int
4
deriving (Show)
Por exemplo, em C, o tipo enumeracao dia util pode ser definido da seguinte forma:
1 enum d i a u t i l { segunda , t e r c a , quarta , quinta , s e x t a } ;
Este tipo pode ser definido, em Haskell, como um tipo algebrico, com a vantagem
da ausencia dos bugs citados. Senao vejamos:
1 data D i a u t i l = Segunda
2
| Terca
3
| Quarta
4
| Quinta
5
| Sexta
6
deriving (Eq, Show)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum forma { c i r c u l o , r e t a n g u l o } ;
struct ponto { f l o a t x , y } ;
struct c i r c u l o {
struct ponto c e n t r o ;
float raio ;
};
struct r e t a n g u l o {
struct ponto c a n t o ;
float base ;
float altura ;
};
struct f i g u r a {
enum forma t i p o ;
union {
struct c i r c u l o m e u c i r c u l o ;
113
16
struct r e t a n g u l o meuretangulo ;
17
} fig gemetrica ;
18 } ;
1 data Ponto = ( Float , Float )
2 data F i g u r a = C i r c u l o Ponto Float
3
| Retangulo Ponto Float Float
Tipos recursivos
Uma expressao aritmetica simples que envolve apenas adicoes e subtracoes de
valores inteiros pode ser modelada atraves de sua BNF.
Usando um tipo algebrico podemos modelar uma expressao da seguinte forma:
1 data Expr = L i t Int | Add Expr Expr | Sub Expr Expr
Podemos criar uma funcao de avaliacao que tome como argumento uma expressao e de como resultado o valor da expressao.
Para isso podemos construir a funcao eval da seguinte forma:
1
2
3
4
eval
eval
eval
eval
: : Expr
( Lit n)
(Add e1
( Sub e1
> Int
= n
e2 ) = ( e v a l e1 ) + ( e v a l e2 )
e2 ) = ( e v a l e1 ) ( e v a l e2 )
114
10 Linguagens l
ogicas
10.1
Linguagens l
ogicas
H B1 , B2 , . . . , Bn
(10.1)
C A, B
(10.2)
DC
(10.3)
D A, B
(10.4)
115
10.2
f lowery(X) rainy(X)
(10.5)
rainy(Rochester)
(10.6)
f lowery(Rochester)
(10.7)
Linguagem Prolog
homem( pedro ) .
homem( j o a o ) .
mulher ( maria ) .
mulher ( t e r e s a ) .
Assim, duas clausulas dao origem a um resolvente quando possuem predicados correspondentes, sendo um positivo (nao negado) e outro negativo (negado).
Quando o usuario realiza uma consulta o motor de inferencia tenta resolver metas,
sendo que uma meta pode conter submetas, quando nao existem mais metas para
serem satisfeitas em uma linha de resolucao, o sistema utiliza encadeamento para
tras (backtracking) em busca de outras respostas possveis.
Win-Prolog
respeita a sintaxe de Edinburgo, e a implementacao mais proxima do dialeto
um sistema comercial, mas possui uma versao para testes gratuita.
puro. E
Open Prolog
disponvel unicamente para Macintosh (Mac OS 7.5 ou superior). Respeita a
sintaxe padrao ISO, baseada na sintaxe de Edinburgo.
Ciao Prolog
distribudo gratuitamente, possui licenca Library General Public License (LGPL).
Respeita a sintaxe padrao ISO, pode ser utilizada em diversos sistemas operacionais, tais como, Windows (98, NT, 2k, XP), Linux, SunOS, Solaris, MacOS
X, entre outros.
YAP Prolog
criado pela Universidade do Porto em parceria com a Universidade Federal do
Rio de Janeiro. Uma das principais caractersticas desta implementacao e sua
velocidade. Respeita a sintaxe padrao ISO, tambem e compatvel com outras
implementacoes de Prolog, tais como, Quintus e SICStus.
SWI Prolog
software gratuito, sob a licenca Lesser GNU Public License. Pode ser utilizado
nas plataformas Windows, Linux e MacOS. Possui diversas ferramentas de
edicao graifca, tais como, J-Prolog Editor e SWI-Prolog-Editor (recomendado).
Permite a utilizacao da linguagem Prolog por outras linguagens, tais como,
C/C++ e Java.
SICStus Prolog
e um software comercial, mas possui versao gratuita para avaliacao. Respeita
a sintaxe padrao ISO e pode ser usada nas plataformas Windows (2k e XP),
Linux, Solaris 7, MacOS X e algumas distribuicoes Unix.
118
Amzi! Prolog
pode ser utilizado de maneira conjunta a IDE Eclipse, permite tambem comunicacao com outras linguagens. Possui distribuicoes gratuitas e comerciais, que
podem ser utilizadas em sistemas operacionais diversos, tais como, Windows,
Linux, Solaris e HP/UX.
Visual Prolog
bastante diferente da versao padrao de Prolog, e fortemente tipado. Tambem
conhecido como PDC Prolog ou Turbo Prolog, possui distribuicoes gratuitas e
comerciais para Windows e Linux.
Os dados representados em Prolog podem ser um dos seguintes tipos:
variaveis - devem ser iniciados com letras mai
usculas ou underscore ( ), seguidos
de qualquer caractere alfanumerico. Caso uma variavel seja definida apenas
com underscore, ela sera considerada uma variavel anonima, ou seja, nao se
deseja saber seu valor. Ex.: X, Y1, Nome, ...;
nao variaveis;
atomicas;
atomos - sao constantes expressas atraves de palavras. Devem ser iniciados
com letra min
uscula seguida de qualquer caractere alfanumerico. Caso seja
necessario definir um atomo com letra mai
uscula ou n
umero, deve se usar
aspas simples. Ex.: joao, Joao, 16, Marys, ...;
inteiros - qualquer n
umero que nao contenha um ponto (.) sera considerado
um inteiro. Caracteres ASCII entre aspas duplas sao considerados inteiros.
Ex.: 1, 6, -3, a (interpretado como 97), ...;
n
umeros em ponto flutuante - qualquer n
umero com um ponto e pelo menos
uma casa decimal. Ex.: 5.3 (correto), 7.8 (correto), 7. (incorreto);
nao atomicas;
listas - e uma sequencia de elementos ordenados. Uma lista e declarada
entre colchetes e os elementos devem ser separados por vrgula. Pode-se
separar a cabeca (1o. elemento) do corpo (demais elementos) de uma lista
utilizando |. Ex.: [ a, b, c], [a | b, c ], ... .
O comando write exibe o valor do parametro no dispositivo de sada corrente. O
dispositivo padrao e o monitor, assim, o comando write(Teste de impressao.). ira
exibir a mensagem Teste de impressao. na tela do monitor.
119
O mesmo comando pode ser utilizado para imprimir o valor de qualquer variavel.
No entanto, nao existe um comando padrao Prolog para escrita de expressoes formatadas.
Devido a isso, o SWI-Prolog utiliza comandos de extensao, um deste e o comando
writef do C-Prolog de Edinburgo. Este comando possui a seguinte sintaxe writef(Formato,
Argumentos).
Onde as opcoes para determinar a formatacao sao:
%w - imprime o termo;
%d - imprime o termo ignorando seu tipo, por exemplo, \n e impresso como
uma string.
%s - imprime o termo como uma string;
%Nc - imprime o termo de modo centralizado numa quantidade N de colunas;
%Nl - imprime o termo alinhado a` esquerda numa quantidade N de colunas;
%Nr - imprime o termo alinhado a` direita numa quantidade N de colunas;
Para gerar alguns caracteres deve se usar sequencias de escape, estas sao:
\n - cria uma nova linha;
\l - criar um separador de linha, o resultado e igual ao produzido por \n;
\r - retorna ao incio da linha;
\t - tabulacao;
\% - imprime o smbolo %;
\nnn - onde n e um n
umero decimal, produz o caractere ASCII com o codigo
informado.
O comando read le um valor no dispositivo de entrada corrente e unifica (atribui) o
valor uma variavel. O dispositivo de entrada padrao e o teclado, assim, o comando
read(X). ira ler um valor do teclado e unificar este valor com a variavel X.
Em Prolog existem dois tipos de comentarios, estes sao identificados pelos smbolos
% e /* */.
O smbolo % expressa que tudo aquilo que estiver entre ele e o final da linha deve
ser tratado como comentario.
Os smbolos /* */ indicam que tudo que estiver entre /* e */ sera tratado como
comentario, pode-se observar exemplos de comentarios no trecho de codigo abaixo:
120
10.3
Tom
Bob
Liz
Ann
Pat
Jim
1
2
3
4
5
6
genitor
genitor
genitor
genitor
genitor
genitor
(pam , bob ) .
( tom , bob ) .
( tom , l i z ) .
( bob , ann ) .
( bob , pat ) .
( pat , jim ) .
Apos a definicao desta relacao podem ser realizadas consultas no sistema, para isso
basta clicar no botao de consulta, e digitar perguntas no prompt de comandos, tal
como exibido a seguir:
1
2
3
4
3 ? g e n i t o r ( pat , jim ) .
Yes
4 ? g e n i t o r ( jim , pat ) .
No
5 ? g e n i t o r (X, bob ) .
X = pam ;
X = tom ;
No
6 ? g e n i t o r ( bob , X) .
X = ann ;
X = pat ;
No
7 ? g e n i t o r (X,Y) .
X = pam
Y = bob ;
X = tom
Y = bob ;
122
14
15
16
17
18
19
20
21
22
X =
Y =
X =
Y =
X =
Y =
X =
Y =
No
tom
liz ;
bob
ann ;
bob
pat ;
pat
jim ;
Do mesmo modo, pode-se afirmar que uma sequencia de clausulas separadas por
ponto e vrgula sera satisfeita se ao menos uma clausula for satisfeita.
Um outro conectivo importante e a negacao.
Para exemplificar a utilizacao deste conectivo serao definidos os predicados homem(x)
e mulher(x), significando que x e homem e x e mulher respectivamente.
Assim, sao definidos os fatos:
1
2
3
4
5
6
7
mulher (pam) .
homem( tom ) .
homem( bob ) .
mulher ( l i z ) .
mulher ( pat ) .
mulher ( ann ) .
homem( jim ) .
Apos definidos os fatos, podemos indagar, por exemplo, quem e a mae de bob,
ou seja, deseja-se saber quem e o genitor de bob que e mulher. Para isso, pode-se
digitar no prompt de comandos a seguinte pergunta:
1 9 ? g e n i t o r (X, bob ) , mulher (X) .
2 X = pam ;
3 No
Muitas outras consultas podem ser realizadas sobre uma base de fatos, porem, e
muito mais interessante utilizar regras, pois o poder de expressao obtido e muito
maior.
Para exemplificar o uso de regras sera definida a relacao prole(y,x), significando que
y e prole de x.
Esta relacao e a relacao inversa de genitor(x,y), assim, pode-se afirmar que y e
prole de x se x e genitor de y.
Para isso, pode-se criar a seguinte regra na janela de edicao do programa:
1 p r o l e (Y,X) : g e n i t o r (X,Y) .
124
11 ? p r o l e (X, tom ) .
X = bob ;
X = liz ;
No
Para satisfazer a regra o interpretador Prolog substituiu a variavel X ate que encontrou o fato genitor(tom, bob), o qual corresponde a parte condicional da regra
prole(Y,X) :- genitor(X,Y), apos as substituicoes adequadas.
Posteriormente foi encontrado o fato genitor(tom, liz), o qual tambem pode satisfazer a regra, nenhuma outra substituicao resultou em sucesso.
Outras relacoes podem ser definidas para o exemplo. Pode-se definir as relacoes
mae(x,y) e avos(x,y), apresentadas anteriormente como consultas.
Para isso, deve-se digitar na janela de edicao do programa as seguintes regras:
1 mae (X,Y) : g e n i t o r (X,Y) , mulher (X) .
2 avos (X, Z ) : g e n i t o r (X,Y) , g e n i t o r (Y, Z ) .
Um outra relacao que pode ser definida e a relacao irma(x,y), significando x e irma
de y.
Note que deve-se ter cuidado na definicao deste relacionamento, pois, pode-se definir
este atraves da seguinte regra:
1 irma (X,Y) : g e n i t o r (Z ,X) , g e n i t o r (Z ,Y) , mulher (X) .
Porem, esta regra permite uma pessoa seja irma de si mesma, para comprovar isso
basta realizar a seguinte consulta:
125
O programador deve estar atento a especificacoes deste tipo, para descrever corretamente a relacao e necessario indicar que x e y precisam ser diferentes, assim a regra
correta seria:
1 irma (X,Y) : g e n i t o r ( Z ,X) , g e n i t o r ( Z ,Y) , mulher (X) , not (X=Y
) .
Uma diferenca basica entre uma regra e um fato e que um fato e sempre uma informacao verdadeira, ja uma regra precisa ser avaliada para que se possa determinar
se esta e verdadeira ou nao.
Regras podem depender diretamente de um fato, como no exemplo anterior ou de
outras regras (inclusive dela mesma).
A recursao e um dos elementos mais importantes da linguagem Prolog, este conceito permite a resolucao de problemas significativamente complexos de maneira
relativamente simples.
A construcao de uma regra recursiva sera apresentada atraves da definicao da relacao
descendente(x,y), significando que y e um descendente de x.
possvel definir esta relacao utilizando a relacao genitor, assim, uma descendencia
E
direta, ou seja, quando x e genitor de y, seria representada com a seguinte regra:
1 d e s c e n d e n t e (X, Z ) : g e n i t o r (X, Z ) .
Para outros casos de descendencia, que nao uma descendencia direta, poderiam ser
utilizadas seguintes regras:
1 d e s c e n d e n t e (X, Z ) : g e n i t o r (X,Y) , g e n i t o r (Y, Z ) .
2 d e s c e n d e n t e (X, Z ) : g e n i t o r (X,Y) , g e n i t o r (Y,W) , g e n i t o r (W, Z ) .
3 . . .
porem, esta solucao seria limitada e trabalhosa. Usando recursao e possvel obter
uma solucao bem mais simples e completa para a relacao de descendencia.
Para isso, e necessario definir a seguinte afirmacao x e um descendente de z se existe
um y, tal que, x seja genitor de y e y seja um descendente de z, a seguinte regra
descreve isso:
1 d e s c e n d e n t e (X, Z ) : g e n i t o r (X,Y) , d e s c e n d e n t e (Y, Z ) .
126
13 ? d e s c e n d e n t e (pam ,X) .
X = bob ;
X = ann ;
X = pat ;
X = jim ;
No
1 X = bob , Z = pat
e entao, a meta atual e trocada para nova meta genitor(bob, pat). Como esta e
satisfeita por um fato presente no programa o procedimento acaba.
10.4
Listas
pam e a cabeca, enquanto [liz, pat, ann, tom, bob, jim] e a cauda. Observe que a
cauda e uma nova lista, que por sua vez tambem possui cabeca e cauda.
Assim, pode-se dizer que o u
ltimo elemento de uma lista possui uma cauda vazia
(uma lista vazia).
Pode-se especificar que um elemento de uma lista e tambem uma lista, assim, podese representar listas tais como:
129
possvel separar as partes de uma lista utilizando uma barra vertical, assim, pode E
se escrever Lista = [cabeca | cauda]. Com isso, e possvel determinar as seguintes
listas:
1 [a | b, c] = [a, b, c]
possvel realizar uma serie de operacoes sobre listas, as secoes seguintes exibem
E
algumas destas acoes.
Para se checar se um determinado elemento pertence `a uma lista deve-se utilizar a
relacao member(x,y), que indica se x pertence a` y, por exemplo:
1
2
3
4
5
6
3 ? member ( a , [ a , b , c ] ) .
Yes
4 ? member ( a , [ [ a , b ] , c ] ) .
No
5 ? member ( [ a , b ] , [ [ a , b ] , c ] ) .
Yes
130
6 ? conc
L = [ a ,
No
7 ? conc
L = [ a ,
( [ a , b ] , [ c ] , L ).
b , c ] ;
( [ a ] , [ b , c ] , L ).
b , c ] ;
As regras definidas tambem podem ser usadas para decompor uma lista em suas
componentes. Para checar isso, basta realizar a seguinte consulta:
1
2
3
4
5
6
6 ?
L1 =
L1 =
L1 =
L1 =
No
conc ( L1 , L2 , [ a , b , c ] ) .
[ ] L2 = [ a , b , c ] ;
[ a ] L2 = [ b , c ] ;
[ a , b ] L2 = [ c ] ;
[ a , b , c ] L2 = [ ] ;
A adicao de um elemento `a uma lista pode ser definida de modo simples. Para isso,
basta inserir o elemento no incio da lista, esta relacao e definida atraves da seguinte
regra:
1 i n s e r e (X, L , [X | L ] ) .
7 ?
L = [
No
8 ?
L = [
No
insere ( a , [ b , c ] , L ) .
a , b , c ];
insere ( [ 1 , 2 ] , 3 , L ) .
[ 1 , 2 ] | 3 ];
131
Ja a segunda regra exclui um elemento que pertence a cauda da lista. Vale salientar que esta implementacao nao exclui todos os elementos existentes na lista que
correspondam ao elemento passado como argumento.
Definidas as regras podem ser realizadas as seguintes consultas:
1
2
3
4
5
6
7
8
9
10
9 ? e x c l u i ( a , [
L = [ b , c ] ;
No
10 ? e x c l u i ( b , [
L = [ a , c ] ;
No
11 ? e x c l u i ( c ,
L = [ a , b , c ]
L = [ a , c , b ]
No
a , b , c ] , L ).
a , b , c ] , L ).
[ a , c , b , c ] , L ).
;
;
10.5
Aritm
etica
Geralmente, quando se escreve uma expressao matematica a notacao infixa e utilizada, por exemplo 2 a + b c, onde 2, a, b e c sao argumentos e + e sao
operadores.
Em Prolog uma expressao e representada internamente como uma arvore.
Uma maneira de representar em Prolog a expressao em questao utiliza notacao
prefixa, a expressao seria representada como +((2, a), (b, c)).
Porem, por ser mais usual a representacao infixa tambem e compreendida pela
linguagem.
Prolog possui definidos operadores para as quatro operacoes: +, , , /, para
realiza soma, subtracao, multiplicacao e divisao, respectivamente. Para se obter o
resultado de uma operacao e necessario utilizar o operador is, tal como ilustrado
nas consultas abaixo:
1
2
3
4
5
6
7
3 ? X i s 2 + 3 .
X = 5;
No
4 ? X i s 4 1 .
X = 3;
No
5 ? X i s 2 5 .
132
8
9
10
11
12
13
19
X = 10;
No
6 ? X i s 9 / 2 .
X = 4.5;
No
Para o SWI-Prolog a operacao / representa uma divisao real, para se obter uma
divisao inteira deve-se usar o operador //.
A precedencia de operacoes aritmeticas em Prolog e a mesma precedencia adotada na
matematica, assim, quando necessario devem ser utilizados parenteses para descrever
uma expressao corretamente.
Alguns dos operadores reconhecidos sao:
mod - para obter o resto da divisao;
b - para potenciacao;
cos - funcao cosseno;
sin - funcao seno;
tan - funcao tangente;
exp - exponencial;
ln - logaritmo natural;
log - logaritmo;
sqrt - raiz quadrada.
Existem tambem operacoes de conversao, algumas sao automaticas outras precisam
ser explicitamente solicitadas.
Um exemplo de conversao automatica ocorre quando um n
umero inteiro e relacionado em uma expressao com n
umeros de ponto flutuante, automaticamente os
inteiros sao convertidos para n
umeros de ponto flutuante.
Algumas conversoes explcitas nativas sao:
integer(X) - converte X para inteiro;
float(X) - converte X para ponto flutuante.
Prolog tambem possui operacoes para comparacao, os operadores sao:
> maior que;
133
18 ? 1 + 2 = 2 + 1 .
No
19 ? 1 + 2 =:= 2 + 1 .
Yes
20 ? 1 + A = B + 2 .
A = 2
B = 1;
No
10.6
Corte de fluxo
estas regras computam a relacao de maneira correta, porem, elas sao exclusivas, ou
seja, quando a primeira obtem sucesso a segunda ira falhar.
Porem, Prolog sempre executa as duas regras, utilizando encadeamento para tras,
o que para esta relacao resulta apenas em ineficiencia.
A mesma relacao pode ser obtida, porem, sem gerar processamento ineficiente utilizando corte. Para isso, as regras seriam escritas como:
1 maximo (X, Y,X) : X >= Y, ! .
2 maximo (X, Y,Y) : X < Y.
com isso, caso a primeira regra obtenha sucesso a segunda regra nao sera executada.
A execucao do programa com corte nao altera o resultado deste, o corte apenas evita
que sejam realizadas buscas desnecessarias.
No entanto, o uso do corte exige muito mais atencao do programador.
Isso ocorre pois um programa sem cortes pode ter a ordem de suas clausulas e regras
modificadas sem alterar o significado do mesmo.
Por sua vez, um programa que possua cortes pode ter seu significado alterado caso
suas clausulas sejam reordenadas.
O seguinte exemplo ilustra estas afirmacoes. Sejam dadas as regras:
1 p : a , b .
2 p : c .
p (a b) c.
(10.8)
Com esta formula podemos modificar a ordem das clausulas e seu significado nao
sera alterado.
Porem, caso seja utilizado corte, tal como nas seguintes regras:
1 p : a , ! , b .
2 p : c .
135
p (a b) (a c).
(10.9)
p c (a b).
(10.10)
136
Refer
encias Bibliogr
aficas
[1] SCOTT, M. L. Programming Languages Pragmatics. 3. ed. United States: Elsevier,
2009. 941 p.
137