Baixe o app para aproveitar ainda mais
Prévia do material em texto
Instituto de Ciências Matemáticas e de Computação - USP Departamento de Ciências da Computação e Estatística Tópicos em Linguagens de Programação Objetivo: Introduzir e discutir os conceitos relacionados a Linguagens de Programação, principalmente no contexto de linguagens imperativas, e apresentar as principais características dos demais modelos (paradigmas) de Linguagens de Programação. A quem se destina: alunos das disciplinas de Linguagens de Programação Organizado por: Profa. Maria das Graças Volpe Nunes (mdgvnune@icmc.sc.usp.br) Publicado por: Katti Faceli (katti@icmc.sc.usp.br) 1 Índice 1. Definição .............................................................................................................................................. 4 2. Porque estudar LP ?.............................................................................................................................. 4 3. Histórico............................................................................................................................................... 4 3.1. FORTRAN (FORmula TRANslation) ............................................................................................. 5 3.2. COBOL (COmmon Business Oriented Language).......................................................................... 6 3.3. ALGOL 60 (ALGorithmic Oriented Language) .............................................................................. 8 3.4. LISP (LISt Processing) .................................................................................................................... 9 3.5. APL (A Programming Language).................................................................................................. 10 3.6. BASIC (Beginners All-purpose Symbolic Instruction Code)....................................................... 10 3.7. PL/I (Programming Language I).................................................................................................... 11 3.8. SIMULA 67 ................................................................................................................................... 12 3.9. ALGOL 68..................................................................................................................................... 13 3.10. PASCAL.................................................................................................................................... 13 3.11. PROLOG (PROgramming in LOGic) ....................................................................................... 14 3.12. SMALL TALK.......................................................................................................................... 15 3.13. C ................................................................................................................................................ 16 3.14. MÓDULA 2............................................................................................................................... 16 3.15. ADA .......................................................................................................................................... 17 4. Especificação de uma LP ................................................................................................................... 18 4.1. Sintaxe de uma LP ......................................................................................................................... 18 4.2. Semântica de LP ............................................................................................................................ 22 5. Tradução de uma LP .......................................................................................................................... 22 5.1. Interpretação .................................................................................................................................. 22 5.2. Compilação .................................................................................................................................... 22 6. Características de Design de LP......................................................................................................... 23 7. Escolha de uma LP............................................................................................................................. 24 8. Paradigmas de LP............................................................................................................................... 25 8.1. O Paradigma Imperativo de LP...................................................................................................... 25 8.1.1. Binding.................................................................................................................................. 25 8.1.1.1. "Information Binding" ...................................................................................................... 25 8.1.1.2. Escopo de Binding e Unidades de Execução .................................................................... 28 8.1.1.3. Escopo de Binding de Nome............................................................................................. 30 8.1.1.4. Escopo de Binding de Locação......................................................................................... 32 8.1.1.5. Exercícios.......................................................................................................................... 34 8.1.2. Estruturas de controle............................................................................................................ 34 8.1.2.1. Condicional Simples ......................................................................................................... 34 8.1.2.2. Condicional Com 2 Alternativas....................................................................................... 35 8.1.2.3. Condicional Multi-alternativas ......................................................................................... 36 8.1.2.4. Condicional Não-Determinística ...................................................................................... 37 8.1.2.5. Iteração Infinita................................................................................................................. 38 8.1.2.6. Iteração Pré-Teste ............................................................................................................. 38 8.1.2.7. Iteração Pós-Teste............................................................................................................. 38 8.1.2.8. Iteração In-Teste ............................................................................................................... 39 8.1.2.9. Iteração com Número Fixo de Passos ............................................................................... 40 8.1.2.10. Iteração Não-Determinística......................................................................................... 41 8.1.2.11. Desvio Incondicional.................................................................................................... 41 8.1.3. Tipos de Dados...................................................................................................................... 42 2 8.1.3.1. Tipo Numérico - Inteiro .................................................................................................... 44 8.1.3.2. Tipo Numérico - Real ....................................................................................................... 45 8.1.3.3. Tipo Boolean..................................................................................................................... 45 8.1.3.4. Tipo Ponteiro .................................................................................................................... 46 8.1.3.5. Tipos Enumerados (domínio criado pelo usuário)............................................................ 48 8.1.3.6. Tiposde Dados Compostos ..............................................................................................49 8.1.3.6.1. Array ........................................................................................................................ 49 8.1.3.6.2. Records ou Structures .............................................................................................. 52 8.1.3.6.3. Records Variantes .................................................................................................... 52 8.1.3.6.4. Strings ...................................................................................................................... 54 Arquivos (Files)......................................................................................................................... 55 8.1.3.6.6. Conjuntos (Sets) ....................................................................................................... 55 8.1.3.6.7. Exercícios................................................................................................................. 56 8.1.4. Procedimentos e Funções ...................................................................................................... 59 8.1.4.1. O ambiente local ............................................................................................................... 61 8.1.4.2. O ambiente global ............................................................................................................. 62 8.1.4.2.1. Escopo Estático ........................................................................................................ 62 8.1.4.2.2. Escopo Dinâmico ..................................................................................................... 63 8.1.4.3. O ambiente de parâmetros ................................................................................................ 64 8.1.4.3.1. Parâmetros de Entrada (IN)...................................................................................... 65 8.1.4.3.2. Parâmetros de Entrada e Saída (IN/OUT)................................................................ 65 8.1.4.3.3. Parâmetros de Saída (OUT) ..................................................................................... 66 8.1.4.3.4. Procedimentos como parâmetros ............................................................................. 66 8.1.4.3.5. Passagem de Parâmetro "Por Nome" ....................................................................... 67 8.1.4.4. Funções ou Value-Returning Procedures (VRPs)............................................................. 69 8.1.4.5. Overloading de Procedimentos ......................................................................................... 69 8.1.4.6. Co-rotinas.......................................................................................................................... 70 8.1.4.7. Exercícios.......................................................................................................................... 71 8.1.5. Tipo Abstrato de Dados (TAD)............................................................................................. 71 8.1.5.1. Definição........................................................................................................................... 71 8.1.5.2. Implementação.................................................................................................................. 73 8.1.5.3. Parametrização de Tipos de Dados ................................................................................... 75 8.1.5.4. TAD em ADA................................................................................................................... 77 8.1.5.5. TAD em MÓDULA 2....................................................................................................... 79 8.1.5.6. TAD em C......................................................................................................................... 79 8.1.5.7. Exercícios.......................................................................................................................... 79 8.1.6. Tratamento de Exceções........................................................................................................ 80 8.1.6.1. Tipos de exceções: ............................................................................................................ 81 8.1.6.2. Habilitação & Desabilitação de Exceções ........................................................................ 81 8.1.6.3. Fluxo de controle quando do término de um Handler ...................................................... 83 8.1.6.4. Propagação de Exceção .................................................................................................... 84 8.1.6.5. Tratamento de Exceções em ADA.................................................................................... 84 8.1.6.6. Exercícios.......................................................................................................................... 84 8.1.7. Concorrência ......................................................................................................................... 85 8.1.7.1. Definição........................................................................................................................... 85 8.1.7.2. Chamada ........................................................................................................................... 86 8.1.7.3. Compartilhamento de Dados e Comunicação Inter-Processsos........................................ 86 8.1.7.3.1. Mail Model............................................................................................................... 87 8.1.7.3.2. Phone Model ............................................................................................................ 88 3 8.1.7.4. Sincronização.................................................................................................................... 89 8.1.7.4.1. Token ....................................................................................................................... 89 8.1.7.4.2. Gate .......................................................................................................................... 89 8.1.7.5. Concorrência em ADA ..................................................................................................... 90 8.1.7.5.1. Compartilhamento de Dados.................................................................................... 90 8.1.7.5.2. Esperas Seletivas...................................................................................................... 92 8.1.7.5.3. Guardas .................................................................................................................... 93 8.1.7.6. Exercícios.......................................................................................................................... 94 8.2. O Paradigma Funcional de LP ....................................................................................................... 96 8.2.1. O Paradigma Funcional......................................................................................................... 96 8.2.2. FP: uma linguagem funcional pura (Backus-78)................................................................... 97 8.2.3. LISP (LISt Processing) ....................................................................................................... 103 8.2.3.1. Componentes básicos...................................................................................................... 103 8.2.3.2. Aglumas Funções Pré-definidas ..................................................................................... 105 8.2.3.3. Definição de Funções...................................................................................................... 106 8.2.3.4. Exercícios........................................................................................................................110 8.2.4. Comparação de LISP com FP ............................................................................................. 110 8.3. O Paradigma Lógico de LP.......................................................................................................... 112 8.4. O Paradigma Orientado a Objeto (OO) de LP ............................................................................. 113 8.4.1. Componentes básicos: ......................................................................................................... 113 8.4.2. Propriedades do Modelo Orientado a Objeto ...................................................................... 113 8.4.3. Exemplo em Hool: .............................................................................................................. 114 8.4.4. Exemplo em C++: ............................................................................................................... 117 8.4.5. Orientação a Objeto (OO) x Paradigma Imperativo (PI) .................................................... 122 8.4.6. Exercícios............................................................................................................................ 122 9. Bibliografia ...................................................................................................................................... 123 4 1. Definição Uma LP (Linguagem de Programação) é uma linguagem destinada a ser usada por uma pessoa para expressar um processo através do qual um computador pode resolver um problema. Os quatro modelos de LP correspondem aos pontos de vista dos quatro componentes citados. A eficiência na construção e execução de programas depende da combinação dos quatro pontos de vista. 2. Porque estudar LP ? 1. Maior habilidade em resolver problemas: uma maior compreensão de uma LP pode aumentar nossa habilidade em pensar em como atacar os problemas. Tanto melhor se dominarmos os vários modelos de LP. 2. Melhor uso de uma LP: compreensão das funções e implementação das estruturas de uma LP nos levam a usar a LP de modo a extrair o máximo de sua funcionalidade e eficiência. 3. Melhor escolha de uma LP: adequação ao problema. 4. Maior facilidade em aprender novas LPs: conceitos chaves comuns às LPs. 5. Melhor designer de LPs: linguagens de interfaces de sistemas, extensão de LP via operadores e tipos de dados. 3. Histórico Veja um pouco da história de algumas linguagens de programação que introduziram conceitos importantes para as futuras LPs e que ainda estão em uso. Essas linguagens estão classificadas em três períodos, de acordo com a época em que surgiram. 1955 - 1965 • FORTRAN (FORmula TRANslation) • COBOL (COmmon Business Oriented Language) • ALGOL 60 (ALGorithmic Oriented Language) • LISP (LISt Processing) • APL (A Programming Language) • BASIC (Beginners All-purpose Symbolic Instruction Code) 1965 - 1971 (LP's baseadas em ALGOL) • PL/I (Programming Language I) • SIMULA 67 • ALGOL 68 • PASCAL Linguagens dos anos 80 (criadas na década de 70) • PROLOG (PROgramming in LOGic) • SMALL TALK 5 • C • MODULA 2 • ADA 3.1. FORTRAN (FORmula TRANslation) A linguagem Fortran, desenvolvida em 1956 por John Backus, foi proposta visando a resolução de problemas científicos, para isto utilizando a notação algébrica. Foi desenvolvida, inicialmente para uma máquina específica, o IBM 704. É, ainda hoje, uma linguagem muito utilizada no meio técnico- científico, tendo sido aprimorada ao longo do tempo, constituindo as diversas versões disponíveis. Um dos motivos pelos quais o FORTRAN é ainda muito utilizado é a disponibilidade de uma vasta biblioteca de software contendo rotinas freqüentemente utilizadas, tais como rotinas para cálculo de funções trigonométricas, equações algébricas polinomiais, etc, o que permite uma redução dos custos e tempo de desenvolvimento dos programas. Contribuições para futuras linguagens: • variáveis • comando de atribuição • conceito de tipos • modularidade (com o uso de subprogramas) • E/S formatadas Exemplo de programa FORTRAN: * Programa que converte um valor decimal, maior ou igual a zero, * lido através do console do sistema, em seu correspondente valor * binário, imprmindo este valor na tela do console. * Autores: José Carlos Maldonado * Ronaldo Luiz Dias Cereda * Data: Abril/87 * - Programa Principal - * Declaracao de variaveis, constantes simbolicas e iniciializacoes INTEGER CONT, J, LIMITE,NUMERO, VETRES(8) PARAMETER (LIMITE = 255) DATA VETRES/8*0/ * Leitura e impressão do valor decimal WRITE(*,*) 'DIGITE O VALOR DECIMAL' READ(*,*) NUMERO WRITE(*,10) NUMERO 10 FORMAT(1X,//,1X,'VALOR DECIMAL FORNECIDO: ',I3) * Verificacao do limite IF (NUMERO.GT.LIMITE) $THEN WRITE(*,*) 'NUMERO MUITO GRANDE ELSE * Chamada da rotina para conversao e impressao dos resultados CONT = 0 CALL CONV(NUMERO, VETRES, CONT) WRITE(*,20) (VETRES(J), J=CONT, 1, -1) 20 FORMAT(1X, 'VALOR BINARIO CORRESPONDENTE: ', 8I2) 6 ENDIF STOP END * Sub-rotina que faz a conversao de um numero decimal, maior * ou igual a zero, em um numero binario * Parametro de entrada * NUM: valor a ser convertido * Parametros de saida * VETRES: vetor que contem o resultado * PONT: numero de bits do resultado SUBROUTINE CONV(NUM, VETRES, PONT) * Declaracao dos parametros INTEGER NUM, PONT, VETRES(8) * Declaracao de variavel local INTEGER AUX * conversao 100 IF (NUM.GE.2) THEN * inicio do enquanto PONT = PONT + 1 AUX = NUM/2 VETRES(PONT) = NUM - AUX*2 NUM = AUX GOTO 100 * fim do enquanto ENDIF PONT = PONT + 1 VETRES(PONT) = NUM RETURN END 3.2. COBOL (COmmon Business Oriented Language) A linguagem COBOL, desenvolvida em 1959 pelo Departamento de Defesa dos EUA e fabricantes de computadores , é padrão para as aplicações comerciais e muito utilizada ainda hoje. Seu desenvolvimento se deu de forma independente da máquina. O código é "English-like" e é excelente para a manipulação de arquivos. Contribuições de COBOL para futuras linguagens: • código mais legível • estrutura de dados heterogênea (record) Exemplo de programa COBOL: Organização de um programa COBOL: Um programa COBOL possui quatro divisões que devem ser colocadas na ordem adequada. A sua estrutura completa é: IDENTIFICATION DIVISION PROGRAM-ID. nome-do-programa 7 [AUTHOR. [comentários] ...] [INSTALLATION. [comentários] ...] [INSTALLATION. [comentários] ...] [DATE-WRITTEN. [comentários] ...] [DATE-COMPILED. [comentários] ...] [SECURITY. [comentários] ...] [REMARKS. [comentários] ...] ENVIRONMENT DIVISION [CONFIGURATION SECTION. SOURCE-COMPUTER. OBJECT-COMPUTER. [SPECIAL-NAMES. ]]. [INPUT-OUTPUT SECTION. FILE-CONTROL. [I-O-CONTROL. ENTRADA.]]. DATA DIVISION. [FILE SECTION. { Descrição dos arquivos { Descrição dos registros } ... } ... ]. [WORKING-STORAGE SECTION. [Descrição dos itens de dados] ... [Descrição de registros] ... ]. [LINKAGE SECTION. [Descrição dos itens de dados] ... [Descrição de registros] ... ]. [COMMUNICATION SECTION. {Descrição das Entradas de Comunicação. [Descrição das Entradas de Registros] ... } ... ]. [REPORT SECTION. {Descrição de relatórios} {Descrição dos grupos de relatórios} ... } ... ]. PROCEDURE DIVISION. [USING identificador-1 [identificador-2] ...] [DECLARATIVES. {nome-da-seção SECTION. USE SENTENCE. {nome-de-parágrafo. {sentença} ... } ... } ... END DECLARATIVES.] {nome-da-seção SECTION [prioridade] - ] {nome-de-parágrafo. {sentença} ... } ... } ... Exemplo: 001 010 IDENTIFICATION DIVISION. 001 020 PROGRAM-ID. TABSORT.001 030 AUTHOR. ALEX BASTOS. 001 040 INSTALLATION. RDC-DIV USUARIOS. 001 050 DATE-WRITEN. 18/12/72. 001 060 DATE-COMPILED. 20/12/72. 001 070 REMARKS. Este programa grava arquivo sequencial em ordem crescente e classifica-o apos em ordem decrescente. 001 100 ENVIRONMENT DIVISION. 001 110 CONFIGURATION SECTION. 001 120 SOURCE-COMPUTER. IBM-370-165. 001 130 OBJECT-COMPUTER. IBM-370-165. 001 140 INPUT-OUTPUT SECTION 8 001 150 FILE-CONTROL. 001 160 SELECT ARQUIVO ASSIGN TO DA-S-DISCO. 001 170 SELECT TRABALHO ASSIGN TO DA-S-SORTWK01. 001 180 DATA DIVISION. 001 190 FILE SECTION. 001 200 FD ARQUIVO LABEL RECORDS ARE STANDARD 002 010 DATA RECORD IS ENTRADA. 002 020 01 ENTRADA. 002 030 02 X PIC 99. 002 040 02 Y PIC X(10). 002 050 02 FILLER PIC X(20). 002 060 SD TRABALHO LABEL RECORDS ARE STANDARD 002 070 DATA RECORD IS TRAB. 002 080 01 TRAB. 002 090 02 Z PIC 99. 002 100 02 K PIC X(10). 002 110 02 FILLER PIC X(20). 002 120 WORKING-STORAGE SECTION. 002 130 77 I PIC 99 VALUE ZEROS. 002 140 PROCEDURE DIVISION. 002 150 OPEN OUTPUT ARQUIVO. 002 160 MOVE 'TESTE-SORT' TO Y. 002 170 GRAVACAO. 002 180 MOVE I TO X. 002 190 ADD 1 TO I. 002 200 WRITE ENTRADA. 003 010 IF I 100 GO TO GRAVACAO. 003 020 SORT TRABALHO DESCENDING Z USING 003 030 ARQUIVO GIVING ARQUIVO. 003 040 OPEN INPUT ARQUIVO. 003 050 GRAVA. 003 060 READ ARQUIVO AT END GO TO FIN. 003 070 DISPLAY X' Y. 003 080 GO TO GRAVA. 003 090 FIN. 003 100 CLOSE ARQ. 003 110 STOP RUN. 3.3. ALGOL 60 (ALGorithmic Oriented Language) Linguagem algébrica de origem européia, desenvolvida pelo comitê Internacional popular, destinada à resolução de problemas científicos. Influenciou o projeto de quase todas as linguagens projetadas a partir de 1960. Descrita em BNF (Backus-Naur Form), foi projetada independente da implementação, o que permite uma maior criatividade, porém de implementação mais difícil. É pouco usada em aplicações comerciais devido à ausência de facilidades de E/S na descrição e pelo pouco interesse de vendedores. Além disso, tornou-se padrão para a publicação de algoritmos. Contribuições de ALGOL 60 para futuras linguagens: • estrutura de blocos: habilidade de se criar blocos de comandos para o escopo de variáveis e extensão de influência de comandos de controle • comandos de controle estruturados: if-then-else e uso de uma condição geral para controle de iteração • recursividade: habilidade de um procedimento chamar a si próprio Exemplo de programa ALGOL 60: Procedimento que calcula raiz quadrada. procedure EQ2OR (A, B, C, z1r, z1i, z2r, z2i, INDETERMINATE); value A, B, C; real A, B, C, z1r, z1i, z2r, z2i; label INDETERMINATE; begin real discriminant; if A ≠ 0 then go to normal; ir B = 0 then go to INDETERMINATE; z1r := z2r := -C/B; go to set zero; normal: discriminant := B 2 - 4 x A x C; if discriminant 0 then go to real solution; complex: z1r := z2r := -B/2/A; z1i := sqrt(-discriminant)/2/A; z2i := - z1i; go to finis; real solution: z1r := (-B+(if B 0 then -1 else 1)xsqrt(discriminant))/2/A; z2r := C/A/z1r; set zero: z1i := z2i := 0; finis: end EQ2OR 3.4. LISP (LISt Processing) Linguagem funcional criada em 1960, por John McCartly do grupo de IA do MIT, para dar suporte à pesquisa em Inteligência Artificial. Foi inicialmente desenvolvida para o IBM 704. Existem muitos dialetos pois LISP nunca foi padronizada. Porém, em 1981 surgiu o Common LISP que é um padrão informal. Os programas em LISP são listas. Contribuição de LISP para futuras linguagens: • é pioneira na idéia de computação simbólica ou não-numérica Exemplo de programa LISP: Organização de um programa LISP: Um programa utiliza a lista encadeada como estrutura de dados básica. Sendo uma linguagem funcional, ao invés de especificar operações como um conjunto sequencial de comandos, invoca funções e composição de funções para especificar múltiplas ações. Utiliza a mesma estrutura para dados e programas. Predicado que testa se dois conjuntos tem algum elemento em comum. Retorna nil se os dois conjuntos são disjuntos. (DEFUN INTERSECTP (A B) (COND ((NULL A) NIL) ((MEMBER (CAR A) B)) (T (INTERSECTP (CDR A) B)))) 9 3.5. APL (A Programming Language) Foi desenvolvida por volta de 1960 por Kenneth Iverson - Harvard, IBM. Utiliza notação matemática, com operadores poderosos, possuindo muitos operadores e muitos caracteres o que gera grande dificuldade de implementação. Tem uma notação compacta e é utilizada em aplicações matemáticas. Segue o modelo funcional e tem como principal estrutura de dados o ARRAY, com diversos operadores sobre esta estrutura. Exemplo de programa APL: Função que eleva ao cubo o seu argumento, caso ele seja negativo, e ao quadrado, se ele for positivo. 3.6. BASIC (Beginners All-purpose Symbolic Instruction Code) A linguagem BASIC, desenvolvida em meados dos anos 60 por John Kemeny e Thomas Kurtz no Dartmouth College, teve como objetivo ensinar alunos de graduação a usarem um ambiente interativo de programação, através de uma LP de fácil aprendizado. Com o surgimento dos microcomputadores de baixo custo, no início dos anos 70, o BASIC tornou-se muito popular, embora não tenha contribuído muito tecnologicamente. Contribuições de Basic para futuras linguagens: • uma das primeiras LPs a prover um ambiente de programação interativo como parte da linguagem • execução interpretativa de programas Exemplo de programa Basic Organização de um programa BASIC: Um programa é constituído de uma seqüência de declarações (instruções) que devem aparecer na ordem em que deverão ser executadas, a menos que seja indicado um desvio. A seguir são apresentadas regras que se aplicam a todas as declarações em BASIC: 1. Cada declaração deve aparecer em uma linha separada. 2. Uma declaração não pode possuir comprimento superior a um linha (em geral 80 caracteres). 3. Cada declaração deve ser iniciada com uma quantidade positiva inteira, conhecida como número da declaração (número da linha). Duas declarações não podem possuir o mesmo número de identificação. 4. Declarações sucessivas devem possuir números de declarações crescentes. 5. Os números das declarações devem ser seguidos de uma palavra-chave do BASIC, que indicará o tipo de instrução a ser executado. 10 11 6. Espaços em branco podem ser inseridos, sempre que se desejar, para melhorar a leitura da declaração. Obs: Cada linha em branco devem possuir um único número de declaração seguido de um espaço em branco. Exemplo: 10 REM ANAGRAMA PARA QUATRO LETRAS 20 PRINT "FORNECA QUATRO LETRAS QUAISQUER:" 30 PRINT 40 INPUT L$(1), L$(2), L$(3), L$(4) 50 PRINT 60 FOR I1 = 1 TO 4 70 FOR I2 = 1 TO 4 80 IF I2 = I1 THEN 150 90 FOR I3 = 1 TO 4 100 IF I3 = I1 THEN 140 110 IF I3 = I2 THEN 140 120 LET I4 = 10 - (I1 + I2 + I3) 130 PRINT L$(I1); L$(I2); L$(I3); L$(I4) 140 NEXT I3 150 NEXT I2 160 NEXT I1 170 END RUN 3.7. PL/I (Programming Language I) Desenvolvida em meados dos anos 60 pela IBM com o objetivo de incorporar características das LPs existentes numa única LP de propósito geral. Assim PL/I inclui: • estrutura de bloco, de controle e recursividade do ALGOL 60; • subprogramas e E/S formatadas do FORTRAN; • manipulação de arquivos e registros do COBOL; • alocação dinâmica de memória e estruturas encadeadas do LISP; • operações de arrays do APL. É uma linguagem difícil de aprender e implementar devido a sua grande complexidade. Além disso, faz uso de defaults. Contribuições de PL/I para futuras linguagens: • tratamento de interrupção - execução de procedimentos específicos quando uma condição excepcionalocorre • multitarefa - especificação de tarefas que podem ser executadas concorrentemente Exemplo de programa PL/I: Programa que calcula média aritmética. MEDIA: PROCEDURE OPTIONS (MAIN); DECLARE (N, M, X, SOMA, MEDIA) FLOAT; 12 SOMA = 0; MEDIA = 0; GET LIST (N); M = N; DO WHILE (N 0); GET LIST (X); SOMA = SOMA + X; N = N - 1; END; MEDIA = SOMA / M; PUT LIST ('MEDIA ARITMETICA = ', MEDIA); END MEDIA; 3.8. SIMULA 67 Linguagem baseada em Algol 60, criada no início dos anos 60 por Ole Johan Dahl e Kristan Nygaard, na Noruega. É destinada à descrição de sistemas e programação de simulações. Contribuição de SIMULA 67 para futuras linguagens: • conceito de classe: Uma classe é um encapsulamento de dados e procedimentos que podem ser instanciados em um conjunto de objetos. É o conceito predecessor ao tipo abstrato de dados (ADA e Módula 2) e das classes das linguagens orientadas a objeto (Smalltalk e C++) Exemplo de programa Simula 67: Classe para manipulação de números complexos. class complex(x, y); real x, y; begin ref(complex) procedure add (c); ref(complex) c; if c =/= none then add :- new complex(x + c.x, y + c.y); ref(complex) procedure sub (c); ref(complex) c; if c =/= none then sub :- new complex(x - c.x, y - c.y); ref(complex) procedure mult (c); ref(complex) c; if c =/= none then mult :- new complex(x * c.x - y * c.y, x * c.y + y * c.x); ref(complex) procedure div (c); ref(complex) c; begin real m; if c =/= none then begin m := c.modulus; if m ¬= 0.0 then div :- mult(c.conjugate).stretch(1/m); end; end ***div***; ref(complex) procedure conjugate; conjugate :- new complex(x, -y); real procedure modulus; modulus := sqrt(x*x + y*y); ref(complex) procedure stretch(k); real k; stretch :- new complex(k*x, k*y); procedure write; begin outchar('('); outfix(x, 3, 12); 13 outchar(','); outfix(y, 3, 12); outchar(')'); end ***write*** end ***complex*** 3.9. ALGOL 68 É muito diferente do Algol 60. Linguagem de propósito geral que foi projetada para a comunicação de algoritmos, para sua execução eficiente em vários computadores e para ajudar seu ensino a estudantes. Porém é de difícil descrição, o que resultou em uma baixa popularidade. Contribuição de ALGOL 68 para futuras linguagens: • ortogonalidade: uma LP que é ortogonal tem um número de construtores básicos e um conjunto de regras para combiná-los relativamente pequeno (oposto a PL/I) Exemplo de programa ALGOL 68: Programa que lê três palavras de dez caracteres cada e as mostra em ordem alfabética. begin [10]char s1, s2, s3; read((s1, s2, s3)); if s1 < s2 then if s2 < s3 then print((s1, s2, s3)) elif s1 < s3 then print((s1, s3, s2)) else print((s3, s1, s2)) fi elif s1 < s3 then print((s2, s1, s3)) elif s2 < s3 then print((s2, s3, s1)) else print((s3, s2, s1)) fi end 3.10. PASCAL Desenvolvida por Niklaus Wirth em 1969, é uma linguagem de fácil aprendizado e implementação, suporta programação estruturada e é adequada para o ensino de programação. Em meados dos anos 80 também passou a ser usada para a programação em micro-computadores. Influenciou praticamente todas as linguagens mais recentes. Contribuições de Pascal para futuras linguagens: • estruturas de controle flexíveis 14 • tipos definidos pelo usuário • arquivos • records • conjuntos Exemplo de programa Pascal: PROGRAM ordena(input, output); (* Este programa ordena um vetor de n números reais, em ordem crescente *) VAR n, i, inic: 1..100; x: ARRAY [1..100] OF real; temp: real; PROCEDURE troca; (* Troca, do maior ao menor, os elementos de um vetor *) BEGIN FOR inic := 1 TO n-1 DO FOR i := inic + 1 TO n DO IF x[i] < x[inic] THEN BEGIN temp := x[inic]; x[inic] := x[i]; x[i] := temp; END; END; (* troca *) BEGIN (* bloco principal *) write('Quantos sao os numeros? '); readln(n); FOR i := 1 TO n DO BEGIN write('x[', i:3, ']= ? '); readln(x[i]); END; troca; writeln; writeln('Dados ordenados:'); writeln; FOR i := 1 TO n DO writeln('x[', i:3, ']= ', x[i]:4:1); END. 3.11. PROLOG (PROgramming in LOGic) Linguagem desenvolvida em 1972 em Marseille na França. É destinada a aplicações de Inteligência Artificial e se baseia em lógica formal. É a LP do projeto japonês de quinta geração. Exemplo de programa PROLOG: Predicado que retorna verdadeiro se e somente se o argumento é uma data válida. date(Year, Month, Day) :- Month 0, Month < 13, 15 mdays(Year, Month, Ndays), Day 0, Day =< Ndays. mdays(_, 1, 31). mdays(Y, 2, 28) :- not leap(Y). mdays(Y, 2, 29) :- leap(Y). mdays(_, 3, 31). mdays(_, 4, 30). mdays(_, 5, 31). mdays(_, 6, 30). mdays(_, 7, 31). mdays(_, 8, 31). mdays(_, 9, 30). mdays(_, 10, 31). mdays(_, 11, 30). mdays(_, 12, 31). leap(Y) :- divides(4, Y), not divides(100, Y). leap(Y) :- divides(400, Y). divides(Div, K) :- 0 is K mod Div. 3.12. SMALL TALK Criada por Alan Kay da Xerox - Palo Alto no início dos anos 70. Apresenta um ambiente de programação com menus pop-up, windows e mouse (modelo para Apple Macintosh). Segue o modelo orientado a objetos, possuindo o conceito de classe do SIMULA 67 mais encapsulamento, herança e instanciação. Contribuições de SmallTalk para futuras linguagens: • primeira LP a utilizar o paradigma de programação interativa • introduz o conceito de LP extensível Exemplo de programa SmallTalk: Classe que representa o objeto Linha. Class Line |startPoint endPoint| [ from: sPoint to: ePoint startPoint <- sPoint. endPoint <- ePoint | + offset ^ Line new; from: (startPoint + offset) to: (endPoint + offset) | 16 drawWith: APen aPen up. aPen goTo: startPoint. aPen down. aPen goTo: endPoint ] 3.13. C Desenvolvida pelo Bell Lab no início dos anos 70, visando a implementação do UNIX. Possui um padrão feito por Kernighan e Ritchie em 1978. Tem facilidades para a programação em "baixo nível" e gera código eficiente. Possui um grande conjunto de operadores, o que permite um código compacto, porém de baixa legibilidade. É excelente para construir programas portáveis. Exemplo de programa C: main()n /* count lines in input */ { int c, nl; nl = 0; while ((c = getchar()) != EOF) if (c == '\n') ++nl; printf("%d\n", nl); } 3.14. MÓDULA 2 Criada por Niklaus Wirth no final dos anos 70, é uma linguagem de propósito geral, baseada em melhorias no Pascal. É boa para projetos de desenvolvimento de software de grande porte. Além disso foi usada para ensinar programação. Módula 2 elaborou Pascal em: • módulos podem ser usados para implementar TAD (Tipos Abstratos de Dados) • todas as estruturas de controle têm uma palavra-chave de terminação • co-rotinas - execução intercalada • tipos de procedimentos Exemplo de programa Módula 2: Programa que lista todas as possíveis permutações de n objetos distintos a[1] ... a[n]. MODULE Permute; FROM InOut IMPORT Read, Write, WriteLn; VAR n: CARDINAL; ch: CHAR; a: ARRAY[1..20] OF CHAR; PROCEDURE output; VAR i: CARDINAL; BEGIN FOR i := 1 TO n DO Write(a[i])END; WriteLn 17 END output; PROCEDURE permute(k: CARDINAL); VAR i: CARDINAL; t: CHAR; BEGIN IF k = 1 THEN output ELSE permute(k-1); FOR i := 1 TO k-1 DO t := a[i]; a[i] := a[k]; a[k] := t; permute(k-1); t := a[i]; a[i] := a[k]; a[k] := t; END END END permute BEGIN Write(""); n := 0; Read(ch); WHILE ch "" DO n := n + 1; a[n] := ch; Write(ch); Read(ch); END; WriteLn; permute(n) END permute. 3.15. ADA Foi desenvolvida no início dos anos 70 pelo Departamento de Defesa dos Estados Unidos. É dedicada aos "embedded systems" (operam como parte de um sistema maior) e se baseia no Pascal. Teve um padrão em 1983. Além disso, usa conceitos de classe do Simula 67, adota o tratamento de exceções de PL/I e provê facilidades para processamento concorrente.Foi projetada para apoiar aplicações numéricas, programação de sistemas e aplicações que envolvem considerações de tempo real e concorrência. Seu nome se deve a ADA Augusta, 1a programadora, colaboradora de Charles Babbage - século 19. Exemplo de programa ADA Prática de tiro ao alvo. with I_O_PACKAGE; procedure TARGET_PRATICE is use I_O_PACKAGE; -- This program reads in four values, respectively representing -- the initial X and Y velocities of a projectile, the distance -- to a target, and the height of the target. -- It prints a message indicating whether the projectile will -- hit the target or not. G: constant FLOAT := 9.81; -- meters per second per second X_VELOCITY, Y.VOLOCITY, TARGET_DISTANCE, TARGET_HEIGHT, NET_RISE: FLOAT; procedure COMPUTE_RISE(V_X, V_Y, DISTANCE: in FLOAT; RISE: out FLOAT) is TIME: FLOAT; begin TIME := DISTANCE / V_X; RISE := V_Y*TIME - (G/2.0)*(TIME**2); end; begin 18 PUT_LINE("ENTER X AND Y VELOCITIES, DISTANCE, AND HEIGHT:"); GET(X_VELOCITY); GET(Y_VELOCITY); GET(TARGET_DISTANCE); GET(TARGET_HEIGHT); COMPUTE_RISE(X_VELOCITY, Y_VELOCITY, TARGET_DISTANCE, NET_RISE); if NET_RISE 0.0 and NET_RISE < TARGET_HEIGHT then PUT("HIT"); else PUT("MISS"); end if; end; 4. Especificação de uma LP A descrição de uma linguagem de programação envolve dois aspectos: sintaxe e semântica. A sintaxe é o conjunto de regras que determinam quais construções são corretas e a semântica é a descrição de como as construções sintaticamente corretas são interpretadas ou executadas. Ex: a := b (Pascal) • comando de atribuição correto (sintaxe) • substitua valor de a com o valor atual de b (semântica) 4.1. Sintaxe de uma LP A sintaxe de uma LP é descrita por uma Gramática que tem como elementos básicos: 1. Conjunto de Símbolos Terminais (T): aparecem nas construções da LP; um conjunto de caracteres. 2. Conjunto de Símbolos Não Terminais (NT): não presentes na LP; nomes de categorias sintáticas definidas pelas produções. Notação: < . 3. Conjunto de Produções (P): definições de símbolos não terminais. Forma: <NT ::= {T U NT}*. 4. Símbolo Inicial (S): um dos NT. Exemplo: Gramática de uma linguagem de calcular em BNF (Backus Naur Form): P: <cálculo> ::= <expressão> = <expressão> ::= <valor> | <valor><operador><expressão> <valor> ::= <número> | <sinal><número> <número> ::= <semsinal> | <semsinal>.<semsinal> <semsinal> ::= <dígito> | <dígito><semsinal> <dígito>::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 <sinal> ::= + | - <operador> ::= + | - | / | * 19 --- T, --- NT, --- Metalinguagem S = <cálculo> Na notação anterior, poderíamos expressar: opcionalidade [ ], repetição { } e alternância |. O uso desses 3 metasímbolos estende a BNF para a notação EBNF (Extended BNF). Rescrevendo os NT <expressão>, <valor>, <semsinal. e <número>: <expressão> ::= <valor> [<operador><expressão>] <valor> ::= [<sinal>] <semsinal> [.<semsinal>] <semsinal> ::= <dígito> {<dígito>} PS: <número> pode ser eliminado. Uma Gramática pode ser usada para gerar sentenças válidas e para verificar se uma seqüência de símbolos é válida. 20 Gerando sentença válida: Cadeia Atual Produção Aplicada S = <cálculo> 1 <expressão> = 3 <valo><operador><expressão> = 4 <número><operador><expressão> = 6 <semsinal><operador><expressão> = 9 <dígito><semsinal><operador><expressão> = 12 2<semsinal><operador><expressão> = 8 2<dígito><operador><expressão> = 15 25<operador><expressão> = 24 25 * <expressão> = 2 25 * <valor> = 4 25 * <número> = 7 25 * <semsinal>.<semsinal> = 8 25 * <dígito>.<semsinal> = 11 25 * 1.<semsinal> = 8 25 * 1.<dígito> = 15 25 * 1.5 = cadeia final 21 Verificando se 6 + 3 / 12 = é um cálculo: 6 + 3 / 1 2 = <dígito> <operador> <dígito> <operador> <dígito> <dígito> | <semsinal> | <semsinal> | | <semsinal> | <número> | <número> | <semsinal> | <valor> | <valor> | <número> | | | | | <valor> | | | | | <expressão | | | <expressão> | <expressão> | <cálculo> Redução de uma cadeia inválida (6 * =): 6 * = <dígito> <operador> | <semsinal> | | <número> | | <valor> | | ? ? ? Gramáticas que podem ser descritas em BNF e EBNF são conhecidas como Gramáticas Livres de Contexto: definições de NT são independentes do contexto em que o NT aparece. A maioria das LPs não são Livres de Contexto - elas contém algumas regras sensíveis ao contexto. Ex: "variável deve ser declarada antes de ser usada". São, assim, chamadas linguagens sensíveis ao contexto. Os métodos formais para reconhecer linguagens sensíveis ao contexto são bastante complexos. Na prática, especifica-se uma LP como livre de contexto e trata-se a sensibilidade ao contexto de maneira informal. 4.2. Semântica de LP A semântica de uma LP, que corresponde a como esta LP será implementada, pode ser descrita de maneira formal, porém, em geral, é descrita de maneira informal. Esta apresentação dará um enfoque operacional ao problema, com o objetivo de fornecer uma visão dos problemas encontrados quando da implementação de linguagens. 5. Tradução de uma LP Existem dois métodos para se traduzir um programa escrito em uma determinada linguagem de programação para a linguagem de máquina: Interpretação e Compilação. 5.1. Interpretação Um interpretador traduz o programa fonte um comando por vez e chama uma rotina para executar esse comando. A vantagem é que o interpretador não traduz comandos que podem não ser executados e pode relatar erros na linguagem original em cada ponto de execução. Na prática as linguagens interpretadas servem para a realização de uma prototipagem rápida. 5.2. Compilação Um Compilador traduz o programa fonte inteiro, produzindo um outro programa equivalente, em linguagem executável. A vantagem é que o compilador precisa traduzir um comando apenas uma única vez, não importando quantas vezes ele será executado. Na prática o compilador é usado para gerar o código definitivo (eficiente) de um programa. 22 O processo de compilação: Fases Principais da Compilação: 6. Características de Design de LP O principal objetivo de uma LP é auxiliar o programador no processo de desenvolvimento de software. Isso inclui auxílio no projeto, implementação, teste, verificação e manutenção do software. Algumas características que contribuem para isso são: • Simplicidade: • Nível semântico: número mínimo de conceitos e estruturas. Os conceitos devem ser naturais, de fácil aprendizado, com pouco risco de má interpretação. • Nível sintático: cada conceito deve ter representação única e "clara" - evitar sintaxe muito concisa, pois isto é contraprodutivo para inteligibilidade. Deve-se excluir múltiplas representações e representações confusas. 23 24 • Abstração: apenas aspectos relevantes dos objetos. Tem reflexo nas tarefas de design, implementação e modificação de programas. • Nível de dados: programador pode trabalhar mais efetivamente usando abstrações mais simples, que não incluam detalhes irrelevantes dos dados. • Nível de procedimentos: facilita modularidade e boas práticas de design. • Expressividade: facilidade com que um objeto pode ser representado. As LPs devem permitir uma representação natural de objetos e procedimentos (por exemplo, estruturas de dados e de controle apropriadas). Simplicidade e Expressividade são características um tanto antagônicas e a maior aplicação de uma ou outra depende do domínio de aplicação do problema. Deste modo deve-se restringir os problemas a domínios específicos. • Ortogonalidade: refere-se à integração entre os conceitos, o grau de interação entre diferentes conceitos, e como eles podem ser combinados de maneira consistente. Por exemplo, quando uma "string" não pode ser passada como parâmetro, os conceitos de "string" e "parâmetro" não podem interagir, e portanto, há falta de ortogonalidade. Além disso, se umaLP usa o operador := para atribuição de "inteiro" e <- para atribuição de "string", então o conceito "atribuição interage inconsistentemente com os de "inteiro" e "string". A ortogonalidade reduz o número de exceções de regras e torna a LP mais fácil de ser aprendida. Também leva a dificuldades: combinações difíceis de se implementar ou mesmo improváveis de ocorrer. • Portabilidade: movimento de um programa de uma máquina a outra. Utilização de um padrão independente de máquina. 7. Escolha de uma LP A escolha de uma linguagem de programação deve basear-se em 7 critérios básicos: • implementação • disponibilidade quanto à plataforma • eficiência: velocidade de execução do programa objeto • competência na LP • experiência do programador • competência do grupo envolvido • portabilidade • necessidade de rodar em diferentes máquinas • sintaxe • certos tipos de aplicação acomodam-se melhor em certas sintaxes • semântica • aplicação X facilidades • por exemplo, para processamento concorrente pode-se usar ADA, para utilização de recursividade pode-se usar Pascal 25 • ambiente de programação • ferramentas para desenvolvimento de software diminuem o esforço de programação • bibliotecas • modelo de computação • aplicação X modelo de computação • por exemplo, para realização de busca heurística é adequado o modelo lógico, para simulações, o modelo orientado a objeto 8. Paradigmas de LP Os paradigmas, ou também chamados modelos, de linguagens de programação são: • Paradigma Imperativo • Paradigma Funcional • Paradigma Lógico • Paradigma Orientado a Objetos 8.1. O Paradigma Imperativo de LP O modelo Imperativo é baseado na perspectiva do computador: a execução seqüencial de comandos e o uso de dados são conceitos baseados no modo como os computadores executam programas no nível de linguagem de máquina. Este modelo é o predominante. As LPs imperativas são de fácil tradução. Um programa imperativo é equivalente a uma seqüência de modificações de locações de memória. Linguagens Imperativas: FORTRAN, COBOL, ALGOL 60, APL, BASIC, PL/I, SIMULA 67, ALGOL 68, PASCAL, C, MODULA 2, ADA. 8.1.1. Binding 8.1.1.1. "Information Binding" As LPs imperativas imitam as ações dos computadores ao nível de linguagem de máquina (LM). Nesse nível, os computadores operam com 2 unidades principais: CPU e memória. Uma unidade de execução típica em LM consiste de 4 passos: 1 - Obter o endereço de locações para um resultado e 1 ou mais operandos 2 - Obter o dado operando da(s) locação(ões) do operando 3 - Computar o dado resultado dos dados operandos 4 - Armazenar o dado resultado na locação do resultado Exemplo: A := B + C seria executado como: 1 - Obter os endereços de A, B e C 2 - Obter os dados dos endereços B e C 3 - Computar o resultado de B + C 4 - Armazenar o resultado na locação de A Apesar da abstração de endereços em nomes, as LPs imperativas mantém os 4 passos como uma unidade de programa padrão. Essa unidade de execução se tornou a unidade fundamental de LP imperativas - e é chamada de comando de atribuição (em BNF: <nome> <op_atribuição> <expressão>). De fundamental importância para a performance dessa atribuição é o estabelecimento e uso de um número de bindings ("ligações"): passo 1: binding entre nomes e locações de operandos e resultados passo 2: binding entre locação e valor para estabelecer valores de operandos passo 4: binding entre locação do resultado e o valor computado No passo 3, a computação depende da interpretação dos dados e dos operadores definidos. LPs relacionam dados e operadores através de tipos. Veremos o papel de tipo em binding. Além disso será discutido o Escopo dos bindings: definição e mudança de bindings. Dados objetos & Bindings Dado objeto = (L, N, V, T) onde L - locação, N - nome, V - valor, T - tipo Binding é a atribuição de valor a um dos quatro componentes acima. Esses bindings podem ser alterados em certas ocasiões. Os bindings podem ocorrer em tempo de compilação, em tempo de loading (quando o programa LM gerado pelo compilador está sendo alocado à locações específicas na memória), ou em tempo de execução. Bindings de locação geralmente ocorrem em tempo de loading, mas também podem ocorrer em 26 tempo de execução (variáveis em procedimentos e alocação dinâmica). Bindings de nome ocorrem tipicamente em tempo de compilação, quando uma declaração é encontrada. (em arrays e records ocorrem bindings compostos de nomes). Bindings de tipo ocorrem geralmente em tempo de compilação, através de declarações de tipo. Veja exemplos dos efeitos da declaração de tipos em ADA nas figuras seguintes. Binding de tipo dinâmico ocorre durante tempo de execução, não havendo declarações. O tipo do dado objeto é determinado pelo tipo do valor, e portanto, uma mudança de valor implica em novo binding. Exemplos de binding em ADA: a) A : integer; 27 b) B : integer := 0; c) C : constant integer := 0; 8.1.1.2. Escopo de Binding e Unidades de Execução Divisões de um programa: • programa - maior divisão; unidade executável fundamental • blocos • comando - menor divisão; unidade indivisível O objetivo de se juntar unidades de execução, neste caso, é para identificar o escopo de bindings. Comandos: 28 29 • unidade de controle fundamental de LPs imperativas • menor unidade traduzível • comandos compostos (condicionais, iterativos) Em LPs como LISP e PROLOG, os comandos possuem um único formato: LISP - (nome_função (par1 ... parn) <corpo>) PROLOG - predicado (arg1, ..., argn) :- <corpo>. A maioria das LPs imperativas possuem várias sintaxes para tipos diferentes de comandos. Referência a comandos: labels <label> : <comando> Binding - tempo de compilação (Pascal, ADA, FORTRAN) - tempo de execução (SNOBOL, APL) Variações: labels podem ser: • obrigatórios - BASIC • opcionais - Pascal, ADA, FORTRAN, C • inteiros - Pascal, FORTRAN, BASIC • identificadores - ADA, C • declarados explicitamente - Pascal • declarados implicitamente - ADA, FORTRAN • detectados por posição - FORTRAN • detectados por notação - Pascal, C Delimitação de Comandos: • um comando por linha - FORTRAN • marca delimitadora - ADA, C, Pascal (em ADA e C ';' termina comandos e em Pascal separa comandos) Pascal: ... x := x + 1 else ... ADA, C: ... x := x + 1; else ... Blocos: conjunto de comandos para atender a determinado fim. 1 - Escopo de estrutura de controle 30 Estruturas condicionais Estruturas iterativas 2 - Escopo de procedimentos ou funções 3 - Unidades de compilação Blocos compilados separadamente e depois unidos para execução 4 - Escopo de bindings Bloco de comandos sobre os quais bindings específicos são válidos 8.1.1.3. Escopo de Binding de Nome Blocos que definem um escopo de binding de nome contém, em geral, duas partes: 1. Uma seção de DECLARAÇÕES, que define os bindings que valem dentro do bloco 2. Uma seção EXECUTÁVEL, que contém os comandos do bloco, onde valem os bindings Sintaticamente, isso requer delimitadores. Ex: ... BLOCK A; DECLARE I; BEGIN A ... {I de A} - binding válido END A; ... Num bloco, dois tipos de bindings: local - feito por declarações no bloco não-local - feito por declaração fora do bloco. Blocos aninhados: program P; declare X; begin P ... {X de P} block A; declare Y; begin A ... {X de P; Y de A} block B; declare Z; begin B ... {X de P; Y de A; Z de B} 31 end B; ... {X de P; Y de A} end A; ... {X de P} block C; declare Z; begin C ... {X de P; Z de C} end C; ...{X de P} end. Política do Escopo Léxico ou Estático: 1. Se um nome tem uma declaração num bloco, este nome é ligado ao objeto especificado na declaração. 2. Se um nome não tem declaração num bloco, ele é ligado ao mesmo objeto ao qual ele foi ligado no bloco que contém o bloco atual, no texto do programa. Se o bloco não está contido em outro, ou se o nome não foi ligado no bloco que contém este, então o nome não está ligado a qualquer objeto no bloco atual. "Hole-in-scope" - Situação em que um bloco redeclara um nome já ligado no ambiente que o contém. Neste caso, a declaração local se sobrepõe ao binding não-local, fazendo o objeto ligado não- localmente inacessível no bloco atual. Ex: program P; declare X, Y; begin P ... {X de P; Y de P} block A; declare X, Z; begin A ... {X de A; Z de A; Y de P; X de P é inacessível em A} end A ... {X de P; Y de P} end P; Escopo Estático x Escopo Dinâmico Escopo Dinâmico: O ambiente "global" de uma unidade (procedure) é o ambiente da unidade que a chamou. Isto é, uma procedure herda como ambiente global aquele da unidade que a chamou e, portanto, este só pode ser determinado em tempo de execução. Contornando o "Hole-in-scope": 32 ADA - via sintaxe: no exemplo, P.X refere-se ao objeto X ligado em P, enquanto X é o objeto ligado em A. 8.1.1.4. Escopo de Binding de Locação Hipótese assumida: binding de locação feito em tempo de loading, permanecendo válido durante toda a execução do programa. Efeito colateral: ao re-executar um bloco, as variáveis locais reteriam os valores da execução anterior. Se um novo binding de locação for feito a cada nova entrada no bloco, nenhuma suposição poderia ser feita. Exemplo: program P declare I; begin P for I := 1 to 10 do block A; declare J; begin A if I = 1 then {I de P; J de A} J := 1 else J := J * I {assume-se que J retém valor de execução prévia} endif end A; end P Desvantagem: memória para todos os blocos do programa deve ter reservada por todo o tempo de execução do programa. Alternativa: (binding dinâmico) Fazer o binding de locação, bem como o de nome, em tempo de execução, a cada entrada de um bloco, desfazendo esses bindings quando da saída do bloco. (Alocação dinâmica de memória). Extensão do binding: período de tempo, durante execução, em que o binding é válido. Implementação: via registros de ativação que são registros contendo informações sobre uma unidade de execução, necessárias para restabelecer sua execução, depois de ela ter sido suspensa. Para efeito de binding de locação, o registro de ativação precisará conter apenas as locações para todos os objetos ligados localmente, mais um ponteiro para o registro de ativação do bloco que o contém. Funcionamento: Conforme um bloco é ativado, seu registro é colocado no topo de uma pilha. No término de sua execução, seu registro é desempilhado. Exemplo: program P; declare I, J; begin P block A; declare I, K; begin A block B; declare I, L; begin B; ... end B; ... end A; block C; declare I, N; begin C ... end C; end P; Uso da pilha para localizar objetos: 1. Procura no registro do topo pelo objeto ligado ao nome. 2. Se não encontrar, procure-o no registro apontado por ele; continue nesse processo até encontrá- lo na lista, ou a lista acabar. Esta busca pode ser otimizada, uma vez que se sabe, em tempo de compilação, a estrutura da pilha e de cada registro de ativação. Em C: "source file" - outra unidade de execução Uma declaração precedida pela palavra extern torna visível um objeto daquele nome, que tenha sido definido num source file diferente. Esses arquivos podem ser compilados separadamente e, portanto, servem como unidades de compilação. extern int compare_strings(); 33 34 extern char *read_string(); extern void copy_string(); 8.1.1.5. Exercícios 1. Em quais situações bindings de locações são feitos em tempo de execução? 2. Que fatores deve-se considerar ao escolher entre um espaço de identificadores mais ou menos restritivo? 3. Algumas LPs permitem o binding de tipo a um objeto em tempo de execução. Discuta vantagens e desvantagens desta estratégia. 4. Quando uma LP oferece tipos reais em ponto fixo e em ponto flutuante, o que é importante considerar ao decidir o tipo de um dado objeto? 5. Que tipo de uso se faria de bindings de labels em tempo de execução? Que perigos isso oferece? 6. Considere o mecanismo de passagem de parâmetros do Pascal. a) O binding feito entre os parâmetros atuais e os formais é estático ou dinâmico? b) Especule como esse mecanismo funcionaria se fosse o oposto do que você respondeu acima. 8.1.2. Estruturas de controle As estruturas de controle são alternativas ao modo seqüencial e podem ser: condicional, iterativa e desvio incondicional. 1) Condicional: determina bloco de execução baseado em testes. Existem 4 formas de estrutura condicional: condicional simples, condicional com 2 alternativas, condicional multi-alternativa e condicional não-determinística. 2) Estruturas Iterativas: iteração infinita, iteração pré-teste, iteração pós-teste, iteração in-teste, iteração com número fixo de passos e iteração não-determinística. 3) Desvio Incondicional. 8.1.2.1. Condicional Simples Consiste de um teste único que determina se um bloco deve ou não ser executado. if <expressão booleana> then <bloco de comandos> Variação: delimitação do bloco de comandos: ALGOL 60, Pascal: begin ... end C: { ... } ADA: endif (keyword) 35 MODULA 2: end (keyword) 8.1.2.2. Condicional Com 2 Alternativas if <expressão boolena> then <bloco de comandos> else <bloco de comandos> Pascal: "dangling else" em condicionais aninhadas. Exemplo: if x 0 then if x < 10 then x := x + 1 else {pertence a qual if?} x := x - 1; Para inicialmente x = 12, se pertencer ao primeiro if, x = 12, se pertencer ao segundo, x = 11. A identação é irrelevante. Possíveis soluções: if x 0 then begin if x < 10 then x := x + 1 else x := x - 1 end; ou if x 0 then begin if x < 10 then x := x + 1 end else x := x - 1; Regra implícita do Pascal: "else" fecha sempre o "if" mais próximo. O uso de keywords evita ambigüidades: ADA: if x 0 then if x < 10 then x := x + 1; else x := x - 1; endif; 36 endif; ou if x 0 then if x < 10 then x := x + 1; endif; else x := x - 1; endif; Outra fonte de erro no Pascal: <comando>; else (neste caso ; é separador e não terminador como em C) que é interpretado como: <comando>; <comando nulo> else (o que dá erro) 8.1.2.3. Condicional Multi-alternativas Existem 2 formas possíveis: 1) Seqüência de expressões booleanas: a primeira expressão verdadeira determina o bloco de comandos a ser executado. Exemplo: Em ADA: if <cond1> then <comando1> elsif <cond2> then <comando2> ... elsif <condN> then <comandoN> [else <comandoN+1>] endif Simulação em Pascal: if <cond>1 then begin <comando1> end else if <cond2> then begin <comando2> end else if ... ... (aninhamento) else begin <comandoN+1> 37 end; 2) Comando CASE: avalia uma expressão e executa bloco correspondente àquele valor. Obs: é mais restrito que o caso 1. Exemplo: Em Pascal: case <expressão> of <val1>: <comando1> <val2>:<comando2> ... <valN>: <comandoN> end Onde <expressão> pode ser qualquer tipo discreto (int, char, tipo enumerado) e <val1> ... <valN> são listas de valores. Em C: switch (<expressão seletora>) { { case <expressão> : [<seq. de comandos>]} [default: <seq. de comandos>] } Onde: <expressão seletora> é interpretada como um inteiro; Cada expressão case pode especificar um único valor; O bloco de comandos a ser executado começa na expressão case "matched"e vai até a última seqüência de comandos (mesmo as nulas) - ("break" interrompe essa série de execuções). Exemplo: switch (i) { case 0: case 1: printf ("1"); case 2: printf ("2"); break; case 5: case 3: printf ("4"); default: printf ("5"); } 8.1.2.4. Condicional Não-Determinística É uma extensão da condicional multi-alternativa proposta por Dijkstra (1975). Forma geral proposta: if <cond1> <seqüência de comandos 1> 38 when <cond2> <seqüência de comandos 2> ... when <condN> <seqüência de comandos N> fi Todas as condições são avaliadas (e não até encontrar a primeira verdadeira). Quando mais de uma for satisfeita, a seqüência de comandos a ser executada é escolhida de forma não-determinística. Ou seja, não há uma regra para escolher uma delas - qualquer uma pode ser escolhida. Se nenhuma condição for satisfeita, um erro ocorre. As condições são chamadas "guardas" ou "sentinelas". ADA tem uma versão desse comando e a usa para implementar controle de concorrência. 8.1.2.5. Iteração Infinita do forever <seqüência de comandos> end do Simulação em Pascal: while true do begin <seqüência de comandos> end; Em ADA: (comando LOOP) LOOP <seqüência de comandos> end LOOP; 8.1.2.6. Iteração Pré-Teste Em Pascal: while <condição de continuação> do <corpo_iteração> Em C: while (<expressão>) <corpo_iteração> 8.1.2.7. Iteração Pós-Teste Em Pascal: 39 repeat <corpo_iteração> until <condição de terminação> Em C: do <corpo_iteração> while (<expressão>) Simulação em ADA: LOOP <corpo> exit when <condição>; end LOOP; 8.1.2.8. Iteração In-Teste Teste no meio do corpo de iteração (caso bem justificado de "goto") Em ADA: (através do comando exit [when <condição>]) LOOP <corpo_iteração1> exit when <condição> <corpo_iteração2> end LOOP; Extensão: aninhamento LOOP <corpo_A> LOOP <corpo_B> exit when <condição> (sai do loop mais interno) <corpo_C> end LOOP; <corpo_D> end LOOP; ou OUTER: LOOP (forma de rótulos exclusiva desse comando) <corpo_A> LOOP <corpo_B> <- exit OUTER when <condição> <corpo_C> end LOOP; <corpo_D> -> end LOOP OUTER; Em C: (através dos comandos break e continue) do { 40 ... do { (<-2) ... if (cond1) { break (1->) continue (2->) } } while (cond2); } (<-1) while (cond3); 8.1.2.9. Iteração com Número Fixo de Passos O controle é feito por VC (Variável de Controle) e a forma mais geral é: for <VC> := <V. inicial> to <V. final> step <incremento> do <corpo> ou for (<V. in.>; <V. Contin.>; <expr. mod.>) <com>; Onde todas as expressões são do mesmo tipo de VC. Variações entre as linguagens: • Tipos permitidos para VC: • ADA, Pascal: integer, char, enumerados • FORTRAN: integer, real • C: integer • Escopo de VC: • ADA: equivale à declaração local • Pascal, C, FORTRAN: escopo da declaração VC pode ser alterada no corpo da iteração? Em geral, não. Valor de VC depois do termino da iteração: • ADA: não haverá binding de locação e valor • Pascal: indeterminado • C: valor final incrementado Quando as expressões final e de incremento são avaliadas? Em Pascal: for i := 1 to n (I) do n := n + 1; 41 Se (I) for reavaliada a cada passo, essa iteração seria infinita. Em geral, as 2 expressões (final e incremental) são avaliadas uma única vez, no início da iteração. Assim, a modificação de n, acima, não alteraria o número de passos inicial. Formas de incremento: • ADA, Pascal: "incremento de 1" (succ) • C, FORTRAN: expressões Decrementos: • Pascal: for i := 6 downto 1 do ... • ADA: for i in reverse 1..6 loop ... end loop Desvios para o interior do loop • ADA, FORTRAN: proíbem • Pascal: permite 8.1.2.10. Iteração Não-Determinística do when <cond1> <seqüência de comandos1> when <cond2> <seqüência de comandos2> ... when <condN> <seqüência de comandosN> od Repete enquanto pelo menos uma condição for verdadeira. Se mais de uma é verdadeira, a escolha da seqüência é feita não-deterministicamente. 8.1.2.11. Desvio Incondicional goto <label> Módula 2 não tem esse comando. Labels: • Pascal: declarados 42 • C, FORTRAN, ADA: implícitos • PL/I, SNOBOL, APL: variáveis (podem ser passados como parâmetros e permitem arrays da labels) 8.1.3. Tipos de Dados Checagem de tipo A checagem de tipo consiste na determinação do tipo de um certo dado objeto. Pode ocorrer em tempo de compilação, de execução ou pode não ocorrer. LPs com checagem de tipo em tempo de compilação são ditas fortemente tipadas (por exemplo, ADA). Pascal, por exemplo, não é fortemente tipada: a maior parte da checagem é feita em tempo de compilação, mas os subintervalos e os registros variantes não são checados. Quando não há possibilidade de haver checagem em tempo de compilação em determinadas circunstâncias, há duas alternativa: ou não se faz a checagem (Pascal), ou se faz em tempo de execução - o que pode ser muito caro em tempo (cada vez que o dado objeto é referido) e em espaço (um indicador de tipo deve ser armazenado como parte de cada valor de dado). LPs dinamicamente tipadas, onde os bindings são feitos em tempo de execução, fazem checagem apenas em tempo de execução, enquanto que LPs estaticamente tipadas, com bindings em tempo de compilação, podem checar tipos ou em tempo de compilação ou em tempo de execução. Exemplo da checagem em Pascal: type soft = record case test: boolean of true: (first: 1..20); false: (second: char); end; var x, y: soft; c: char; begin ... c := x.second; y.first := 2 * x.first; ... end. Pascal não pode verificar totalmente os tipos por 2 razões: 1. Quando x.second é usado, não é possível checar, durante a compilação, se a segunda parte variante de x está ativada. (se x.teste = true, então x.second tem "lixo") 2. Na atribuição a y.first, haverá violação se x.first 10, o que não é possível verificar em tempo de compilação. Conversão de tipos A conversão de tipos pode ser implícita (coerção de tipos) ou explicita. Por exemplo, há conversão implícita entre um "integer" e um "real" e explicita quando se usa float(), round, trunc. Quando dois tipos são considerados iguais? 43 Sejam os tipos e variáveis: type T1: 0..10; T2: 0..10; var A: T1; B, C: T1; D: T2; São legais as seguintes atribuições? A := B; B := C; A := D; Existem 3 perspectivas para se estabelecer a igualdade entre tipos: 1. Equivalência de domínios: dois dados objetos são equivalentes se tiverem associados a seus tipos um mesmo domínio de valores possíveis (equivalência estrutural). Neste caso A := B; A := D; e B := C são legais (A, B, C, D são tipos equivalentes). É o que ocorre em ADA. 2. Equivalência de nome: dois dados objetos são de tipos equivalentes se seus tipos tiverem o mesmo nome. Neste caso A, B e C são variáveis de tipos equivalentes, mas D não é. É o que ocorre em Pascal. 3. Equivalência de declaração: dois dados objetos são de tipos equivalentes se eles são ligados ao tipo numa mesma declaração. Neste caso só B e C são de tiposequivalentes. E os tipos anônimos? Por exemplo (ADA, Pascal): var E, F: 0..10; G: 0..10; Neste caso não existe equivalência de nome. Subtipos e Tipos Derivados Por exemplo: var A: integer; B: 0..100; A := B é válido? e B := A O subtipo inclui o conjunto de operadores do tipo principal. Nestes casos a checagem é realizada em tempo de execução. Forçando incompatibilidade em LP com equivalência de domínio (ADA): type tempo is new float; 44 comprimento is new float; duração: tempo; distância: comprimento; Neste caso o domínio de "duração" é igual ao domínio de "distância" mas a operação "duração + distância" não é permitida. Tipos de dados Escalares São aqueles dos dados objetos atômicos, que não podem ser desmembrados em componentes. Podem ser Built-in types (já presentes na LP) ou User-Defined types (definidos pelo usuário). Propriedades a considerar: 1. Parâmetros do tipo (por exemplo, precisão) 2. Formato da declaração 3. Domínio 4. Operações (operadores e funções) 5. Atributos 6. Conversão 7. Constantes pré-definidas 8. Implementação As LPs em geral apresentam os seguintes tipos básicos: Tipo Numérico – Inteiro, Tipo Numérico – Real, Tipo Boolean, Tipo Ponteiro, Tipos Enumerados e Tipos Compostos. 8.1.3.1. Tipo Numérico - Inteiro Algumas LPs suportam um único tipo inteiro ( integer) cujo domínio é o intervalo dos inteiros que podem ser representados na máquina. A desvantagem desta implementação é que tem o domínio dependente de implementação. Outras LPs permitem a especificação de subtipos de inteiros, limitando o domínio a um intervalo do domínio de inteiros da máquina. Exemplos: • Módula 2: "integer" e "cardinal" (não-negativos) • C: "short int", "int", "long int", "unsigned" • Pascal: type Positivos = 1..maxint • ADA: subtype T is Integer range 0..maxint (o tipo é derivado do tipo das constantes do intervalo) A checagem de tipos para os subtipos pode ser de duas maneiras: • ignorar a checagem, já que há necessidade de checagem em tempo de execução - o que custa tempo. • checar, em tempo de execução, toda vez que um novo binding de valor é feito A escolha entre essas opções pode ser feita em tempo de compilação. Implementação 45 Geralmente a linguagem segue a representação de inteiros da máquina em que a LP é implementada. (em geral binário com complemento de 2) Operações São em geral implementadas diretamente em linguagem de máquina (LM) ou podem ser programadas em LM usando algoritmos aritméticos padrões. 8.1.3.2. Tipo Numérico - Real Possíveis parâmetros: • número de dígitos significativos • fator de escala designando o número de dígitos à direita do ponto decimal Exemplo: Números reais com 10 dígitos significativos e fator de escala de 2 dígitos representariam todos os números de -99999999.99 até 99999999.99 com intervalo de 0.01 de distância (representação em ponto fixo). A maioria das LPs utiliza representação com ponto flutuante (mantissa e expoente) ADA utiliza tanto ponto fixo como ponto flutuante. 8.1.3.3. Tipo Boolean Domínio = {Falso, Verdadeiro} Em C representado como 0 e 1 (boolean é um subtipo de integer) Operadores: AND, OR, NOT Considere: (I <> 0) AND (K/I > 6) Se I <> 0 for falso, então K/I não pode ser executado, desde que I é zero. Mas, se a primeira condição é falsa, então não há necessidade de avaliar a segunda, já que a conjunção será falsa. Então uma implementação mais esperta prevê: A AND B = if A then B else FALSE e A OR B = if A then TRUE else B ADA possui AND THEN e OR ELSE - opcional para programador - além de AND e OR. Constantes FALSE e TRUE. 8.1.3.4. Tipo Ponteiro O valor de um ponteiro é o endereço de um outro dado objeto. Neste caso existem dois dados objetos envolvidos: o apontador e o apontado. Bindings para ponteiros Neste caso, os bindings de tipo e nome ocorrem em tempo de compilação, o binding de locação ocorre em tempo de loading e o de valor, em tempo de execução. O binding estático de tipo requer a declaração do tipo do objeto apontado (ADA, PASCAL, C), já no binding dinâmico de tipo o tipo do objeto apontado é derivado do valor a ele atribuído em tempo de execução. Exemplo: • PASCAL: var P: ^ tipo; • ADA: P: access ^ tipo; • C: int * P; O binding de valor a um ponteiro é feito de duas formas: • criação • Pascal - new (P); • ADA - P := new tipo; ou P := new integer'(0); - cria e aponta para o inteiro 0 • C - malloc (P); • atribuição • Pascal - P := Q; - onde var P, Q: ^tipo; • C (operadores de ponteiros) - 46 int *P; int X; *P = X; P = &X; Desalocação de objetos apontados em tempo de execução: • Pascal - dispose (P); • ADA, C - free (P); Atenção (desalocação implícita x explícita) : uma vez desalocado o objeto apontado, é desfeito o binding de valor do objeto ponteiro - o que inviabiliza o uso do elemento apontado. Esses ponteiros são ditos estar "suspensos". (Mas eles podem ter reestabelecido o binding de valor através de uma atribuição). O objeto apontado desalocado é dito garbage, impossível de ser alcançado pelo programa. (Garbage Collection - Alocação dinâmica) 47 "Deferenciar" um ponteiro é o processo de obter o valor do objeto apontado, através da referência do nome do ponteiro. Exemplo: • Pascal - P^ • ADA - P.ALL • C - *P Constantes utilizadas: • Pascal - NIL • ADA, C – NULL 8.1.3.5. Tipos Enumerados (domínio criado pelo usuário) O usuário cria e dá nome a uma lista finita de valores. Em Pascal por exemplo: type cor = (verde, azul, branco); var c: cor; Operadores: atribuição e comparação (ordem em que aparecem na lista - verde < azul < branco). Em Pascal existe também succ( ) e pred( ). Implementação: mapeamento nos inteiros não negativos 0, 1, 2, ... . 48 49 Dificuldades: 1) Mesmo identificador em tipos distintos Exemplo: type fruta = (maçã, laranja, pêra); cor = (branco, azul, laranja); var F: fruta; C: cor; Pascal proíbe, ADA permite e trata como: Exemplo: f := laranja - fruta (pelo contexto - F é do tipo fruta) Em contextos ambíguos, permite o uso da forma F := fruta'laranja. 2) Input/Output Pascal não permite. Outras LPs provêm conversão automática entre strings e representação interna. Ex: r. u. string 0 - maçã - 'maçã' 1 - laranja - 'laranja' 2 - pêra - 'pêra' 8.1.3.6. Tipos de Dados Compostos Existem várias formas de composição, correspondendo aos diversos tipos de dados compostos: • Mapeamento: f: T1 -> T2 (arrays) • Produto Cartesiano: T = T1 x T2 x ... x Tn (records) • União discriminante: T = T1 U T2 U ... U Tn (records variantes) • Seqüência: T = {(t1, t2, ..., tn)} (strings, files) • Conjunto Potência: T = 2T1 (sets) 8.1.3.6.1. Array <def. array> ::= array [<tipo domínio>] of <tipo base> onde <tipo domínio> é finito e <tipo base> é qualquer. Em Pascal, FORTRAN e C o binding de <tipo domínio> é feito em tempo de compilação. Isso determina a impossibilidade de usar parâmetros, do tipo array, com domínios gerais. 50 Exemplo: type TipoA1 = array [1..10] of integer; TipoA2 = array [1..20] of integer; var A1: TipoA1; A2: TipoA2; procedure media1(A: TipoA1, var media: integer); procedure media2(A: TipoA2, var media: integer); ... media1(A1, m); media2(A2, m); Em ADA a definição é irrestrita. Exemplo: type vector is array (integer range <>) of float; var V: vector (INF..SUP); {declaração local a um bloco} APL e SNOBOL permitem alteração do domínio. Seleção: a[i] a(i) slice: a (3..N) - ADA Construção: ADA: (1.0, 5.0, 3.0, 6.0) Extensão: (1 -> 7.0, 5..12 -> 1.0, others -> 0.0) C: int name[10] = {6, 4, 8, 9, 2,
Compartilhar