Paradigmas de Programação Aula 6 Nomes, Vinculações, Verificação de Tipos e Escopos Prof.: Edilberto M. Silva http://www.edilms.eti.br Prof. Edilberto Silva / edilms.eti.br
Introdução As linguagens de programação imperativas são baseadas na arquitetura de Von Neumann. Os espaços de memória utilizados pelas variáveis são diferentes, de acordo com a especificação do tipo de variável (propriedade mais importante da variável). dado 1 dado 2 dado 3 dado 4 instrução instrução instrução
Nomes Considerações de projeto Tamanho máximo? Caracteres de conexão são permitidos? Sensível a maiúscula/minúscula? Palavras especiais são reservadas ou chaves? Tamanho FORTRAN I: máximo 6 / COBOL: máximo 30 FORTRAN 90 e ANSI C: máximo 31 Ada: sem limite, e todos são significantes C++: sem limite, mas implementação impõe limites Conectores (Delimitadores) ($? # @ ~ ; : / \ etc.) Pascal, Modula-2, and FORTRAN 77: Não permite Outras permitem
Nomes Sensível a maiúscula/minúscula Desvantagem: legibilidade (nomes que parecem iguais são diferentes) Em Modula-2 nomes pré-definidos possuem letras maiúsculas e minúsculas (e.g. WriteCard) C, C++, Java, e Modula-2 nomes são sensíveis à caixa (alta e baixa) Nomes nas outras linguagens não são Palavras especiais Def: Uma palavra chave (keyword) é uma palavra que é especial somente em certo contexto Desvantagem: legibilidade pobre Def: Uma palavra reservada é uma palavra especial que não pode ser usada como nome definido pelo usuário.
Palavras Especiais São utilizadas para tornar programas mais legíveis ao denominar acções. Na maioria das L.P., as palavras especiais são classificadas como reservadas, e em algumas, são somente palavras-chave. Palavra reservada: é independente do contexto e não pode ser utilizada como um nome. Exemplo: INTEGER REAL (Não permitido se REAL for reservada) REAL INTEGER (Não permitido se INTEGER reservada)
Variáveis Uma variável é uma abstração de um endereço de memória Variáveis podem ser caracterizadas por um 6-tuplas de atributos: nome, endereço, valor, tipo, tempo de vida e ambiente (escopo) Nome - nem toda variável o possui Endereço - o endereço de memória a que está associada. Uma variável pode possuir endereços diferentes em tempos diferentes durante execução Uma variável pode ter diferentes endereços em partes diferentes do programa. Se dois nomes de variáveis podem ser usados para acessar o mesmo local de memória, eles são chamados de aliases
Variáveis Endereço: é o endereço de memória à qual está associada uma variável. Os Aliases existem quando duas ou mais variáveis apontam para o mesmo endereço de memória. Os aliases são prejudiciais a uma boa legibilidade Nome: identifica uma entidade. Nem todas as variáveis têm nome. ex.: variáveis temporárias show( x+y )
Variáveis Tipo: determina a faixa de valores que a variável pode conter, e o conjunto de operações definidas para os valores deste tipo. n Valor: é o conteúdo da célula de memória associada à variável. n ex. Considere a seguinte atribuição: x := x ; x Left-value endereço da variável x Right-value valor da variável (ou expressão que evolui para um valor)
Variáveis Alguma das justificativas para se criar aliases não são mais válidas; e.g. reutilização de memória em FORTRAN. (Atualmente basta trocá-las por alocação dinâmica) Tipo - determina a amplitude de valores das variáveis e o conjunto de operações que são definidos para estes valores. Obs: no caso de ponto flutuante o tipo também determina a precisão
Variáveis Valor - o conteúdo do local a que a variável está associada. Célula abstrata de memória ( Endereço) - a célula física ou conjunto de células associada a uma variável Considere a atribuição: X := X Como interpretar cada X? O l-value de uma variável é o seu endereço O r-value de uma variável é o seu valor
Vinculação Uma vinculação (binding) é uma associação, entre um atributo e uma entidade ou entre uma operação e um símbolo Associação entre operação de somar e o símbolo '+' Associação dos atributos (nome, endereço, tipo, valor...) Tempo de vinculação ou associação (Binding time) é o tempo em que o vínculo se realiza. O símbolo '*' é vinculado à operação de multiplicação em tempo de design Existem vários tipos de vinculações em linguagem de programação, bem como vários tempos em que a vinculação ocorre
Tempos de Vinculação (binding Time) 1. Em tempo de projeto de linguagem --e.g., associação de operador com símbolo da operação 2. Em tempo de implementação associação do tipo pt. fl. Com sua representação 3. Em tempo de compilação associação de uma variável a seu tipo (C, Pascal, Java, etc) 4. Em tempo de carregamento associar em FORTRAN 77 uma variável a um endereço de memória (ou a C variável static ) 5. Em tempo de execução associar um variável local não estática a um endereço de memória; ou associar um parâmetro formal a seu endereço por exemplo em C ou Pascal.
Tempo de Vinculação Exemplo de tempo de vinculação: seja a atribuição X := X + 10 Em tempo de definição de linguagem Estabelecer os possíveis tipos da variável X, por exemplo, real, inteiro, boleano, char, etc e/ou Permitir que programas definam novos tipos como em C, Pascal, ADA... Em tempo de compilação vincular X a um tipo por uma declaração explícita, ex: float X. Em algumas linguagens esta vinculação pode dar-se em tempo de execução, e.g. Smalltalk, Prolog, LISP. Em tempo de implementação vincular X a valores específicos. Se X for do tipo real, então seus valores, a qualquer instante da execução, é um valor do conjunto da seqüência de bits que representa um número real e depende do hardware que executará o programa.
Tempos de Vinculação Em tempo de execução associar X a um valor específico, por exemplo, via uma atribuição. A atribuição X := X + 10 altera o vínculo de X. Representação da constante 10 Em tempo de definição podemos definir que será representa por pelo string 10 (carater 1 e 0 ) ou por uma seqüência de bits em tempo a ser gerada em tempo de execução. Propriedades do operador + Em tempo de definição escolher + para representar adição. Em geral admite-se sobrecarga deste operador para representar adição em inteiros, reais, complexo e até concatenação de strings. Em linguagens compiladas esta vinculação, em geral, é feita em tempo de compilação.
Vinculação Importância da vinculação - Questões importantes Em tempo de compilação, classificado como vinculação mais cedo ou em tempo de execução, vinculação mais tarde? Vinculação mais cedo menor flexibilidade da linguagem, e.g. Fortran, porém maior eficiência. Vinculação mais tardia maior flexibilidade, e.g. ML, porém menor eficiência.
Formas de Vinculação Um vínculo é estático ocorre antes do tempo de execução e permanece inalterado durante a execução do programa. Um vínculo é dinâmico se ocorre durante a execução ou pode ser alterado durante a execução do programa. Tipos de vinculações (Bindings) 1. Como um tipo é especificado? 2. Quando acontece a associação? Se estático, o tipo pode ser especificado por declaração explicita ou implícita.
Vinculação Estática Uma declaração explicita é um comando de programa usado para declarar tipos de variáveis. Uma declaração implícita é um mecanismo de default para especificar tipos de variáveis (a primeira ocorrência da variável no programa) FORTRAN, PL/I, BASIC, e Perl possuem declarações implícitas Vantagem: escritabilidade Desvantagem: legibilidade
Vinculação Dinâmica Especificado através de comando de atribuição, e.x. LIST = {2, 4, 6, 8}; Vantagem: flexibilidade (unidades genéricas de programas) independente de tipos de dados Desvantagens: 1. Custo alto (verificação dinâmica de tipo e interpretação do tipo) 2. Dificuldade para o compilador detectar o tipo de erro Inferência de Tipo (ML, Miranda, and Haskell) Em vez de atribuição, os tipos são determinados pelo contexto de referência
Vinculação de Memória Reserva (Alocação) - pegar uma célula de um conjunto livre de Células (Vinculação Endereço) Liberação - colocar a célula de volta no conjunto de células livres O tempo de vida de uma variável é o tempo durante o qual ela está associada a uma particular célula de memória. As vinculações de memória a variáveis, podem ser, de acordo com o seu tempo de vida: Variáveis Estáticas / Variáveis Pilhas-Dinâmicas Variáveis Heap-Dinâmicas Explícitas/ Variáveis Heap- Dinâmicas Implícitas Heap: é um conjunto de células de memória, desorganizada devido a imprevisibilidade da sua utilização.
Vinculações de Memória Categorias Variáveis por tempo de vida 1. Estática--associada a célula de memória antes do início da execução e permanece associada à mesma célula durante toda a execução Vantagem: eficiência (endereçamento direto), suporte histórico de subprogramas Desvantagem: falta de flexibilidade (não permite recursão)
Vinculações de Memória 2. Pilha-dinâmica--associação de memória é realizada quando da declaração da variável. Se escalar, todos os atributos, exceto endereço, são associados em tempo de compilação Vantagem: recursão; retém armazenamento Desvantagens: Overhead de alocação e liberação Subprogramas não retém historia Referência ineficiente (endereçamento indireto)
Vinculações de Memória 3. Heap-Dinâmico Explicito--Alocação e desativação por diretivas explicitas, especificadas pelo programador, em tempo de execução Referenciadas somente por apontadores e referências, objetos dinâmicos em C++ (via new and delete) e todos os objetos em Java int * intnode... intnode = new int; /* aloca uma célula */... delete intnode; /* libera célula */ Vantagem: Gerenciamento dinâmico de memória Desvantagem: ineficiente e não seguro
Vinculações de Memória 4. Heap Dinâmico Implícito - Alocação e liberação realizada por comandos de atribuição e.g. todas variáveis de APL Vantagem: Flexibilidade, código muito genérico Desvantagens: Ineficiente- todos os atributos são dinâmicos Perca de detecção de erro Geração de lixo
Verificação de Tipos Generalização do conceito de operandos e operadores para incluir subprogramas e atribuições. Verificação de tipo é a atividade que garante que operandos e operadores são de tipos compatíveis. Um tipo é compatível se for correto para o operador ou se for permitido conversão implícita, pelas regras da linguagem, pelo compilador. Esta conversão implícita automática é chamada coesão de tipos (coercion). Um erro de tipo é uma aplicação de um operador a um operando de tipo não apropriado Se todos os bindings forem estáticos, em geral a, verificação de tipo pode ser em tempo de compilação Se os binding forem dinâmicos, a verificação deve ser em tempo de execução.
Tipificação Forte Uma linguagem é fortemente tipada se podemos detectar todo de tipo de erro em tempo de compilação. Vantagem de fortemente tipada: detecção de uso indevido de variáveis que resultam em erros de tipo Obs: Poucas linguagens são fortemente tipadas Linguagens e tipagem forte: 1. FORTRAN 77 (Não é): parâmetros, EQUIVALENCE / 2. Pascal (Não é): variant records / 3. Modula-2 (Não é): variant records, tipo WORD / 4. C and C++ (Não são): verificação de tipo de parâmetro pode ser evitado em parâmetros; não há verificação para o tipo union Tipificação Forte: 5. Ada, ML e JAVA
Inferência de Tipos Em algumas linguagens declaração de tipo não é necessária, o tipo pode ser inferido pelo contexto em que se encontra (ML) ou mesmo não sendo sendo necessário (LISP) Exemplo: ML fun area(l:int, w:int): int = l*w uso equivalente, por inferência de tipo: fun area(l, w): int = l*w fun area(l:int, w) = l*w fun area(l, w:int) = l*w Situação de erro, impossível inferir: fun area(l, w) = l*w
Compatibilidade Tipos compatibilidade por nome - duas variáveis possuem tipos compatíveis se, ou estão na mesma declaração, ou em declarações que utilizam o mesmo nome de tipo. Fácil de se implementar mas altamente restritivo: Sub-faixas do tipo inteiro não são compatíveis com o tipo inteiro Parâmetros formais devem possuir o mesmo tipo dos correspondentes parâmetros atuais (Pascal) type indextype = 1..100; { um tipo subfaixa} var count : integer; index : indextype; Neste caso index e count não são compatíveis, isto é: count := index => erro (semântico) de tipo
Compatibilidade de Tipo Compatibilidade por estrutura - duas ou mais variáveis possuem tipos compatíveis se seus tipos possuem estruturas idênticas. Maior flexibilidade, porém mais difícil de se implementar. Exemplos: Pascal padrão type celsius = real; fahrentheit = real tipo1 = array [1..10] of integer tipo2 = array [1..10] of integer tipo3 = tipo2; Celsius e fahrentheit => compatíveis tipo1 e tipo2 => não compatíveis significando que no Pascal padrão não existe compatibilidade por estrutura. Tipo3 e tipo2 => compatíveis Pascal => compatibilidade nem por nome nem por estrutura. Pascal => em geral compatibilidade por estrutura
Escopo O escopo de uma variável é o espaço, em termos de comandos, em que é visível (i.e onde pode ser utilizada) Variáveis não-locais de uma unidade de programa são aquelas que são visíveis mas não estão declaradas nesta uunidade. As regras de escopo de uma linguagem determinam como referências a nomes estão associados com variáveis.
Escopos Escopo Estático Baseado no texto estático do programa Para se associar uma referência a uma variável deve-se encontrar sua declaração. Processo de pesquisa: pesquise declarações, primeiro localmente, em seguida em ordem crescente do escopo, até que uma declaração seja encontrada para um dado nome. Escopos aninhados forma uma cadeia de escopos com sucessores e antecessores.
Constantes Def: Um constante é uma variável que está associada a um valor e a uma célula de memória. Vantagens: legibilidade e modificabilidade O vínculo de valor a nome de constante pode ser estático ( chamadas constantes explícitas) ou dinâmico Linguagens: Pascal: somente para literais Modula-2 e FORTRAN 90: constante-valoradas expressões Ada, C++, e Java: expressões de qualquer tipo
Inicialização de Variáveis O vínculo de uma variável a um valor no momento em em que é vinculada a um endereço é chamado de inicialização Inicialização é freqüentemente feita no comando de declaração, por exemplo, em Ada SUM : FLOAT := 0.0; Pascal não oferece meios de se inicializar variáveis exceto em run-time por instruções de atribuição