orlando/clp.doc  · web viewalguns de seus principais problemas são a inexistência de herança...

62
1 Características das Linguagens de Programação CONTEÚDO PROGRAMÁTICO 1. Características importantes de linguagens de programação 2. Tipos de linguagens 2.1 Procedurais 2.2 Funcionais 2.3 Baseadas em regras 2.4 Orientadas a objetos 3. Tipos de dados 3.1 Características 3.2 Arquitetura 3.3 Principais tipos 4. O processo de geração de código executável 4.1 Compilação 4.2 Interpretação 4.3 Montagem

Upload: vanxuyen

Post on 10-Nov-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

1

Características das Linguagens de Programação

CONTEÚDO PROGRAMÁTICO

1. Características importantes de linguagens de programação

2. Tipos de linguagens

2.1 Procedurais2.2 Funcionais2.3 Baseadas em regras2.4 Orientadas a objetos

3. Tipos de dados

3.1 Características3.2 Arquitetura3.3 Principais tipos

4. O processo de geração de código executável

4.1 Compilação4.2 Interpretação4.3 Montagem4.4 Link-edição4.5 Técnicas mistas

5. Subrotinas

5.1 O conceito de registro de ativação5.2 Encadeamento estático e dinâmico5.3 Parâmetros e variáveis locais

26. Gerência de memória

7. Alocação dinâmica

7.1 Gerência de heap7.2 Coleta de lixo

BIBLIOGRAFIA

Conceitos de Linguagens de Programação, Robert Sebesta, Editora Bookman, 2000.

Conceitos de Linguagens de Programação (2a edição), Carlo Ghezzi, Mehdi Jazayeri, Editora Campus, 1985.

Programming Language Concepts (3a edição), Carlo Ghezzi, Mehdi Jazayeri, John Wiley, 1997.

Programming Languages: Design and Implementation (4a

edição), Terrence W. Pratt, Martin V. Zelkowitz, Prentice Hall, 2000.

Foundations for Programming Languages, John C. Mitchell, MIT Press, 1996.

1. CARACTERÍSTICAS IMPORTANTES DE LINGUAGENS DE PROGRAMAÇÃO

Por que estudar Linguagens de Programação?

Para melhorar o entendimento das linguagens que são usadas. Para aumentar seu vocabulário com construções de programação

úteis. Para poder escolher melhor uma linguagem de programação. Para tornar mais fácil o aprendizado de uma nova linguagem. Para facilitar o projeto de uma nova linguagem.

3O que torna uma linguagem boa?

Clareza, simplicidade e unidade de seus conceitos. Clareza da sintaxe. Design apropriado a determinado tipo de aplicação. Suporte a determinadas abstrações. Facilidade de verificação dos programas. Ambiente de programação. Portabilidade dos programas. Custo do uso.

Custo de uso de uma linguagem

Execução do Programa. Compilação. Criação, testes e uso do programa. Manutenção.

Quais são as características desejáveis em uma linguagem?

Legibilidade (principalmente). Capacidade de escrita. Eficiência do código gerado. Eficiência da compilação. Disponibilidade de ferramentas. Disponibilidade de Bibliotecas.

Legibilidade em Pascal

Legibilidade via pontuação

4

As características desejáveis em uma linguagem de programação são vistas também como critérios para se avaliar essa mesma linguagem. Sendo assim, a legibilidade, a capacidade de escrita, a confiabilidade e o custo definem os principais critérios de avaliação das linguagens.

Legibilidade

Antes de 1970, o desenvolvimento de software era muito imaginado em termos da escrita do código. Na década de 70, o conceito de ciclo de vida do software foi desenvolvido; a codificação foi relegada a um papel menos importante, e a manutenção foi reconhecida como uma parte importante do ciclo, especialmente em termos de custo.

Uma vez que a facilidade de manutenção é determinada, em grande parte, pela legibilidade dos programas, ela tornou-se uma medida importante da qualidade dos programas e das linguagens. A legibilidade deve ser considerada no contexto do domínio do problema. Por exemplo, se um programa que descreve uma computação tiver sido escrito em uma linguagem não projetada para esse uso, o programa pode ser antinatural e enrolado, difícil de ser lido.

Características que contribuem para a legibilidade

Simplicidade global Ortogonalidade Instruções de controle Tipos de dados e estruturas Sintaxe

Capacidade de escrita

5

Capacidade de escrita é uma medida de quão facilmente uma linguagem pode ser usada para criar programas e para um domínio de problema escolhido. A maioria das características da linguagem que afeta a legibilidade, também afeta a capacidade de escrita.

Fatores que influenciam a capacidade de escrita

Simplicidade e ortogonalidade Suporte para abstração Expressividade

Confiabilidade

Diz-se que um programa é confiável se ele se comportar de acordo com suas especificações sob todas as condições.

Recursos que afetam a confiabilidade de programas

Verificação de tipos Manipulação de exceções Aliasing Legibilidade e capacidade de escrita

Custo

O custo final de uma linguagem de programação é uma função de muitas de suas caraterísticas. Primeiro, há o custo de treinamento dos programadores para usar a linguagem; em segundo lugar, há o custo para se escrever programas na linguagem. Em terceiro lugar, há o custo para compilar programas na linguagem.

Em quarto lugar, o custo para executar programas é grandemente influenciado pelo projeto da linguagem. Se ela exigir muitas verificações de tipos durante a execução, proibirá a execução rápida de código, independente da qualidade do compilador.

O quinto fator é o custo do sistema de implementação da linguagem. Uma linguagem de programação cujo sistema de implementação seja caro, ou rode somente em hardware caro, terá muito menos chance de tornar-se popular.O sexto fator é o custo da má confiabilidade. Se o software falhar em um

6sistema crítico, como uma usina de energia nuclear, o custo poderia ser muito elevado. As falhas em sistemas não-críticos também podem ser muito caras em termos de futuro comercial ou de ações judiciais em função de programas defeituosos.

A consideração final é o custo de manutenção dos programas, que inclui tanto correções como modificações para adicionar novas capacidades. O custo da manutenção do software depende de uma série de características da linguagem, mas principalmente da legibilidade.

2. TIPOS DE LINGUAGENS

As linguagens de programação muitas vezes são classificadas em quatro categorias: procedurais (ou imperativas), funcionais, baseadas em regras (ou lógicas) e orientadas a objetos.

As linguagens de marcação (markup), como a linguagem HTML, às vezes, são confundidas com as de programação. Porém, as linguagens de programação não especificam computações; ao contrário, elas descrevem a aparência geral de documentos. Todavia, muitos dos critérios de projeto e de avaliação também se aplicam às linguagens de marcação. Afinal de contas, é evidentemente importante que o código de marcação seja fácil de ler e de escrever.

2.1 Procedurais

As linguagens procedurais são um dos sub-tipos das linguagens chamadas “estruturadas”. Essas linguagens se diferenciam das linguagens não estruturadas, pela existência de estruturas de controle, que entre outras coisas promovem o teste de condições (if-then-else), controlam a repetição de blocos de código (for, while, do), fazem a seleção de alternativas (switch, case), e dividem o código do programa em módulos chamados de funções ou procedimentos.

As linguagens procedurais são caracterizadas pela existência de algoritmos, que determinam uma seqüência de chamadas de procedimentos, que constituem o programa. Essa característica é colocada em contraposição às linguagens funcionais, onde se

7descrevem expressões que caracterizam um certo tipo de conhecimento.

As linguagens procedurais mais comuns são o C, o Pascal e o Fortran. Algumas linguagens mais sofisticadas, tais como o Ada, Modula-2 e Modula-3 foram desenvolvidas para corrigir deficiências e explorar novos contextos onde o C, Pascal e Fortran não eram totalmente adequados.

C

O desenvolvimento da linguagem C está intimamente conectado ao desenvolvimento do sistema operacional Unix. Durante o desenvolvimento do Unix, foi necessária a criação de uma linguagem de alto nível para a programação do sistema, uma vez que o esforço de programação em linguagem assembly era muito grande. Sendo assim, um grupo de pesquisadores do Bell Labs, incluindo Ken Thompson e Dennis Ritchie, desenvolveram uma linguagem de alto nível a qual batizaram de “linguagem B” (sucessora de uma primeira tentativa –– a linguagem A).

A linguagem B foi posteriormente substituída por outra que a sucedeu, a “linguagem C”. Com o desenvolvimento de um compilador C portátil (por Steve Johnson, do Bell Labs), o Unix pôde ser transportado facilmente para diversas plataformas, tornando tanto a linguagem C como o sistema operacional Unix um sucesso para diferentes plataformas computacionais.

O grande sucesso da linguagem C está intimamente ligado ao sucesso do sistema operacional Unix, sendo que quase todas as implementações do Unix (para diferentes arquiteturas) possuem um compilador C nativo. Posteriormente, devido a esse sucesso, a linguagem C passou a ser utilizada também fora do ambiente Unix, como, por exemplo, em PC´s (DOS, Windows e OS/2), com a introdução dos compiladores da Borland e Microsoft.

Atualmente, a linguagem C é descrita pela norma ISO - ISO/IEC 9899:1990.

Pascal

8

Pascal é uma linguagem que foi originada no Algol, tendo sido desenvolvida por Niklaus Wirth para o CDC 6600, por volta de 1967-68, como uma ferramenta de instrução para programação elementar. Apesar de ter sido desenvolvida inicialmente apenas com propósitos educacionais, tornou-se posteriormente uma linguagem de propósitos gerais, tendo-se tornado inclusive o ancestral de diversas outras linguagens, tais como Modula-2 e Ada.

É uma linguagem que enfatiza bastante sua sintaxe, evitando artifícios encontrados em outras linguagens, tais como “casts”, para efetuar conversão de tipos, ou outros artifícios que poderiam tornar a linguagem mais flexível. Neste sentido, o Pascal foi perdendo lugar para o C, que tem uma estrutura semelhante, e ao mesmo tempo dá ao programador a flexibilidade necessária para resolver problemas onde a sintaxe rígida do Pascal não permite muita elaboração.

Fortran

O “IBM Mathematical FORmula TRANslating system”, ou FORTRAN, foi a primeira linguagem de alto nível (ou seja, superior ao assembly). Desenvolvida por Backus, dentre outros, a linguagem foi introduzida em 1958, direcionada primeiramente para cálculos científicos, de tal forma que facilidades como manipulações de “strings” eram quase inexistentes, e a única estrutura de dados existente era o “array”.

Entretanto, tratou-se de um grande salto qualitativo, quando comparado ao Assembly. Tornou-se uma linguagem de grande aceitação, sendo utilizada até hoje em aplicações numéricas.

A primeira norma regulamentando a linguagem veio entretanto somente em 1966, quando a “American National Standards Institute” (ANSI) publicou uma norma que unificou a linguagem, sendo referenciada como FORTRAN´66. Depois disso, a linguagem passou por diversas revisões, de modo a incorporar idéias da programação estruturada. As mais notáveis revisões, foram o FORTRAN´77 e o FORTRAN´90, introduzidas em 1978 e 1991 , respectivamente.

O FORTRAN´90 incorpora diversas características das linguagens modernas de programação, dentre estas o uso de “arrays” dinâmicos, “records”, módulos e apontadores, derivação de tipos e “overload” de operadores, um melhor sistema de declarações e o uso de protótipos e

9modernas estruturas de controle (SELECT CASE, EXIT, ...).

O FORTRAN´90 é descrito pela norma internacional ISO/IEC 1539:1991 (E). O comitê X3J3 da ISO, que é dedicado exclusivamente ao desenvolvimento do FORTRAN, está, no momento, preparando uma nova versão da linguagem, que deverá incluir, dentre outros, suporte para processamento paralelo e programação orientada a objetos.

Ada

Ada é uma linguagem de programação moderna e avançada, planejada e padronizada de modo a encorajar alguns princípios fundamentais da engenharia de software: confiabilidade, portabilidade, modularidade, reusabilidade, eficiência, facilidade de manutenção, ocultamento de informação, tipos abstratos de dados, generalidade, programação concorrente e mais recentemente programação orientada a objetos.

Ada foi introduzida em 1978, desenvolvida por um grupo de projeto internacional, como resposta aos requisitos levantados pelo Departamento de Defesa dos Estados Unidos (DoD).

Seu desenvolvimento foi inspirado originalmente pelo Pascal e pelo Fortran 77. Em 1983, a linguagem foi transformada em uma norma, a ANSI/MIL-STD-1815A. Em 1995, uma nova norma, a ISO/IEC 8652:1995(E) introduziu o chamado Ada 95, onde uma série de inovações foram incorporadas.

Dentre outras, o Ada 95 inclui suporte para programação orientada a objetos, sincronização orientada a dados, programação in-the-large e sistemas de tempo real.

Uma das principais vantagens de Ada em relação às outras linguagens procedurais mais usuais é seu suporte para programação em tempo real, o que inclui primitivas para sincronização de processos e programação concorrente.

Modula-2

Modula-2 é uma linguagem de programação desenvolvida por Niklaus Wirth (o mesmo que desenvolveu Pascal), no final dos anos 70. É uma das linguagens descendentes do Pascal, assim como Oberon e Modula-3. Suas principais características são a existência de um rígido

10sistema de tipos, módulos, uma rica variedade de tipos diferentes, um poderoso conjunto de estruturas de controle, tipos de procedimentos, tipos opacos e co-rotinas.

Até há pouco tempo atrás, não existia uma norma formal para a linguagem Modula-2. O texto clássico descrevendo a linguagem pode ser encontrado no livro “Programming in Modula-2” 3a. edição, publicado pela Springer-Verlag em 1985.

Um comitê da ISO, o ISO/JTC1/SC22/WG13 vem, desde 1987, estendendo a linguagem a cada ano. O resultado dos esforços desse comitê resultou na norma ISO IS10514, que foi votada recentemente e é agora a norma oficial para a linguagem. O WG13 estuda no momento duas normas adicionais, uma que define Modula-2 orientado a objetos e outra que determina facilidades genéricas para a linguagem.

Modula-3

Modula-3 é outra linguagem descendente do Pascal e do Modula-2, desenvolvida no final dos anos 80, pela Digital Equipment Corporation e a Olivetti. Modula-3 corrige diversas deficiências do Pascal e do Modula-2, no que concerne às práticas modernas de engenharia de software.

Em particular, o Modula-3 mantém a simplicidade e segurança no uso dos tipos, ao mesmo tempo que provê facilidades no manuseio de exceções, concorrência, programação orientada a objetos e “garbage collection” automático.

Modula-3 é ao mesmo tempo uma linguagem prática para a implementação de projetos de software e uma excelente linguagem para o ensino de linguagens de programação.

Assim como C, Modula-3 permite que o programador tenha acesso direto ao hardware, por meio de instruções especiais (programação em baixo nível), ao mesmo tempo em que tem mecanismos que protegem o acesso a esses recursos (tais como memória, acesso a discos etc), durante as instruções “normais”.

2.2 Funcionais

11

Linguagens funcionais são linguagens que evidenciam um estilo de programação diferente das linguagens procedurais, chamado de programação funcional. A programação funcional enfatiza a avaliação de expressões, ao invés da execução de comandos. As expressões nessas linguagens são formadas utilizando-se funções para combinar valores básicos. A linguagem funcional mais conhecida é a LISP. A linguagem Scheme também é freqüentemente citada, por ser uma variante simplificada do LISP.

Diversas outras linguagens funcionais são encontradas na literatura, por exemplo, ASpecT, Caml, Clean, Erlang, FP, Gofer, Haskell, Hope, Hugs, Id, IFP, J, Miranda, ML, NESL, OPAL e Sisal.

As diversas linguagens funcionais tiveram origem no Cálculo (lambda) o qual é todo definido com o uso de símbolos e com o conceito de funções da matemática.

2.2.1 Características Iniciais das Linguagens Funcionais

Nomes e Valores

Linguagens Imperativas Linguagens Funcionais<comando1>;<comando2>;<comando3>;

<nome> := <expressão>;

<função1> (<função2> (<função3> ...) ...)

Ordem de Execução

Linguagens Imperativas

T := X; X := Y; T := X;X := Y; T := X; Y := T;Y := T; Y := T; X := Y;

Linguagens Funcionais

FUNCTION F (X,Y,Z : INTEGER) : INTEGER;

12BEGIN .... END;

FUNCTION A (P : INTEGER) : INTEGER;BEGIN .... END;

FUNCTION B (Q : INTEGER) : INTEGER;BEGIN .... END;

FUNCTION C (R : INTEGER) : INTEGER;BEGIN .... END;

F (A(0),B(0),C(0));

Execução Repetitiva

Linguagens Imperativas

SUM1 := A[1];SUM2 := SUM1 + A[2];SUM3 := SUM2 + A[3];

I := 0;SUM := 0;WHILE I < N DO BEGIN I := I + 1; SUM := SUM + A[I]; END;

Linguagens Funcionais

FUNCTION SUM (A : ARRAY [1..N] OF INTEGER; I,N : INTEGER) : INTEGER;

BEGIN IF I > N THEN SUM := 0 ELSE SUM := A[I] + SUM (A,I+1,N);END;

SUM (B,1,M)

B[1] + SUM (B,2,M) =

13B[1] + B[2] + SUM (B,3,M) =B[1] + B[2] + ... + B[M] + SUM (B,M+1,M) =B[1] + B[2] + ... + B[M] + 0

Estruturas de Dados e Funções como Valor

As linguagens funcionais fornecem representações explícitas para as estruturas de dados.

As linguagens funcionais permitem que as funções possam ser tratadas como valor.

TYPE OPTYPE = (ADD,SUB,MULT,QUOT);

FUNCTION ARITH (OP : OPTYPE) : FUNCTION;

FUNCTION SUM (X,Y : INTEGER) : INTEGER;BEGIN SUM := X + Y; END;

FUNCTION DIFF (X,Y : INTEGER) : INTEGER;BEGIN DIFF := X - Y; END;

FUNCTION TIMES (X,Y : INTEGER) : INTEGER;BEGIN TIMES := X * Y; END;

FUNCTION DIVIDE (X,Y : INTEGER) : INTEGER;BEGIN DIVIDE := X DIV Y; END;

BEGIN CASE OP OF

ADD : ARITH := SUM;SUB : ARITH := DIFF;MULT : ARITH := TIMES;QUOT : ARITH := DIVIDE;

END;END;

ARITH (ADD); ARITH (SUB); ARITH (ADD)(3,4);

Expressões do Cálculo

<expressão> ::= <nome>|<função>|<aplicação>

14<função> ::= <nome>.<corpo><corpo> ::= <expressão>

EXEMPLO:

x.x primeiro.segundo.primeiro f.a.(f a)

<aplicação> ::= (<expressão de função> <expressão de argumento>)<expressão de função> ::= < expressão><expressão de argumento> ::= <expressão>

EXEMPLO:

(x.x a.b.b)

LISP

O LISP (LISt Processor) é uma linguagem de programação que foi desenvolvida utilizando-se idéias oriundas do estudo da inteligência artificial. Sua constituição é tal que programas escritos em LISP tendem a ser um modelo que emula as habilidades cognitivas do ser humano. O elemento básico utilizado pelo LISP são os símbolos, eminentemente símbolos alfanuméricos.

A partir de 1984, o LISP começou a desenvolver-se em diversos dialetos, sem que um deles dominasse marcadamente os outros. Em um esforço conjunto de um seleto grupo de pesquisadores em linguagens de programação, representando diversas instituições, desenvolveu-se o Common LISP, integrando com sucesso diversas características positivas das diferentes versões do LISP que circulavam na época.

O Common LISP é um dialeto do LISP, sucessor do MacLISP e influenciado marcadamente pelo ZetaLISP e de certa forma também pelo Scheme e pelo InterLISP. O Common LISP é hoje utilizado como um padrão comercial para a linguagem LISP, sendo suportado por um grande número de companhias. Posteriormente, o Common LISP foi enriquecido pela adição de funções genéricas, dando origem a uma variante sua com capacidade de programação orientada a objetos chamada CLOS (Common LISP Object System).

15Exemplos da Linguagem LISP:

>(+ 2 3)5

>(+ 2 (* 3 4))14

>(CAR '(A B C))A

>(CDR '(A B C))(B C)

>(CONS 'A '(B C))(A B C)

>(CONS '+ '(2 3))(+ 2 3)

>(CDR (CONS '+ '(2 3)))(2 3)

>(CAR (CONS '+ '(2 3)))+

>(LIST 'A 'B '3 'D)(A B 3 D)

>(LIST)NIL

Scheme

A linguagem Scheme é um dialeto do LISP que enfatiza a elegância conceitual e a simplicidade. É definida pelo padrão IEEE P1178, sendo substancialmente menor que o Common LISP. Sua especificação tem aproximadamente 50 páginas, quando comparadas às 1300 páginas do Common LISP.

O Scheme é freqüentemente utilizado no estudo de linguagens de programação devido à sua habilidade em representar várias abstrações de programação com um conjunto bem limitado de primitivas.

2.3 Baseadas em regras

As linguagens baseadas em regras, também conhecidas como linguagens lógicas ou declarativas utilizam a lógica dos predicados como base para a definição das cláusulas que irão definir o comportamento esperado do sistema. Tal comportamento é retratado como declarações que envolvem proposições da lógica simbólica.Proposições

Os objetos nas proposições de programação lógica são representados

16por termos simples, constantes ou variáveis. Uma constante é um símbolo que representa um objeto. Uma variável é um símbolo que pode representar diferentes objetos em diferentes tempos, ainda que em um sentido bem mais próximo da matemática do que as variáveis em uma linguagem de programação imperativa.

As proposições mais simples, chamadas proposições atômicas consistem em termos compostos. Um termo composto é um elemento de uma relação matemática.

Um termo composto é formado por duas partes: um functor, o símbolo de função que nomeia a relação, e uma lista ordenada de parâmetros. Um termo composto com um único parâmetro é uma 1-tupla; um com dois parâmetros é uma 2-tupla e assim por diante.

EXEMPLO:

homem (jake)gosta (bob, bife)

As proposições são declaradas em dois modos: um, em que a proposição é definida como verdadeira, e outro, em que a verdade da proposição é algo que precisa ser determinado. Em outras palavras, as proposições podem ser declaradas como fatos ou como consultas.

As proposições compostas têm duas ou mais proposições atômicas, ligadas por conectores lógicos ou operadores da mesma maneira que as expressões lógicas são construídas nas linguagens imperativas.

Nome Símbolo Exemplo SignificadoNegação a não aConjunção a b a e bDisjunção a b a ou bEquivalência a b a é equivalente a bImplicação

a ba b

a implica bb implica a

EXEMPLO:

a b c a b d

Variáveis podem aparecer em proposições, mas somente quando

17introduzidas por símbolos especiais, chamados quantificadores. O cálculo de predicados inclui dois quantificadores: universal e existencial.

Nome Exemplo

Significado

Universal X.P Para todo X, P é verdadeiroExistencial X.P Existe um valor de X tal que P seja

verdadeiro

EXEMPLO:

X.(mulher(X) humano(X))X.(mãe(mary,X) homem(X))

Cálculo de Predicados

O cálculo de predicados consiste de um grupo de proposições lógicas que retratam fatos e uma(s) pergunta(s) que será(ão) deduzidas a partir desses fatos. Para se processar tal dedução, aplica-se um método conhecido como resolução.

Resolução é uma regra de inferência que permite a computação das proposições inferidas a partir de proposições dadas. O conceito é o seguinte:

Sejam duas proposições com as formas:

P1 P2Q1 Q2

Se P2 for idêntica à Q1, podemos substituí-las por T, logo:

P1 TT Q2

Nesse caso, deduz-se que P1 Q2.

O processo de inferir tal proposição a partir das duas proposições originais é uma resolução.

EXEMPLO:

18mãe (joanne,jake) mais velho (joanne,jake)mais velho (joanne,jake) mais sábio (joanne,jake)

A partir dessas proposições, a seguinte pode ser construída usando-se a resolução:

mãe (joanne,jake) mais sábio (joanne,jake)

A mecânica dessa construção de resolução é simples: os termos do lado esquerdo das duas proposições são unidos por um E para formar o lado esquerdo da nova proposição. Então, a mesma coisa é feita para obter o lado direito dela. Em seguida, o termo que aparece em ambos os lados é todo removido.

O processo é exatamente o mesmo quando as proposições têm termos múltiplos em qualquer um ou em ambos os lados. O esquerdo contém inicialmente todos os termos dos lados correspondentes nas duas proposições dadas. O novo lado direito é similarmente construído. Então, o termo que aparece em ambos os lados da nova proposição é removido.

EXEMPLO:

pais (bob,jake) pai (bob,jake) mãe (bob,jake)pai (bob,jake) pai (jake,fred) avô (bob,fred)

A resolução diz que:

EXEMPLO:

pais (bob,jake) pai (jake,fred) mãe (bob,jake) avô (bob,fred)

O resultado acima tem todas, a não ser uma das proposições atômicas originais –– pai (bob,jake) –– que foi eliminado por aparecer em ambos os membros. Na linguagem natural, poderia-se falar:

SEbob é um dos pais de jake, implica que bob é ou o pai ou a mãe de jakeEbob é o pai de jake e jake é o pai de fred implica que bob é o avô de fred

19ENTÃOse bob é um dos pais de jake e jake é o pai de fred, então ou bob é a mãe de jake ou bob é o avô de fred

A resolução é, de fato, mais complexa do que os exemplos acima, em particular, a presença de variáveis em proposições exige que a resolução encontre valores para essas variáveis, permitindo que o processo de comparação seja bem sucedido. Esse processo de determinação de valores úteis é chamado de unificação.

A atribuição temporária de valores a variáveis para permitir a unificação é chamada instanciação.

Um propriedade crucialmente importante da resolução é a sua capacidade de detectar qualquer inconsistência em um conjunto dado de proposições. Tal propriedade permite que a resolução seja usada para demonstrar teoremas, o que pode ser feito da seguinte maneira:

Pode-se imaginar uma prova de teorema em termos de cálculo de predicados como determinado conjunto de proposições pertinentes, com a negação do próprio teorema declarada como uma nova proposição.

Esse é negado a fim de que a resolução possa ser usada para prová-lo, descobrindo uma inconsistência. Essa é a prova pela contradição. Tipicamente, as proposições originais são chamadas hipóteses, e a negação do teorema é chamada meta.

Teoricamente, este é um processo válido e útil. O tempo necessário para a resolução, entretanto, pode ser um problema. Apesar da resolução ser um processo finito quando o conjunto de proposições é finito, o tempo necessário para encontrar uma inconsistência em um banco de dados de proposições grande pode ser enorme.

A demonstração de teoremas é a base da programação lógica. Grande parte do que é computado pode ser expresso na forma de uma lista de fatos dados e de relações como hipóteses, e na forma de uma meta a ser inferida a partir das hipóteses, usando-se a resolução.

Programação Lógica

As linguagens usadas para a programação lógica são chamadas de declarativas porque os programas nelas escritos consistem em

20declarações em vez de atribuições e em instruções de fluxo de controle. Essas declarações são, de fato, instruções ou proposições, em lógica simbólica.

Uma das características essenciais das linguagens de programação lógicas é sua semântica, chamada de semântica declarativa. Seu conceito básico consiste de uma maneira simples de determinar o significado de cada instrução, e não depende de como esta poderia ser usada para resolver um problema.

A semântica declarativa é consideravelmente mais simples do que a das linguagens imperativas. Por exemplo, o significado de uma proposição dada em uma linguagem de programação lógica pode ser determinado concisamente a partir da própria instrução.

Em uma linguagem imperativa, a semântica de uma instrução de atribuição simples exige o exame de declarações locais, o conhecimento das regras de escopo da linguagem, e possivelmente até mesmo o exame de programas em outros arquivos apenas para determinar os tipos das variáveis na instrução de atribuição.

Então, supondo que a expressão da instrução de atribuição contenha variáveis, a execução do programa antes da instrução de atribuição deve ser rastreada para determinar os valores delas. A ação resultante da instrução, então, depende de seu contexto durante a execução.

Comparando com o simples exame de uma única instrução, sem nenhuma necessidade de considerar o contexto textual ou as seqüências de execução, é claro que a semântica declarativa é bem mais simples do que a das linguagens imperativas.

A programação tanto nas linguagens imperativas como nas funcionais é principalmente baseada em rotinas, cujo significado está no fato do programador saber o que deve ser realizado por um programa e instruir o computador a respeito de como a computação deve ser feita. Em outras palavras, o computador é tratado como um simples dispositivo que obedece ordens. Tudo que é computado deve ter todos os detalhes dessa computação redigidos.A programação nas linguagens declarativas não descrevem como um resultado deve ser computado, mas, ao contrário, descrevem a sua forma. A diferença é que se presume que o sistema de computador possa, de alguma maneira, determinar como o resultado deve ser

21obtido.

O que é necessário para oferecer tal capacidade para as linguagens de programação lógicas é um meio conciso de abastecer o computador tanto com informações relevantes tanto com um método de inferência para computar resultados desejáveis.

O cálculo de predicados fornece a forma básica de comunicação ao computador, e o método da prova, desenvolvido pela primeira vez por Robinson, fornece a técnica imperativa.

Prolog

O Prolog é uma linguagem de programação prática e eficiente, introduzida em 1973 por Alain Colmerauer e seus associados na Universidade de Marseille, com o propósito inicial de traduzir linguagens naturais. Em 1977, David Warren da Universidade de Edimburgo, implementou uma eficiente versão do Prolog, batizada de Prolog-10. A partir daí, tornou-se uma escolha natural para a resolução de problemas que envolvem a representação simbólica de objetos e relações entre objetos.

O fundamento básico por trás do Prolog é a noção de programação em lógica, onde o processo de computação pode ser visto como uma seqüência lógica de inferências. Esta noção desenvolve-se então de modo a resultar em um mecanismo automático de prova de teoremas.

Atualmente, o Prolog é utilizado em diversas aplicações na área de computação simbólica, incluindo-se aí: bases de dados relacionais, sistemas especialistas, lógica matemática, prova automática de teoremas, resolução de problemas abstratos e geração de planos, processamento de linguagem natural, projeto de arquiteturas, logística, resolução de equações simbólicas, construção de compiladores, análise bioquímica etc.

Versões diferentes do Prolog podem ser encontradas nas mais diversas plataformas, tanto na forma de compiladores como de interpretadores.

2.4 Orientadas a objetos

22As linguagens orientadas a objeto foram originadas a partir da necessidade de se organizar o processo de programação em uma linguagem. A programação orientada a objetos é por si só uma técnica.

Uma linguagem é dita uma linguagem orientada a objetos, se ela suporta o estilo de programação orientada a objetos. Um dos objetivos da engenharia de software é particionar o projeto de um programa em diversos módulos, de tal forma que o desenvolvimento do software e sua manutenção possam ser implementados com baixo custo.

No passado, os paradigmas da engenharia de software derivavam os módulos baseados na funcionalidade de um sistema. Esses módulos correspondiam basicamente a módulos procedimentais, que eram alimentados por dados, gerando novos dados.

O paradigma de orientação a objeto mudou essa concepção, idealizando a idéia de objetos, como módulos que se comunicam por meio de mensagens, encapsulando ao mesmo tempo dados e funções, por meio de um mecanismo conhecido como tipo de dados abstratos.

A primeira linguagem orientada a objetos que se tem notícia foi o Simula, desenvolvido em 1967. Posteriormente veio o Smalltalk em 1970. Atualmente, existe uma enorme diversidade de linguagens orientadas a objeto, abrangendo desde linguagens de propósito geral, até linguagens para multimídia e programação em lógica.

Simula

A linguagem Simula foi desenvolvida por O-J Dahl, B. Myhrhaug e K. Nygaard, no Centro Norueguês de Computação, em Oslo, em 1967, como uma extensão do ALGOL60. A linguagem Simula mantém algumas propriedades do ALGOL60, além de extensões tais como os conceitos de classe e co-rotina, bem como os conceitos associados de variável de referência, manipulação de textos e facilidades de entrada/saída. Um dos problemas com a Simula é que ela não provê proteção para uma classe, o que foi resolvido posteriormente no Smalltalk.

Smalltalk

23A linguagem Smalltalk foi desenvolvida em 1970. No Smalltalk, não se chama uma função, mas manda-se uma mensagem para um objeto. Dessa maneira, nada do mundo externo pode enxergar a definição de uma classe, o que protege essa classe de qualquer interferência.

Um fator característico do Smalltalk é que qualquer dado é visto como um objeto, mesmo um número. Assim, até mesmo cálculos aritméticos simples beneficiam-se da técnica de orientação a objeto. Entretanto, mandar mensagens para objetos pode significar um aumento de custo computacional que às vezes se torna um desperdício. Algumas otimizações foram efetuadas, portanto, para evitar a passagem de mensagens em funções simples, tais como operações aritméticas.

C++

O C++ é uma linguagem de programação de propósito geral, desenvolvida por Bjarne Stroustrup, nos laboratórios da AT&T Bell, no início dos anos 80, como uma evolução do C, incorporando, dentre outras, as seguintes extensões: suporte para a criação e uso de tipos de dados abstratos, suporte ao paradigma de programação orientada a objeto, além de diversas outras pequenas melhorias nas construções existentes no C.

Algumas de suas características são o uso de tipos estáticos, a definição de classes, funções virtuais e overload de operadores para o suporte à programação orientada a objeto, o uso de templates para programação genérica, além de prover facilidades de programação de baixo nível (a exemplo do C).

Depois de seu release inicial pela AT&T, em 1985, diversas implementações do C++ tornaram-se disponíveis, em mais de 24 tipos de sistemas, desde PCs até mainframes. Em 1989, o número de usuários e diferentes implementações do C++ demandaram a geração de uma norma, sem a qual seria inevitável o surgimento de diversos dialetos.

Em 1995 os comitês de normalização da ANSI e ISO C++ chegaram a um nível de estabilidade na linguagem. Basicamente, o C++ corresponde à linguagem descrita em “The C++ Programming Language (2nd edition)” , de Bjarne Stroustrup, ed. Addison Wesley, 1991. Objective-C

24Objective-C é uma linguagem de programação orientada a objetos, que corresponde a um superconjunto do ANSI-C, provendo classes e passagem de mensagens de um modo similar ao Smalltalk. A linguagem inclui, quando comparada ao C, alguns termos e construções adicionais.

Por ser também uma linguagem derivada do C, assim como o C++, ela é freqüentemente comparada a essa. O desenvolvimento do C++ se baseia no estilo de programação orientada a objetos iniciado pela linguagem SIMULA67.

O Objective-C, por seu lado, é originário do estilo de programação encontrado no Smalltalk. No C++, o tipo estático de um objeto determina quando ele pode receber uma mensagem, ao passo que no Objective-C, seu tipo dinâmico é utilizado.

O estilo de programação oriundo do SIMULA67 é mais seguro, uma vez que mais erros podem ser detectados ainda em tempo de compilação. O estilo desenvolvido pelo Smalltalk é mais flexível. Algumas das vantagens do Objective-C incluem a possibilidade de se carregar as definições das classes e métodos em tempo de execução, o fato de os objetos serem tipados dinamicamente, a possibilidade de se utilizar objetos remotos. Alguns de seus principais problemas são a inexistência de herança múltipla e a inexistência de variáveis de classe.

Java

A linguagem Java foi desenvolvida a partir de 1990 pela Sun Microsystems, como uma linguagem que pudesse executar o mesmo programa em múltiplas plataformas de hardware e software. Seu uso imediato seria a execução de programas pela Internet. Para tanto, não poderia haver nenhum vínculo da linguagem com o hardware e/ou o sistema operacional utilizado, de modo que em princípio, qualquer computador conectado à Internet e capaz de entender a linguagem, fosse capaz de executar os programas.

Outra especificação básica da linguagem seria a segurança, ou seja, como as máquinas executando os programas em Java estariam, em princípio, executando programas sem garantias de confiabilidade e procedência, um programa em Java não poderia de modo algum influir na execução de outros programas e o próprio sistema operacional. Com isso, alguns recursos como alocação dinâmica de memória e acesso a

25arquivos foram sistematicamente eliminados.

A linguagem Java foi desenvolvida a partir de um subconjunto do C++, gerando uma linguagem que originalmente foi chamada de Oak. Em 1995, a linguagem foi re-batizada como Java e introduzida na comunidade Internet.

O Java é uma linguagem parcialmente compilada e parcialmente interpretada. Um compilador Java transforma o programa fonte, escrito em Java, em arquivos-objeto chamados bytecodes. Esses bytecodes precisam ser executados então por interpretadores Java que são desenvolvidos para cada plataforma de hardware/software.

Os bytecodes podem ser basicamente de dois tipos. O primeiro tipo tem acesso completo à máquina, ou seja, é capaz de manipular a memória, o console e o sistema de arquivos. Programas desse tipo são chamadas de aplicações Java.

O segundo tipo de bytecode sofre uma série de restrições quanto ao acesso de memória, console e sistema de arquivos. Essas restrições são colocadas em nome da segurança, visto que seu destino é a elaboração de programas que serão distribuídos pela Internet, e por isso não provém de fonte conhecida ou confiável.

Esses bytecodes são chamados de Java applets. Os applets têm uma capacidade de atuação bem restrita, de modo que não possam causar danos ao sistema durante sua execução. Normalmente sua atuação se restringe a animações e interações mínimas com o usuário, sendo que seu uso é marcadamente em browsers do WWW (World Wide Web).

Para executar applets, os browsers necessitam interpretar os bytecodes Java. Browsers com essa capacidade são chamados de Java aware. Uma outra variante da linguagem Java é o JavaScript, que são programas colocados em forma de código fonte, incluídos nos textos das páginas HTML.

Programas escritos com o JavaScript não precisam ser compilados, nem geram bytecodes, sendo interpretados diretamente pelos browsers quando a página HTML é interpretada.

Eiffel

26A linguagem Eiffel é uma avançada linguagem de programação orientada a objetos, que enfatiza o projeto e a construção de software reutilizável de alta qualidade. A linguagem Eiffel não corresponde a um sub-conjunto ou extensão de nenhuma outra linguagem.

É uma linguagem que enfatiza o uso de técnicas de programação orientada a objeto, impedindo algumas práticas que podem ser potencialmente perigosas, e que são encontradas em outras linguagens. Entretanto, ela permite um interfaceamento com outras linguagens, tais como o C ou o C++.

A linguagem Eiffel foi criada por Bertrand Meyer e desenvolvida por sua companhia, a Interactive Software Engineering (ISE). Seu design é inspirado por algumas preocupações levantadas por engenheiros de software, diante da criação de sistemas mais complexos.

Como linguagem, vem evoluindo continuamente, desde sua concepção, em 1986. Compiladores para a linguagem Eiffel podem ser encontrados para diversos sistemas operacionais e plataformas de hardware.

UML

O paradigma de programação orientado a objetos foi proposto para ser empregado, fundamentalmente, no desenvolvimento de grandes sistemas. Todavia, para se desenvolver grandes sistemas, é necessário elaborar um modelo(s) antes, pois sem isso fica praticamente impossível se atingir o objetivo e, mesmo que esse seja alcançado, não é possível, durante o desenvolvimento, prever o tempo necessário para a conclusão do projeto, bem como, os recursos que serão necessários.

No sentido de auxiliar os analistas no desenvolvimento de projetos orientados a objetos, alguns pesquisadores criaram uma linguagem gráfica de modelagem conhecida como UML (Unified Modeling Language).

Tal linguagem é constituída de diversos diagramas que servem para o analista organizar o seu projeto e documentá-lo. Usando ferramentas CASE (Computer Aided Software Engeneering), pode-se inclusive gerar automaticamente o código de programação a partir do modelo elaborado em UML.3. TIPOS DE DADOS

27

3.1 Características

Uma variável é um objeto cujas ligações são definidas em tempo de execução e podem, portanto, mudar durante sua vida.

Uma constante é um objeto ligado a um valor durante toda sua vida, esta ligação é feita em tempo de compilação. Uma constante literal é um objeto cujo nome é somente uma representação escrita do seu valor (por exemplo "21" é a representação decimal do objeto constante literal com valor 21).

Uma constante definida pelo programador é um objeto cujo nome é definido pelo programador durante a definição do objeto.

EXEMPLO:

Const MAX = 30;Var N: Integer;BeginN:= 27;N:= N + MAX;...

N é uma variável simples, do tipo inteira. MAX é uma constante definida pelo programador, associada

permanentemente ao valor 30. "27" é um literal que nomeia um objeto contendo o valor 27. 30 pode ser representado tanto pela constante definida pelo usuário

MAX, quanto pelo literal "30".

Cada linguagem possui um conjunto de tipos primitivos. Linguagens mais modernas permitem que o programador defina novos tipos de dados. Podemos estudar os tipos de dados de uma linguagem em dois níveis:

de acordo com sua especificação (organização lógica); ou de acordo com sua implementação.

Além destes dois aspectos, devemos também observar a forma de representação sintática do tipo de dado.

28Elementos básicos para a especificação de tipos de dados

1 - Atributos, que diferenciam um tipo de outro qualquer.2 - Valores que os objetos de um tipo podem assumir.3 - Operações permitidas sobre o tipo de dados.

EXEMPLO:

Tipo Array

Atributos: número de dimensões, faixa de valores para cada dimensão e o tipo dos dados de seus componentes.

Valores: Conjunto de números que formam valores válidos para os componentes do Array.

Operações: cálculo do índice para acesso a determinado elemento do array; operações para mudar sua forma; para determinar atributos como limite inferior ou superior dos índices e para fazer operações em pares de arrays.

Elementos básicos para a implementação de tipos de dados

A forma de armazenamento dos objetos durante a execução do programa.

A maneira que as operações definidas para um tipo são representadas em termos de algoritmos que manipulem a forma de armazenamento escolhida para o objeto.

Representação sintática de um tipo

Define quais símbolos ou funções serão usadas para definir as operações sobre os tipos. Define a forma de uso dos tipos.

Tem pouca influência sobre a implementação, mas pode fornecer informações importantes ao tradutor para um uso eficiente do tipo ou para a checagem de erros.

3.2 Arquitetura

29Especificação de tipos elementares

Um objeto de tipo elementar contém apenas um único valor. Quase toda linguagem possui tipos como inteiros, reais, caracter, booleano ou enumeração.

Atributos de tipos elementares

O atributo mais básico de um objeto é seu tipo. Atributos adicionais podem ser necessários, por exemplo, um número real pode ter como atributo adicional sua PRECISÃO, com dois possíveis valores: SIMPLES e DUPLA.

Atributos são invariantes durante a vida de um objeto.

Valores de tipos elementares

Define o conjunto de possíveis valores que um objeto pode conter. Em qualquer momento, o objeto contém um único valor deste conjunto.

Normalmente é um conjunto ordenado de valores.

Usualmente este conjunto é determinado pela máquina onde a linguagem é implementada, de forma a ser melhor representado (de forma eficiente), mas isto não é obrigatório.

Operações em tipos elementares

Determina como os objetos deste tipo podem ser manipulados. São as operações primitivas, isto é, definidas pela própria definição da linguagem. Cada operação possui um domínio, um conjunto de possíveis argumentos e uma faixa de possíveis resultados que ela pode produzir.

3.3 Principais tipos

Aparecem em praticamente toda linguagem de programação.Detalhes dos tipos elementares variam de linguagem para linguagem e mesmo de implementação para implementação.

30Inteiros e reais são os mais comuns devido ao suporte direto pela maioria dos hardwares.

Inteiros

Geralmente o único atributo é o seu tipo. Possuem limites máximos e mínimos definidos pela linguagem.

Operações podem incluir: Aritméticas, Relacionais e Atribuição.

a. Implementação de inteiros

Freqüentemente implementados diretamente usando a definição do hardware com suas operações. Usualmente como uma word completa ou seqüência de bytes.

b. Formas de implementar inteiros

Sem descritor para o tempo de execução (PASCAL, FORTRAN).

Com descritor em uma word separada (LISP).

Com descritor na mesma word.

Ponto Flutuante

31Pode ser especificado pelo tipo simples REAL, como em FORTRAN ou PASCAL. Algumas linguagens, como PL/I e ADA permitem especificar também a precisão. Possui um valor máximo e um valor mínimo, porém seus valores não são distribuídos uniformemente.

Além das operações definidas para inteiros, algumas linguagens também oferecem as funções: SIN, COS e SQRT.

Implementação de ponto flutuante

Enumerações

O tipo pode assumir um de um pequeno número de valores. Linguagens antigas só permitiam a manipulação indireta destes valores, com o programador associando cada valor a um inteiro. Linguagens mais recentes permitem a criação de novos tipos, fazendo a checagem das operações sobre estes valores.

Implementação de enumerações

Usa a representação do inteiro.

As operações definidas são, em geral, as relacionais, atribuição e as operações de sucessor e predecessor.

O sucessor e predecessor podem ser implementados apenas com a soma ou subtração de 1 e depois se fazendo a checagem de faixa válida para o resultado.

32

Booleanos

Também chamado de tipo lógico. Pode assumir valores FALSE ou TRUE. Funciona como uma enumeração restrita a 2 valores. Em PASCAL ou ADA poderia ser definido como type BOOLEAN = (FALSE,TRUE).

Operações comuns:

and: boolean x boolean -> boolean;or: boolean x boolean -> boolean;not: boolean -> boolean.

Pode ser implementado como um simples bit. Como um bit em geral não pode ser endereçado na memória, usa-se o byte ou word para representar o boolean. Neste caso, pode-se usar um bit específico do byte ou usar uma convenção, onde um número igual a zero corresponde a FALSE e qualquer outra coisa, a TRUE.

Caracteres

Usualmente correspondem a uma enumeração baseada em um código de caracteres, como o ASCII.

Operações definidas sobre este tipo incluem as relacionais, atribuição e por vezes algumas operações para teste de tipos especiais de caracteres, como "letter", "digit" ou "special caracter".

Implementação de caracteres

Usualmente são diretamente suportados pelo hardware e pelo sistema operacional.

Ocasionalmente, a linguagem pode usar uma representação que não é suportada pela máquina virtual imediatamente abaixo, neste caso, a linguagem deve fornecer as conversões adequadas.

4. SUBPROGRAMAS E DADOS DEFINIDOS PELO USUÁRIO

33

Um tipo abstrato de dados pode ser definido por:

Atributos Elementos Operações

As primeiras linguagens não forneciam recursos para a criação de tipos abstratos de dados. (FORTRAN e COBOL)

4.1 Evolução do Conceito

Desenvolve-se a partir da década de 70. Nas linguagens como FORTRAN e COBOL vemos o embrião

destas idéias, com tipos básicos como real, inteiro e caractere. Para cada variável, deve-se fazer uma declaração separada. Também é feita uma checagem de tipos rudimentar. Próximo passo é feito em Pascal, com a definição de tipos (Type) Em seguida, compreende-se que um tipo de dados não é

simplesmente seus atributos, mas também as operações definidas sobre ele.

A implementação real do tipo é “encapsulada”, isto é, escondida do programador.

Um programador usa o tipo inteiro sem precisar saber como ele é representado internamente.

4.2 Abstração

Abstração é a base da programação. Abstração aparece com vários nomes: Refinamentos sucessivos,

Programação estruturada ou programação top-down.

Uma linguagem fornece suporte para abstrações de dois modos:1. Fornecendo um computador virtual que é mais simples para o uso

e mais potente que o computador real.2. A linguagem fornece facilidades para o programador criar suas

próprias abstrações, como subprogramas, tipos, pacotes etc.

34

4.3 Encapsulamento

Quando uma informação é encapsulada, o usuário desta informação:

1. Não precisa saber detalhes internos para usar a abstração,2. Não pode manipular diretamente a informação interna do tipo,

mesmo se ele queira.

Encapsulamento é importante para a facilidade de manutenção de programas.

Subprogramas são a forma básica de encapsulamento.

4.4 Subprogramas

Um subprograma possui duas partes: uma especificação e uma implementação. Na especificação temos:

1. O nome do subprograma2. O número de argumentos, sua ordem e tipos de dados.3. O número de resultados, sua ordem e tipos.4. A ação tomada pelo subprograma.

Subprogramas representam uma função matemática que mapeia cada conjunto específico de argumentos em um conjunto específico de resultados.

Se um subprograma retorna um valor único, ele é chamado função.

Ex: Function FN(X:real; Y:integer): realmapeia:FN: real x integer -> real

Se um subprograma retorna mais de um resultado ou se ele modifica seus argumentos em vez de usar uma sintaxe de retorno, ele é chamado Procedimento ou sub-rotina.

Em Pascal:

35

Procedure SUB(X:real; Y: Integer; var Z: real; U: boolean);

Em ADA:

Procedure SUB(X: in REAL; Y: in INTEGRE; Z: in out REAL; U: out BOOLEAN);

4.5 Observações

1. Um subprograma pode ter argumentos implícitos.2. Um subprograma pode ter resultados implícitos.3. Um subprograma pode não ter sido definido para alguns possíveis

argumentos.4. Um subprograma pode ser sensível ao histórico.

4.6 Implementação de subprogramas

A implementação é definida pelo seu corpo, que consiste de:

1. Declarações de dados locais.2. Comandos, definindo as ações executadas.

Normalmente as declarações são encapsuladas, deste modo, nem os dados locais, nem os comandos são acessíveis para o usuário do subprograma.

O corpo do subprograma pode incluir definições de outros subprogramas. Estes subprogramas são encapsulados e não podem ser invocados por programas externos.

A checagem de tipos é um fator importante nos subprogramas e pode ser estática ou dinâmica.

4.7 Definição e ativação de subprogramas

36

1. Um programador escreve uma definição de subprograma em um programa.

2. Durante a execução do programa, se o subprograma é chamado, uma ativação do subprograma é criada.

3. Quando a execução do subprograma termina, a ativação é destruída.

4. Se uma nova chamada é feita, outra ativação é criada.

4.8 Definição versus ativação

A partir de uma única definição de subprograma, várias ativações podem ser criadas.

A definição serve como um modelo para as ativações. Uma definição está presente quando o programa é traduzido. Ativações existem somente durante a execução. Durante a execução, as definições existem apenas como modelos para a criação das ativações. Isto é semelhante à definição de tipos e instanciação de variáveis.

Considere a seguinte definição em Pascal:

function FN(X real; Y:integer):real;const MAX=20;var M: array [1..20] of real;N: integer;begin...N:= MAX;X:= 2*X+M[5];...end;

Esta definição especifica para uma ativação do subprograma em tempo de execução, uma área de armazenamento para:

Parâmetros, X e Y. Resultados, um objeto do tipo real. Variáveis locais. Literais e constantes definidas pelo usuário, 20, 2 e 5.

37 Código executável.

Definição

Para construir uma ativação a partir de uma definição divide-se o modelo em duas partes:

1. Uma parte estática, chamada segmento de código, consistindo dos itens constantes e código executável. Esta parte é invariante durante a execução do programa e uma única cópia pode ser compartilhada entre todas as ativações.

2. Uma parte dinâmica, chamada registro de ativação, consistindo de parâmetros, resultados, dados locais e mais outros dados temporários. Esta parte tem a mesma estrutura entre as ativações, porém com valores diferentes.

Em vez de manter um registro de ativação completo, apenas o seu tamanho é guardado para a operação de ativação.

Quando um subprograma é chamado, várias ações são feitas para atualizar o registro de ativação.

Normalmente o tradutor coloca um código chamado prólogo para executar estas ações antes da execução do subprograma.

Ao término do subprograma, ações chamadas epílogo também são executadas.

4.9 Subprogramas genéricos

Um subprograma geralmente lista o número, a ordem e o tipo dos argumentos.

Um subprograma genérico é um com um único nome, porém várias definições, diferenciadas por números, ordens ou tipos de argumentos diferentes.Um nome de subprograma genérico é sobrecarregado (overloaded).

Ex:

38

Procedure ENTER(ESTUDANTE: in INTEGER; SECT: in out SECTION) isbegin...end;Procedure ENTER(S: in SECTION; TAB: in out CLASSLIST) isbegin...end;

Nesta situação, o tradutor deve resolver qual procedimento deve ser chamado.

4.10 Definição de Tipos

Para definir um novo tipo abstrato de dados uma linguagem precisa de mecanismos para uma definição de classes de objetos.

Em Pascal e Ada o mecanismo é a definição de tipos. Apenas a definição de tipos não determina um tipo abstrato de dados. Um tipo deve possuir um nome. A implementação só tem sentido durante a fase de tradução.

Ex:

type REALVECT= array [1..10] of real;var A:REALVECT;B,C: REALVECT;

4.11 Equivalência de tipos de dados

A checagem de tipos, estática ou dinâmica, envolve a comparação de dois argumentos.

Se os tipos são iguais, o argumento é aceito e a operação continua.

39 Se os tipos são diferentes, será considerado erro ou uma

conversão do tipo pode ser feita.

4.12 A questão é: Quando os tipos são idênticos?

Considere o seguinte exemplo:

type VECT1: array [1..10] of real;VECT2: array [1..10] of real;var X,Z: VECT1;Y: VECT2;Procedure SUB(A:VETC1);...end;begin...X:=Y;SUB(Y);end;

Equivalência de nomes ou de estrutura?

Equivalência de Nomes

Dois tipos de dados são considerados equivalentes se possuem o mesmo nome.

Neste caso VETC1 e VETC2 são diferentes. Usado em ADA e para parâmetros de subprogramas em Pascal.

Desvantagens

1. Cada tipo deve ter um nome, desta forma, não pode haver tipos anônimos. var W: array [1..10] of real;

2. Uma única definição de tipos deve ser usada para todas ou uma grande parte do programa.

Equivalência Estrutural

Dois tipos de dados são considerados equivalentes se possuem a

40mesma estrutura interna.

Neste caso, VECT1 e VECT2 são equivalentes.

Desvantagens

1. Surgem questões sutis sobre a equivalência de tipos: records devem ter os mesmos nomes de campos ou é suficiente que tenham o mesmo número de componentes, de mesmos tipos, na mesma ordem? Os índices dos arrays devem ser idênticos, ou basta que possuem o mesmo número de componentes?

2. Duas variáveis podem, por erro, serem consideradas idênticas.

Ex:

type METROS: integer;LITROS: integer;var DIST: METROS;VOL: LITROS;

3. Determinar se duas estruturas são idênticas pode consumir boa parte do tempo de tradução.

Em Ada e Pascal a equivalência de tipos é um importante aspecto na especificação da linguagem.

Em linguagens mais antigas, como FORTRAN ou COBOL e PL/I não existe definição de tipos, então uma forma de equivalência estrutural é usada.

4.13 Definição de tipos com parâmetros

É útil possuir uma forma de “parametrizar” os tipos, de forma a utilizar estes tipos com diferentes substituições de parâmetros.

Ex (ADA):

type SECTION (MAX_SIZE: integer)isrecordSALA: INTEGER;

41INSTRUTOR: INTEGER;CLASS_SIZE: INTEGER range 0.. MAX_SIZE;CLASS_ROLL:array(1.. MAX_SIZE) of STUDENT_ID;end record;X: SECTION(100);Y: SECTION(200);

4.14 Tipos Abstratos de Dados

Inclui:

1. Uma definição de um conjunto de objetos de dados, usando uma ou mais definições de tipos,

2. A definição de um conjunto de operações abstratas nestes objetos, usando uma ou mais definições de programas para definir cada operação abstrata.

3. Encapsulamento do todo, de tal modo que o usuário de um novo tipo não possa manipular os objetos deste tipo, exceto por meio das operações definidas.

4.15 Abstração de dados

Mesmo que a linguagem não ofereça recursos para abstração de dados, o programador pode usar uma convenção própria. Sem o suporte, no entanto, o encapsulamento é impossível.

Em ADA, o uso de pacotes permite o encasulamento.

package SECTION_TYPE istype STUDENT_ID is INTEGER;type SECTION (MAX_SIZE: INTEGER) is private;procedure ASSIGN_STUDENT(SECT: in out SECTION;STUD: in STUDENT_ID);procedure CREATE_SECTION(SECT: in out SECTION;INSTR: in INTEGER;ROOM: in INTEGER);

42privatetype SECTION(MAX_SIZE: INTEGER) isrecordROOM: INTEGER;INSTRUTOR: INTEGER;CLASS_SIZE: INTEGER range 0.. MAX_SIZE;CLASS_ROLL:array(1.. MAX_SIZE) of STUDENT_ID;end record;end;package body SECTION_TYPE isprocedure ASSIGN_STUDENT(..) is... end;procedure CREATE_SECTION(..) is... end;end;

4.16 Tipos Abstratos Genéricos

Permite especificar um tipo básico que será usado para a criação de outros objetos.

Ex: PILHA.

package INT_STACK_TYPE istype STACK(SIZE: POSITIVE) is private;procedure PUSH(I: in INTEGER; S: in out STACK);procedure POP(I: out INTEGER; S: in out STACK);privatetype STACK(SIZE:POSITIVE) isrecordSTK_STORAGE: array(1..SIZE) of INTEGER;TOP: INTEGER range 0..SIZE:=0;end record;end INT_STACK_TYPE;package body INT_STACK_TYPE isprocedure PUSH(I: in INTEGER; S: in out STACK) isbeginend;procedure POP(I: out INTEGER; S: in out STACK) isbegin

43end;end INT_STACK_TYPE;

4.17 Pilha de tipos diferentes

generictype ELEM is private;package STACK_TYPE istype STACK(SIZE: POSITIVE) is private;procedure PUSH(I: in ELEM; S: in out STACK);procedure POP(I: out ELEM; S: in out STACK);privatetype STACK(SIZE:POSITIVE) isrecordSTK_STORAGE: array(1..SIZE) of ELEM;TOP: INTEGER range 0..SIZE:=0;end record;end STACK_TYPE;package body STACK_TYPE isprocedure PUSH(I: in ELEM; S: in out STACK) isbeginend;procedure POP(I: out ELEM; S: in out STACK) isbeginend;end STACK_TYPE;package INT_STACK_TYPE isnew STACK_TYPE(ELEM => INTEGER);package SECT_STACK_TYPE isnew STACK_TYPE(ELEM => SECTION);STK1: INT_STACK_TYPE.STACK(100);NEW_STK: INT_STACK_TYPE.STACK(20);SS_STACK: SECT_STACK_TYPE.STACK(10);

5. TRATAMENTO DE EXCEÇÕES

44

Exceções são caracterizadas pela ocorrência de uma condição que interrompe o programa para que elas sejam atendidas prontamente. Tal condição em geral trata-se de um erro, mas também pode se tratar de um evento comum já esperado pelo programa tais como: overflow, índice de um vetor fora do intervalo, fim de arquivo etc.

Exceções são condições síncronas (ocorrem em um momento previsto), ao contrário de interrupções geradas pelo usuário ou dispositivos de E/S que são assíncronas, ou seja, podem acontecer em qualquer parte do programa.

Os procedimentos que são chamados para darem atendimento às exceções são chamados de exception handlers. O exeption handler suspende a execução do programa ou subprograma que o chamou.

Ao término de um exception handler podem ser tomadas duas ações:

Retomada da execução do programa ou subprograma suspenso; Suspensão definitiva (terminação) do programa ou subprograma

que chamou o exception handler.

Os exception handlers diferem dos procedimentos comuns apenas pelo fato de serem chamados implicitamente. Os procedimentos comuns poderiam causar o mesmo efeito, através do teste de condições explícitas. Por exemplo, o seguinte comando poderia ser incluído antes de cada acesso a um vetor:

if indice > indice_max or indice < indice_min then fora_intervalo_handler;

O uso de testes explícitos sobrecarregam o texto do programa fonte.

Existem dois tipos de exceções: built-in e definidas pelo usuário. As definidas pelo usuário não possuem condições associadas e, por isso, só podem ser chamadas explicitamente. Assim, seu comportamento é similar ao de um procedimento comum com a diferença na terminação, pois as exceções fazem um término abrupto.5.1 Habilitação e Desabilitação de Exceções

45

O principal fator para desabilitar uma exceção é o custo em tempo e espaço versus efeito. A desabilitação ocorre em tempo de compilação e previne a geração do código que implementa a exceção.

5.2 Fluxo de Controle Quando do Término de Um Handler

Existem três alternativas:

1. Termina o programa ou subprograma que invocou a exceção, retornando ao programa ou subprograma unidade que a ativou.

2. Volta a executar o programa ou subprograma que invocou a exceção, em ponto imediatamente seguinte ao comando que provocou a exceção.

3. Termina a execução do programa ou subprograma que chamou (como em 1) e ativa a mesma exceção no programa ou subprograma que a chamou.

Qual alternativa adotar?

1. Mesma alternativa default para todos os casos; 2. Cada exceção, uma alternativa; 3. A linguagem de programação oferece construções para especificar

a alternativa dentro do handler.

Quando não há handler definido para uma exceção que ocorreu: handler default é associado ou a exceção é propagada.

5.3 Propagação de Exceção

Se uma exceção é ativada quando não existe handler associado, o programa ou subprograma corrente é terminado e a mesma exceção é ativada pelo programa ou subprograma que a chamou. Caso exista agora um handler no nível hierárquico mais alto de programa ou subprograma, esse handler é executado, caso contrário o

46programa ou subprograma é terminado e a exceção é novamente propagada para níveis superiores da árvore de chamadas dinâmicas.

Caso não exista um handler até no nível da raiz, então o handler default é executado e o programa principal é terminado.

O registro de ativação (RA) de uma unidade contém uma lista de exceções built-in e definidas pelo usuário, com ponteiros para os respectivos handlers.

Condições de término:

Efeito de RETURN - desempilha RA; Efeito idêntico ao de um procedimento.