sexta-feira, 27 de março de 2009

Dados Primitivos


Tipo de dados

(Por: Daniel Max Costa e Wagner Geniplo Parreiras)


Sao tipos de variáveis ou dados, é uma combinação de valores e de operações que uma variável pode executar, o que pode variar conforme o sistema operacional e a linguagem de computador. São utilizados para indicar ao compilador ou interpretador as conversões necessárias para obter os valores em memória durante a construção do programa. Por outro lado, ajudam também o programador a detectar eventuais erros (maioritariamente sintáticos).

Dependendo da
linguagem de programação, o tipo de um dado é verificado diferentemente, de acordo com a análise léxica, sintática e semântica do compilador ou interpretador da linguagem. Os tipos têm geralmente associações com valores na memória ou com objetos (para uma linguagem orientada a objeto) ou variáveis.
Tipos de dados não-definidos em termos de outros tipos são chamados tipos de dados primitivos. Praticamente todas as linguagens de programação oferecem um conjunto de tipos de dados primitivos.Alguns tipos de dados primitivos são reflexos do hardware


Por exemplo: os inteiros

Outros exigem um pequeno suporte de software para sua implementação.
Os tipos de dados primitivos são aqueles que não podem ser decompostos, por exemplo: inteiro, real, lógico e caracter.

Dados Primitivos

Dados primitivos típicos incluem caracter, inteiro (representa um sub-conjunto dos números inteiros, com largura dependente do sistema; pode possuir sinal ou não), ponto flutuante (representa o conjunto dos números reais), booleano (lógica booleana, verdadeiro ou falso) e algum tipo de referência (como ponteiro ou handles).
Dados primitivos mais sofisticados incluem
tuplas, listas ligadas, números complexos, números racionais e tabela hash, presente sobretudo em linguagens funcionais.
Espera-se que operações envolvendo tipos primitivos sejam as construções mais rápidas da linguagem.

Por exemplo, a adição de inteiros pode ser feita com somente uma instrução de máquina, e mesmo algumas CPUs oferecem instruções específicas para processar sequências de caracteres com uma única instrução. A maioria das linguagens não permite que o comportamento de um tipo nativo seja modificado por programas. como exceção, Smalltalk permite que tipos nativos sejam estendidos, adicionando-se operações e também redefinindo operações nativas.


A cada variável está associado um Tipo de Dados. O tipo de dados define quais os valores que a variável pode conter. Se, por exemplo, dissermos que uma variável é do tipo Inteiro, não poderemos colocar um valor Real ou um Caracter.
Antes de começarmos a utilizar uma variável, temos que declarar qual o seu tipo. Tal é feito do seguinte modo:

: Tipo;

Neste caso estamos a declarar que a variável var, é do tipo Tipo. Se tivermos várias variáveis do mesmo tipo podemos junta-las numa só declaração:

, var2, var3: Tipo;

Ao declararmos o tipo de dados de uma variável, estamos a definir, não só, o tipo de valores que esta pode conter, mas também quais as operações que com elas podemos realizar.
Para já vamos considerar os seguintes tipos de dados: Flutuande, STR, Inteiro, Real, Caractere, Lógico

Tipos de dados primitivos:


Ponto Flutuante - Modelam os números reais, mas são aproximações
Linguagens para fins científicos suportam pelo menos dois tipos ponto flutuante (float e double)

Tipo de dados STR - tipo de dados STR corresponde a seqüências de caracteres e é utilizado para representar texto. Os valores do tipo STR são representados entre apóstrofes ou entre aspas:

Exemplo: 'Ola Mundo!' / ``Ola! Como passa?''


Inteiro - Qualquer valor pertencente ao conjunto dos números inteiros Z: { ..., -2, -1, 0,1, 2, ...


Exemplo: Ele tem 15 irmãos. / A temperatura desta noite será de -2 graus centígrados.

Real - Qualquer valor pertencente ao conjunto dos números reais R: { ..., -2, ..., -1.9, ..., 0, ..., 1.4,..., 12.5, ...}


Exemplo: Ela tem 1.73 metro de altura. / Meu saldo bancário é de R$ 121,07.

Caractere - Qualquer conjunto de caracteres (A, B, ..., !, &, ' , +, \, ?, ^,... ) entre aspas.:


Exemplo: Constava na prova: "Use somente caneta!". / O parque municipal estava repleto de placas: "Não pise na grama".


Lógico - Representa dois estados possíveis: V (Verdadeiro) ou F (Falso).


Exemplos: A porta pode estar aberta ou fechada. / A lâmpada pode estar acesa ou apagada.

Aplicação EmLinguagens:


C e C++
- Não primitivo
Usam vetores char e uma biblioteca de funções que oferecem operações (string.h)

SNOBOL4 - (uma linguagem de manipulação de cadeias)
Primitivo
Muitas operações, incluindo pattern matching elaborados

Java - Primitivo através da classe String

Pascal - A linguagem Pascal oferece 6 tipos de dados abstratos[1][1] primitivos. Para cada um destes tipos, há um limite de valores que eles podem assumir e um conjunto de operadores que os manipulam ou permitem que eles sejam convertidos ou utilizados em conjunto com os outros tipos.

Os tipos que a linguagem oferece são: INTEGER, BYTE, REAL, BOOLEAN, CHAR e STRING. Destes, os tipos Byte e String não constavam na definição inicial do Pascal e podem não ser encontrados em alguns compiladores.

Integer: armazenam números inteiros (naturais) cujos valores estejam entre –(MAXINT+1) e MAXINT. MAXINT é uma constante predefinida que pode variar de sistema para sistema ou de compilador para compilador. No ambiente Turbo Pascal, por exemplo, MAXINT vale 32768. Isto significa que os números neste compilador variam entre -32767 a 32768 e ocupam dois bytes (16 bits, que geram 65536 combinações possíveis de armazenamento);

Byte: subconjunto do tipo Integer que armazena valores entre 0 e 255. Este tipo ocupa 1 byte (8 bits, que geram 256 combinações);

Real: esse tipo armazena números reais positivos e negativos (incluindo frações). No turbo Pascal este tipo ocupa 6 bytes de memória (48 bits), mas o número não é armazenado como nos outros tipos. Neste caso, o número é armazenado num formato conhecido por formato científico, onde ele é dividido em duas partes: a mantissa e o expoente. A mantissa contém números significativos do numero real automaticamente normalizadas pelo computador para um valor fracionário na faixa entre 1 e 10. O expoente é um valor inteiro positivo ou negativo que indica a posição da vírgula no número. Para calcular o número armazenado, deve-se aplicar a seguinte fórmula (isso é feito automaticamente pelo computador):
número = mantissa * 10expoente

Veja o exemplo de alguns números:

10.45 = 0.1045*10^+2 ou 1.0450000000E+01
0.00056993 = 0.56993*10^-3 ou 5.6993000000E-04

Esse tipo de representação pode ocasionar alguns erros ou perdas, principalmente quando se está calculando números com muitas casas decimais
ou muito grandes, pois se o número de casas de uma soma, por exemplo, ultrapassar o número máximo de casas que o computador usa para armazenar o número, ele será arredondado.

Char: abreviação da palavra inglesa “character”, que significa caractere. Como o próprio nome indica, serve para armazenar um único caractere (uma letra, dígito ou símbolo). Ocupa 1 byte de memória, o que significa que pode armazenar 256 combinações de bits. Esse é exatamente o tamanho da tabela Americana de Códigos Padrão para a Troca de Informações (ASCII), que contém uma lista de 256 caracteres que variam entre caracteres de controle, letras, dígitos e símbolos. Cada um deles possui um código (um número) fixo. Através do número é possível se descobrir o caractere correspondente na tabela e vice-versa.

String: Armazena cadeias de caracteres. No fundo é o mesmo que um vetor de Chars, cujo tamanho máximo é o de 255 caracteres. Possui um byte (na posição 0) que indica quantas letras a string possui armazenada dentro dela;

Boolean: armazena valores lógicos que variam entre “true” (verdadeiro) ou “false” (falso). São muito utilizadas em variáveis do tipo flag e em testes condicionais.

Um tipo é dito abstrato quando sua faixa de valores e seus operadores (ou relações) com os outros tipos (ao menos primitivos) são definidos. Desta forma, podemos através do tipo pensar em todas as suas propriedades sem que ele (o objeto) realmente exista. Podemos assim, usá-los ou raciocinar sobre eles sem precisarmos saber como eles e suas operações são realmente implementados no computador.

Opções de Tamanho:

Estático
COBOL, FORTRAN 90, Pascal, Ada e Java classe String

Tamanho dinâmico limitado: C e C++
Um caractere especial é usado para indicar o final da cadeia, em vez de manter seu tamanho

Dinâmico (sem máximo): SNOBOL4, Perl, JavaScript

Implementação de Cadeias de Caracteres:
Tamanho estático
descritor em tempo de compilação
Tamanho dinâmico limitado
Podem exigir um descritor em tempo de execução para armazenar tanto o tamanho máximo como o tamanho atual (mas não em C e C++)
Tamanho dinâmico
Exigem um descritor em tempo de execução
Exigem um gerenciamento de armazenagem mais complexo
Alocação e desalocação




Outros Tipo Especiais:

Vetores (array com uma dimensão)

Matrizes (array com n dimensões)

Ponteiros

Referências

Registros (estruturas)


Referências Bibliográficas:


http://www.di.uminho.pt/~jfc/ensino/SebentaLEP/node12.html
http://www.leandro.wives.nom.br/pascal/Apostila/Tipos%20primitivos.htm
http://200.136.79.4/informatica/alunos/tutoriais/Algoritmos_Programacao/tipos_primitivos.html
http://www.pawfal.org/fluxus/docs/0.15/pt/dados-primitivos.html
http://www.macoratti.net/net_tpdt.htmhttp://si.uniminas.br/~mauro/SIS05/Tipos-JAVA.pdf

quinta-feira, 26 de março de 2009

==================================

ESCOPO E TEMPO DE VIDA

(Postado por: José Mário Ramos e Jussara Pereira dos Santos)

VARIÁVEL

Uma Variável é uma abstração de uma ou mais células de memória de um computador. Uma variável pode ser caracterizada através dos seguintes seis atributos:
Nome;
Endereço;
Valor;
Tipo;
Tempo de vida;
Escopo.

VINCULAÇÃO DE ARMAZENAMENTO E TEMPO DE VIDA
Alocação: marcar\tomar uma célula de memória de um conjunto de memória disponível.
Desalocação: devolver a célula ao conjunto de memória disponível.
O tempo de vida de uma variável se inicia quando ela é vinculada a uma célula específica e encerra-se quando ela é desvinculada

Categorias de variáveis baseado no tempo de vida

Variáveis Estáticas
Variáveis Dinâmicas na Pilha
Variáveis Dinâmicas no Monte Explícitas
Variáveis Dinâmicas no Monte Implícitas
Pilha (Stack) e Monte (Heap)

VARIÁVEIS ESTÁTICAS

Vinculadas a células de memória antes que a execução do programa se inicie. Exemplo: todas as variáveis do FORTRAN 77 e as variáveis static do C
Vantagens: eficiência (endereçamento direto), suporta subprogramas sensíveis à história
Desvantagem: pouca flexibilidade (não permitem recursão)

VARIÁVEIS DINÂMICAS NA PILHA

São aquelas cujas vinculações de armazenamento criam-se a partir da elaboração de suas instruções de declaração, mas cujos tipos são estaticamente vinculados (Elaboração: processo de alocação e de vinculação de armazenamento). Ocorre em tempo de execução
Exemplo: Variáveis locais em subprogramas C e métodos em Java
Vantagem: permite recursão, compartilhamento de espaço de memória
Desvantagens: sobretaxa de alocação e de desalocação em tempo de execução, subprogramas não podem ser sensíveis à história

VARIÁVEIS DINÂMICAS NO MONTE EXPLÍCITAS

As variáveis dinâmicas no monte explícitas são células de memória sem nome (abstratas) alocadas e desalocadas por instruções explícitas em tempo de
execução, especificadas pelo programador

Essas variáveis alocadas no monte e desalocadas para o monte só podem ser referenciadas por meio de variáveis de ponteiro ou de referência

O monte é um conjunto de células de armazenamento desorganizado, devido à imprevisibilidade de seu uso
Exemplo: Objetos dinâmicos em C++ (via new e delete). Todos os objetos em Java
Vantagem: convenientes para estruturas dinâmicas: listas encadeadas e árvores.
Desvantagens: dificuldade de usar ponteiros e referência corretamente, custo das referências para as alocações e para as desalocações

VARIÁVEIS DINÂMICAS NO MONTE IMPLICITAS

Alocação e desalocação causadas por instruções de atribuição
– Todas as variáveis em APL
– Todas as strings e vetores em Perl e em JavaScript
Vantagem: flexibilidade
Desvantagens: ineficiente, pois todos os atributos são dinâmicos, perda de grande parte da capacidade de detectar erros

ESCOPO

O escopo (scope) de uma variável representa a área do programa onde esta é visível. Uma variável é visível em uma instrução se puder ser referenciada nessa instrução. Ex. em C e C++(escopo definido por bloco):

void foo (int x) { // início do escopo de x
int y; // início do escopo de y
x = y = 0;
} // fim do escopo of x and y

As variáveis não-locais de uma unidade ou de um bloco de programa são as visíveis dentro deste, mas não são declaradas lá.
As regras de escopo de uma L.P. determinam a forma como as referências e nomes estão associadas a variáveis.

Algumas regras de escopo:

1. Procurar variáveis localmente
2. Procurar em ordem crescente do escopo até encontrar uma declaração para o nome da variável.
Escopos aninhados: definição de escopos dentro de outros, formando uma seqüência de escopos com sucessores e intercessores.
A redefinição de uma variável com o mesmo nome (num escopo interior) de uma já existente num escopo exterior, permite "esconder" a definição exterior.
C++, Pascal, ADA, etc. permitem o acesso a estas variáveis escondidas em escopos exteriores.

O escopo de uma variável pode ser: Estático e Dinâmico.

ESCOPO ESTÁTICO

Método para vincular nomes a variáveis não-locais
Para conectar uma referência a uma variável, o compilador precisa encontrar a declaração.
Processo de busca:
– Caso a declaração não for encontrada localmente, passa-se a buscar em escopos mais amplos.
O pai-estático (static parent) é o subprograma no qual encontra-se a declaração
Os ancestrais estáticos são todos os subprogramas até se chegar à declaração
Variáveis podem ser escondidas de uma unidade quando a mesma possui uma variável com o mesmo nome.


C++ e Ada permitem acesso a essas variáveis escondidas
Em Ada: unit.name
Em C++: class_name:name

BLOCOS
Um método para criar novos escopos estáticos no meio do código executável introduzido no ALGOL 60
Permite que uma seção de código tenha suas próprias variáveis locais cujo escopo é minimizado
Essas variáveis são tipicamente dinâmicas na pilha– Alocada quando a seção é iniciada e desalocada quando ela é finalizada
Exemplo em Ada
...
declare TEMP: integer;
begin
TEMP := First
First := Second
Second := TEMP
end
...


AVALIAÇÃO DO ESCOPO ESTÁTICO


Suponha que a especificação é alterada e E deve acessar alguma variável em “D”.
Soluções:
– Colocar E em D (porém, E não poderá acessar o escopo de B).
– Mover as variáveis de D, que são necessárias em E, para MAIN (isso permite o acesso por todos os procedimentos.
De maneira geral: escopo estático encoraja o uso de variáveis globais.

ESCOPO DINÂMICO

Baseia-se na seqüência de chamada de subprogramas, não em suas relações espaciais (temporal versus espacial).
Desta forma o escopo pode ser determinado apenas em tempo de execução
Quando a procura por declarações locais falha, as declarações do pai-dinâmico (procedimento de chamada) são pesquisadas, e assim sucessivamente.
Caso nenhuma declaração seja encontrada em qualquer ancestral dinâmico, haverá um erro em tempo de execução.
Escopo dinâmico:


Exemplo :


BIG chama SUB2
SUB2 chama SUB1
SUB1 usa x
Nesse caso, SUB1 usa o x declarado em SUB2 procedure big;


var x: integer;
procedure sub1;
begin { sub1 }
...x...
end; { sub1 }
procedure sub2;
var x: integer;
begin { sub2 }
...
end;
begin { big }
...
end; { big }

AVALIAÇÃO DO ESCOPO DINÂMICO

Vantagem: conveniência
Desvantagem: pouca legibilidade
Linguagens que usam escopo dinâmico
– APL, SNOBOL4 e nas primeiras versões do LISP
– Perl também permite que as variáveis sejam declaradas com escopo dinâmico

ESCOPO E TEMPO DE VIDA

Escopo e Tempo de Vida, algumas vezes, parecem estar relacionados, mas são conceitos diferentes
void printheader(){
...
} /* fim de printheader */
void compute() {
int sum;
...
printheader();
} /* fim de compute */

O escopo da variável sum é completamente contido pela função compute Porém, o tempo de vida de sum estende-se ao longo do tempo durante o qual printheader é executado

AMBIENTES DE REFERENCIAMENTO

O ambiente de referenciamento de uma instrução é o conjunto de todos os nome visíveis na instrução.
Em uma linguagem com escopo – O ambiente de referenciamento é formado pelas variáveis locais mais todas as variáveis de seus escopos ancestrais visíveis
Um subprograma é ativo se sua execução tiver começado, mas ainda não tiver terminado.
Em um linguagem com escopo dinâmico – O ambiente de referenciamento é formado pelas variáveis locais, mais as variáveis de todos os subprogramas ativos.





REFERÊNCIAS BIBLIOGRÁFICAS

http://www-di.inf.puc-rio.br/~rangel/ - Instituto de Informática da pontifícia universidade católica do rio de janeiro
Centro de Informática da Universidade Federal de Pernambuco - http://www.cin.ufpe.br/
Estig - Escola Superior de Tecnologia e Gestão de Informática.
www.dca.fee.unicamp.br/~leandro/paradigmaslp/aulas/aula7plp1sem2008.pdf
http://www.netbeans.org/kb/docs/web/scopes_pt_br.html

segunda-feira, 23 de março de 2009

Vinculação

O conceito de vinculação : Vinculação em geral é uma associação , como exemplo entre um atributo e uma entrada ou entre uma operação e um símbolo . O momento em que uma vinculação desenvolve-se é chamado tempo de vinculação. As vinculações podem ocorrer no tempo de projeto da linguagem , no tempo de implementação , no tempo de compilação , no tempo de ligação , no tempo de carregamento ou no tempo de execução. Um exemplo de vinculação no tempo de projeto da linguagem seria o símbolo “*” normalmente vinculado a operaçãode multiplicação . Um tipo de dados como INTEGER no FORTRAN, é vinculado a uma variedade de valores possíveis no tempo de implementação da linguagem. No tempo de compilação uma variável em um programa C ou PASCAL e vinculada a um tipo particular de dados . Uma chamada a um subprograma de biblioteca é vinculada a código do subprograma no tempo de ligação . Uma variável pode ser vinculada a uma célula de armazenamento quando o programa é carregado para a memoria .
Essa mesma vinculação não acontece até o tempo de execução , em alguns casos , como acontece com variáveis em subprogramas PASCAL e em funções C (se a definição incluir o qualificador static). Considere a seguinte instrução de atribuição C, cuja a variável count foi definida da seguinte maneira:

int count ;
...
count = count + 5;

Algumas das vinculações e seus tempos de vinculação para as partes dessa instrução de atribuição são as seguintes :

Conjunto dos tipos possíveis para count: Vinculado no tempo de projeto
Tipo de count : Vinculado no tempo de compilação
Conjunto dos valores possíveis de count : Vinculado no tempo de projeto do compilador
Valor de count : vinculado no tempo de execução com essa instrução
Conjunto dos significados possíveis para o simbolo do operador + : vinculado no tempo de definição da linguagem
Significado do símbolo do operador + nessa instrução : vinculador no tempo de compilação
Representação interna do literal 5 : vinculada no tempo de projeto do compilador

Vinculação de Atributos a Variáveis

Uma vinculação é estatica se ocorrer antes do tempo de execução e permanecer inalterado ao longo da execução do programa .Se ela ocorrer durante a execução ou puder ser modificada no decorrer da execução de um programa , será chamada dinâmica.

A vinculação estática de Tipos

Antes de uma variável poder ser referenciada em um programa, ela deve ser vinculada a um tipo de dados. Os dois aspectos desta vinculação do tipo são: (1) a maneira como o tipo é especificado e (2) quando a vinculação ocorre. Os tipos podem ser especificados estaticamente por meio de uma declaração explícita ou implícita. Uma declaração explícita é uma instrução em um programa que lista nomes de variáveis e especifica que elas são de um tipo particular. Uma declaração implícita é uma forma de associar variáveis a tipos por convenções padrão ao invés de por instruções de
declaração. Neste último caso a ocorrência de um nome de variável em um programa
constitui a sua declaração implícita.
A maioria das linguagens de programação projetada desde o começo da década de 60
exige uma declaração explícita de todas as variáveis - exceções a esta regra são as
linguagens Perl e ML. Outras linguagens projetadas antes da década de 60, como o
FORTRAN, a PL/I e o BASIC, têm declarações implícitas. As declarações implícitas em algumas circunstâncias podem ser uma conveniência
para os programadores, entretanto trazem em geral um grande prejuízo para a legibilidade.
Isso porque elas impedem que o processo de compilação detecte diversos erros tipográficos
e do programador. Por exemplo, se alguma variável for deixada sem a declaração de tipo,
ela será vinculada a um tipo-padrão e a atributos iniciais, o que pode vir a causar erros sutis
de difícil detecção e diagnóstico.
Uma solução interessante para o uso de declarações implícitas é apresentada pela
linguagem Perl, em que qualquer nome que se inicie com o caractere $ é um escalar, que
pode armazenar uma cadeia de caracteres ou um valor numérico. Se o nome se iniciar com
@, ele será uma matriz e se se iniciar com %, será uma estrutura hash (técnica de pesquisa
em tabela). Neste caso, o leitor/programador sempre conhece o tipo de uma variável ao ler o
nome dela.

A vinculação dinâmica de Tipos
Em uma vinculação dinâmica de tipos, o tipo não é especificado por uma instrução
de declaração. Ao invés disso, a variável é vinculada ao tipo no momento em que lhe é
atribuída um valor em uma instrução de atribuição.
Quando a instrução de atribuição é executada, a variável que está sendo atribuída é
vinculada ao tipo do valor, da variável ou da expressão que está no lado direito da
atribuição.
As linguagens que possuem vinculações dinâmicas de tipos são muito diferentes das
que não possuem. A principal vantagem da vinculação dinâmica de variáveis a tipos é que
ela proporciona muita flexibilidade de programação.
Por exemplo, no caso de se estar programando uma rotina, função ou método para o
processamento de uma lista de dados em uma linguagem com vinculação dinâmica de tipos,
esta rotina ou método pode ser escrita(o) de forma genérica, permitindo que a(o) mesma(o)
seja capaz de lidar com dados de qualquer tipo.
As linguagens fortemente tipadas, como o C++ e o Java, não permitem esse tipo de
vinculação dinâmica de tipos.
Um exemplo de linguagem que permite a vinculação dinâmica de tipos é o
JavaScript. Nesta linguagem, pode-se escrever:
[1] LISTA = [10.2 5.1 0.0]
[...] ...
[n] LISTA = 72
A atribuição da linha [1] faz com que a variável LISTA se torne uma matriz
uni dimensional de elementos reais e de tamanho 3. Já na linha [n] a variável LISTA se torna
uma variável escalar inteira. Existem duas desvantagens na vinculação dinâmica de tipos. A primeira delas é
que a capacidade de detecção de erros por parte do compilador é diminuída em relação ao
compilador de uma linguagem que tenha vinculação de tipos estática. Um exemplo disso é
que os tipos incorretos – se houverem - do lado direito de uma atribuição não são detectados
como erros, e sim o tipo presumidamente correto do lado esquerdo é convertido
automaticamente para o tipo incorreto.
A segunda desvantagem da vinculação dinâmica de tipos é que o custo para a
implementação da vinculação dinâmica de quaisquer atributos é elevado, principalmente
durante a execução. A verificação de tipos é feita durante o tempo de execução. A memória
usada para o valor de uma variável deve ter um tamanho variável pois diferentes valores de
tipos exigem quantidades diferentes de armazenamento.
Além disso, a maioria das linguagens que implementam a vinculação dinâmica de
tipos é usualmente interpretada, em virtude da dificuldade de se mudar dinamicamente os
tipos de variáveis em código de máquina. Neste caso, o tempo para fazer a vinculação
dinâmica é oculto pelo tempo global de interpretação.



Referências Bibliográficas
[1] MELO, Ana Cristina Vieira de; SILVA, Flávio Soares Corrêa da. Princípios de Linguagens
de Programação, Editora Edgard Blücher Ltda. 1a Edição – 2003.
[2] SEBESTA, Robert. Conceitos de Linguagens de Programação. Editora Bookman. 5a
Edição. 2003.


sexta-feira, 20 de março de 2009

Sintaxe e Semântica de Linguagens de Programação

(Postado por: Miriãn Pereira, Priscilla Camila e Suzy Santiago)

Linguagem é um conjunto de regras sintáticas e semânticas usadas para definir uma forma de comunicação. Na computação, também surgiu a necessidade de se criar um método padronizado para expressar instruções sobre as quais um computador vai atuar, como elas serão armazenadas ou transmitidas e quais ações devem ser tomadas sob várias circunstâncias.
Como toda linguagem é utilizada por várias pessoas, ela deve ser inteligível por todos. Desta forma, uma linguagem de programação também tem estudo semelhante às demais, ou seja, deve-se analisar sua sintaxe e semântica.
Uma definição informal é escrita usando-se alguma linguagem natural. Quando bem escrita ela pode ser prontamente entendida pelo implementador da linguagem, no entanto, devido à natureza ambígua da linguagem natural, a definição pode ser imprecisa e incompleta. Assim, é bem possível que dois implementadores interpretem distintamente certas características da linguagem, o que levará a implementações inconsistentes para a mesma linguagem.
Numa definição formal, uma linguagem de programação é definida precisamente (matematicamente); para isto faz-se uso de alguma linguagem ou método formal (isto é, com base matemática) para escrever a definição da linguagem. A especificação formal de linguagens de programação evita ambigüidades e falhas de entendimento por parte dos projetistas, implementadores e usuários da linguagem.
Sintaxe é a forma como as instruções de uma linguagem são escritas, mas sem atender ao seu significado. Enquanto no C++ os blocos de comando que serão executados são limitados por “{ }”, em Pascal são limitados por “begin” e “end”. Veja a seguir:


Pascal
var nome: string; (*variável do tipo string*)
begin
clrscr;
writeln('Digite o primeiro nome:');
readln(nome);
writeln(nome);
readkey;
end

C++
char nome[40] //variável do tipo string com no máximo 40 caracteres
int main(){
clrscr;
cout<<"Digite o primeiro nome: ";
cin>>nome;
cout<<"Nome: ";
return 0; }

Na sintaxe de uma linguagem de programação existem descrições formais, que são chamadas de lexemas. Os lexemas de uma LP incluem seus identificadores, palavras reservadas, literais e operadores. Um símbolo de uma linguagem é uma categoria de lexemas e é denominado token.

Exemplo de uma instrução em linguagem C:
- index = 2 * cont + 17;



Para descrever a sintaxe das linguagens de programação são criadas gramáticas. Entre as gramáticas livres de contexto, a mais utilizada é a BNF– Forma de Backus- Naur, que foi definida por Peter Naur e melhorada por John Backus, dai o seu nome: Backus-Naur Form (BNF). É uma metalinguagem (linguagem utilizada para explicar outra linguagem), composta por um conjunto de regras para descrever como os processos computacionais são realizados.

A BNF usa abstrações para representar estruturas sintáticas.

Exemplo:
- soma = valor1 + valor2

A instrução à esquerda da seta é a abstração que está sendo definido. Esta instrução é chamada de “não-terminal” (precisa ser definida). O texto à direita da seta é a definição do que está do lado esquerdo. Esta instrução é chamada de “terminal” (não precisa ser definida, que são os lexemas e os tokens). A definição é chamada de regra.

A BNF é suficientemente poderosa para descrever a grande maioria das sintaxes das linguagens de programação.
As sentenças da linguagem são geradas por uma seqüência de aplicações das regras. Uma geração da sentença é chamada derivação (esclarecer, explicar o programa).


As gramáticas descrevem naturalmente a estrutura sintática hierárquica das linguagens que definem. Essas estruturas são chamadas de árvores de análise.

Exemplo:
- A = B * (A + C)

onde:
- O nível mais baixo será executado primeiro
- As folhas são elementos terminais
- Os nós (vértices) são elementos não-terminais
- Toda subárvore de uma árvore de análise descreve uma instância de uma abstração na instrução.


Para solucionar alguns problemas encontrados na BNF, o standard ISO 14977 definiu uma extensão ao BNF designado EBNF e no qual existem quatro novos operadores:
-( ... ...... ) escolha múltipla
- [ ] símbolos opcionais (zero ou uma vez)
-{ } símbolos opcionais com repetição (zero ou mais vezes)
-{ }+ símbolos com repetição (uma ou mais vezes)

O EBNF não é mais poderoso que o BNF;apenas é mais facilitador, mais prático.
Exemplo:


As informações das gramáticas BNF e EBNF podem ser representadas através de gráficos de sintaxe. Esses gráficos possuem diferentes tipos de vertices para representar os simbolos terminas e não-terminais. Os vértices retangulares contém os nomes das unidades sintáticas (não-teminais) e círculos ou elipses contém símbolos terminais. Com a análise dos graficos aumenta a legibilidade, pois permite visualizar em duas dimessões.

Exemplo:
=> if then {}[else ] end if
=> else_if then

Representação Gráfica: A Gramática de atributos, projetada por Knuth, é um dispositivo usado para descrever mais detalhes da estrutura das linguagens de programação do que é possível com uma gramática livre de contexto (BNF).Ela permite que certas regras de linguagem sejam descritas, com a incompatibilidade de tipos. Consegue descrever tanto a sintaxe como a semântica estática do programa.


A Semântica é complementar a sintaxe. Ela corresponde à descrição do significado das instruções válidas de uma linguagem. Por exemplo, a sintaxe da instrução if da linguagem C++ é: if () {} e sua semântica é: “se o valor da expressão for verdadeiro, as instruções incorporadas serão executadas pelo programa”. É através da semântica que conseguimos utilizar melhor e validar uma linguagem.

Semântica estática se relaciona indiretamente com o significado do programa durante a execução, ele se refere as formar legais do programa. Geralmente as regras de semântica estática declarama as restriçoes de tipos. Ela é assim chamado por que a análise necessária para verificar essas especificações durante a compilação.

Semântica dinâmica representa o significado das expressões, das instruções e das unidades de programas. É dividida em Operacional, Denotacional e Axiomática.

Semântica operacional: descreve o significado de um programa através da execução de suas instruções numa máquina (real ou simulada, virtual). Alterações no estado da máquina ao executar determinada instrução definem o significado desta, ou seja, preocupa-se com o resultado, em como será executado pelo computador.

Exemplo:

Semântica denotacional dentre os métodos mais empregados, é o de maior rigor matemático. Baseia-se solidamente na teoria das funções recursivas, ou seja, explica o significado das instruções das linguagens, através de instruções matemáticas.

Semântica axiomática baseia-se na lógica matemática associada a um método para provar, ou seja, preocupa-se em provar a exatidão de um programa. As expressões lógicas são chamadas de predicado. Eles precedem uma instrução, seguido da instrução temos a pós-condição.

Exemplo:



Referências Bibliográficas:

Sebesta, Robert W.; trad. Santos, José Carlos Barbosa dos. Conceitos de Linguagens de Programação – 4ª Edição – Porto Alegre: Bookman, 2000
http://pt.wikipedia.org/wiki/Gramáticas_livres_de_contexto
http://www.dcc.ufrj.br/~fabiop/CAP3.ppt#17
http://www.cefetrn.br/~robinson/LPCPCap3.pdf
http://www.din.uem.br/~vfeltrim/linguagens2008/Sintaxe.pdf

Verificação de Tipos

(Por: Felipe Roberto Silva Ferreira e Thiago da Silva Resende)

Análise semântica é uma fase da compilação. Onde se verifica os erros semânticos existentes no código fonte e se obtém informações necessárias para a próxima fase da compilação. Uma das etapas da Análise semântica é a Verificação de Tipos, onde se assegura que operandos de um operador sejam de tipos compatíveis. Um tipo compatível é aquele que pode ser utilizado pelo operador, ou que tenha permissão, nas regras da linguagem, para ser transformado pelo compilador em um tipo válido (coerção).

Exemplo: Código em C++


1int restoDivisao(int a, float b)
2{
3 return a%b;
4}
Tentar compilar esse código geraria um ERRO DE TIPO, já que a aplicação do operador % não pode ter um operando real (float b).


Coerção

Em alguns casos, o compilador realiza a conversão automática de um tipo para outro que seja adequado à aplicação do operador.
Em C existe uma seqüência que determina como acontecerá a coerção:

1. Char e short são convertidos para int, float para double;
2. Se um dos operandos é double, o outro é convertido para double e o resultado é double;
3. Se um dos operandos é long, o outro é convertido para long e o resultado é long;
4. Se um dos operandos é unsig
ned, o outro é convertido para unsigned e o resultado é unsigned;

Exemplo: Código em C++


1int soma(int a,char b)
2{
3 //A variável b e convertida para int.
4 return a + b;
5}
6
7int main()
8{
9 int a = 1;
10 char b = '1';
11
12 //Imprime 50.
13 cout << soma(a,b);
14
15 getch();
16 return 0;
17}
Ao compilar o código acima a variável b do tipo char e convertida automaticamente para int e realizando a soma.

Em outros casos a conversão pode ser feita pelo próprio programador, para isso ele deve utilizar um operador molde entre parênteses que indicará em qual tipo o resultado deve ser convertido.

Exemplo:


int total = 0;
double exp = (double) total;


A verificação de tipos pode ser Estática ou Dinâmica.

Estática
A verificação de tipo e executada em tempo de compilação, sendo assim muitos erros são tratados durante o processo de desenvolvimento e a execução do programa pode ser realizada com mais eficiência.

Exemplo de linguagens com verificação estática: Ada, C, C++, C#, Java, Fortran, ML, Pascal, Haskell e etc.

Dinâmica
A verificação de tipo é executada em tempo de execução, tornando uma linguagem mais flexível e perdendo parte da capacidade de dectar erros.

Exemplo de linguagens com verificação dinâmica: Groovy, Java Script, Lisp, Objective-C, Perl, PHP, Prolog, Python,Ruby, Smalltalk,scheme,visual basic, clojure,apl,snobol4 e etc.


Tipificação forte


“Uma linguagem é dita fortem
ente tipificada se erros de tipo sempre forem detectados”. Permite detectar toda utilização de variáveis que resultam em erros de tipo.



Bibliografia:

Conceitos de Linguagens de Programação, 4º Edição, Robert W. Sebesta
http://pt.wikipedia.org/wiki/Tipo_de_dado
http://en.wikipedia.org/wiki/Type_system#Type_checking
http://www.dca.fee.unicamp.br/cursos/EA876/apostila/HTML/node71.html
http://gersonc.anahy.org/repcomp/Compiladores11-Semantica.pdf
http://www.inf.ufrgs.br/procpar/disc/cmp135/trabs/martinotto/trabII/semantica.htm
http://www.gpec.ucdb.br/ricrs/Courses/CompilerII-2008/Lectures/Verificacao_Tipos.pdf
http://www.dsc.ufcg.edu.br/~patricia/plp2003.1/aulas/valores2.pdf
http://ctgalina.googlepages.com/aula_5_TIPOSDEDADOS.doc

quinta-feira, 5 de março de 2009

Critérios de Avaliação de linguagens de programação

Por (Rodney Ribeiro / Kamyla Mayara / Jaqueline Spindula)

São os critérios:

LEGIBILIDADE (Critério mais importante)

ü Através do exame de um programa escrito por outra pessoa, poder seguir a sua lógica e descobrir a presença de erros.

Fatores mais importantes:

- Simplicidade -> Permite conhecer a linguagem toda;

- Ortogonalidade -> Todas as combinações dos mecanismos primitivos são válidas;

- Comandos de Controle de Qualidade;

- Estrutura de Tipos de dados de Qualidade;

- Sintaxe Racional -> Com palavras reservadas, construções diferentes para mecanismos diferentes;


REDIGIBILIDADE(Capacidade de escrita)


ü Forma de expressar um problema de forma natural, sem desviar a atenção do programador para “truques ” da linguagem.

Fatores mais importantes:

- Simplicidade, Ortogonalidade, Suporte para abstração (ajuda a dominar a complexidade dos problemas), Expressividade (construções simples para operações freqüentes).


SEGURANÇA(Confiabilidade)

ü Escrever programas garante atingir o efeito desejado.

Fatores mais importantes:

- Sistema de Tipos Estáticos -> Detecta todas as incompatibilidades de tipo em tempo de compilação;

- Tratamento de Exceções -> Permite a tomada de medidas corretivas em situações inesperadas;

- Ausência de Sinonímia -> É perigoso uma mesma entidade ser conhecida por dois nomes diferentes;

EFICIÊNCIA

ü Atualmente a eficiência já não é mais medida apenas com base na velocidade execução dos programas e na economia do uso da memória, considera-se também o esforço necessário para produzir os programas e para os manter.

CUSTO

ü Para determinar o custo final de uma linguagem de programação, devemos levar em consideração alguns fatores:

- Treinamento -> Quanto maior a complexidade e quanto mais recursos contém a linguagem maior o grau de dificuldade de aprendizado;

- Programação -> Está ligado aos fatores de simplicidade: quanto menor o numero de componentes básicos, mais fácil a leitura e a programação do código fonte,

Exemplo: Comandos que tem a mesma finalidade:

cont:= cont +1

cont ++

- Compilação -> Existem duas situações de compilação de programas:

1- Programas auto executáveis (EXE, COM, etc)

2- Programas que dependem de um “rum time” para ser executado (FLX-Dataflex);

No primeiro caso a distribuição do aplicativo é mais simples, pois independe que o cliente tenha que adquirir ferramentas de linguagem para a execução, que encarece consideravelmente o custo final do projeto.

- Execução -> Otimização é a palavra chave. Trata-se de preocupação em desenvolver um código otimizado, com execução mais rápida.

- Teste -> Testes realizados na linguagem visam confiança. Em sistemas Críticos se houverem falhas, o custo pode tornar-se elevado.

- Manutenção -> Visa corrigir ou modificar para adicionar/remover recursos. O custo da manutenção mede-se principalmente pelas suas características de legibilidade, quanto mais fácil é escrever o programa, mais fácil torna-se a manutenção. Os fatores mais consideráveis em termos de custos de linguagem são o desenvolvimento do programa, manutenção e confiabilidade, sendo as duas últimas mais onerosas.

- Evolução -> É complicado prever a evolução da linguagem, mesmo da aplicação desenvolvida pela linguagem, quando notamos a grande variedade de dispositivos e tecnologias que surgem freqüentemente, as quais podemos implementar em nossas aplicações. Um fator muito importante diz respeito justamente à capacidade de implementações: a portabilidade. Deve ser avaliado o que determina a faixa de utilização da linguagem e encontrar boa definição na documentação sobre a que se propõe a linguagem.



Custo/ Benef ício (“Trade- of fs”) no projeto de LPs

Confiabilidade vs Custo de execução

Ada, ao contrário de C, verifica todos os índices de arranjos para garantir que estão dentro do intervalo. A verificação da validade do índice de um array aumenta a confiabilidade através de mais instruções, mas isso significa custo de execução.

Simplicidade de escrita vs Legibilidade

Uma linguagem compacta é mais simples de escrever, contudo a sua leitura pode ser muito ruim.Comando em APL que gera uma lista de primos entre 1 e N, inclusive:

(2 = 0 +.= T o.| T) / T <- iN

Flexibilidade vs Segurança

C, ao contrário de Java, permite operações aritméticas sobre apontadores.Porém, em linguagem C, o tipo void pode conter qualquer tipo de dados, o que pode ser perigoso.


Bibliografia

SEGALA,Paulo César et al.Critérios de Avaliação de Linguagem – Custo.Disponível em: <www.mguacu.com.br/paradg_custo.doc>. Acesso em: 27 fev. 2009.

PINTO,Bruno Queiro.Conceitos Iniciais e Características de uma Linguagem de Programação.Disponível em: <www.inf.fesurv.br/~bruno/disciplinaPP/arquivos/aulas/aula02.ppt>.Acesso em:27 fev. 2009.

DIAS,Artur Miguel.O Essencial Sobre Linguagem de Programação.Disponível em: <ssdi.di.fct.unl.pt/seminf/docs/aula06/2006-SEMINARIO_LP.ppt>.Acesso em: 27 fev. 2009.

BRILHANTE,Virgínia et al.Linguagens de Programação.Disponível em: .Acesso em: 27 fev. 2009.


quarta-feira, 4 de março de 2009

História das linguagens de programação

(Por: Luiz Carlos Braga e André Luis Figueiredo)

Antes de conhecer toda a história das linguagens de programação é preciso ter uma definição sucinta do que a mesma significa. Linguagem de programação é um método padronizado de expressar uma instrução ao computador, um conjunto de regras sintáticas e semânticas, que sejam reconhecíveis, por meio de softwares de compilação, interpretação ou mesmo linguagem máquina, conhecida como binária.
A primeira linguagem criada foi a PLANKALKÜL, desenvolvida por Konrad Zuse, na Alemanha, esta não teve nenhum impacto nas gerações futuras no desenvolvimento de outras linguagens. ASSEMBLY também criado na década de 50 foi um marco importante que trouxe consigo a facilidade do uso de variáveis, mas foi considerada uma linguagem de baixo nível.
A primeira linguagem de alto nível foi o FORTRAN, cujo significado vem da expressão FORmula TRANslation, criada em 1954, possuía uma facilidade que era uma gama de instruções prontas. Em 1957 foi criada [B-0], que daria origem a Flow-Matic (1958), antecessor imediato de COBOL, de 1959. Lisp e ALGOL foram criadas em 1958. COBOL, “Common Business Oriented Language”, como já dito, foi criada no fim da década de 50 com o objetivo de ser plataforma de desenvolvimento para aplicações bancárias e financeiras em geral.
A Lisp, como dito anteriormente, nasceu em 1958 como uma ferramenta matemática independente de qualquer computador e só posteriormente se procedeu à sua adaptação a uma máquina e é uma linguagem dinâmica, cujos programas são constituídos por pequenos módulos, de funcionalidade genérica e que cumprem um objetivo muito simples. Já o Algol também desenvolvido em 1958 e possui várias versões, como por exemplo o Algol 58, Algol 60 e por fim o Algol 68. O Algol 58 foi a primeira proposta da linguagem, seguida pela Algol 60 que é a versão mais conhecida por todos, já o Algol 68 é a versão mais poderosa, porém menos conhecida, onde é uma linguagem imperativa e projetada a meta de um escopo muito maior de aplicações e uma sintaxe e semântica mais rigorosamente definida.
O Pascal é outra linguagem de alto nível, criada durante a década de 60. É uma linguagem bastante estruturada, com regras bastante rígidas, o que torna difícil de usar. Apesar de ter sido muito usada na década de 80, nos dias de hoje ela é usada apenas em alguns cursos como ferramenta de aprendizado.
A Ada é uma linguagem de programação estruturada, de tipagem estática, imperativa, orientada a objetos e é uma linguagem de alto nível, da qual foi origina do Pascal e outras linguagens. Foi originalmente produzida por uma equipe liderada por Jean Ichbiah da Cll Honeywell Bull, contratados pelo Departamento de Defesa dos Estados Unidos durante a década de 70, com o intuito de substituir as centenas de linguagens de programação usadas pelo mesmo. Ada é uma aplicação com compiladores validados para uso confiável em missões críticas, tais como softwares de aviação.
O Smalltalk é uma linguagem orientada a objetos fracamente tipada, nela tudo é objeto, sejam os números, as classes, os métodos, blocos de código, dentre outros. Não há tipos primitivos, ao contrário de outras linguagens que são orientadas a objetos; strings, números e caracteres são implementados como classes em Smalltalk, por isso esta linguagem é considerada puramente orientada a objetos.
O C foi desenvolvido durante a década de 70, mas ainda é largamente utilizado. A grande vantagem do C é permitir escrever tanto programas extremamente otimizados para a máquina, como seria possível apenas em Assembly, e ao mesmo tempo vir com várias funções prontas, como uma linguagem de alto nível, que podem ser utilizadas quando não for necessário gerar um código tão otimizado. Esta flexibilidade, permitiu que o C fosse usado para desenvolver a maioria dos sistemas operacionais, como o Unix, Linux e o próprio Windows. Usando o compilador adequado, o C pode ser usado para desenvolver programas para várias plataformas diferentes.
O C++ mantém os recursos do C original, mas traz muitos recursos novos, como recursos orientados a objetos, sento também bem mais fácil de utilizar. O C++ é bastante usado atualmente para desenvolver muitos programas para várias plataformas.
O Visual Basic por ser uma linguagem visual, ele é extremamente fácil de usar, janelas, botões e muitas funções já vem prontas, bastando ao programador usá-las em seus programas. O custo da praticidade é o fato dos programas serem bem mais pesados do que equivalentes feitos em outras linguagens, com em C, e de rodarem apenas dentro do Windows.
O Java, muito conhecido e usado nos dias de hoje, também é uma linguagem orientada a objetos e foi desenvolvida na década de 90 por uma equipe de programadores chefiada por James Gosling, na empresa Sun Microsystems. Diferentemente das linguagens convencionais, que são compiladas para código nativo, a linguagem Java é compilada para um “bytecode” que é executado por uma máquina virtual.
Enfim, com toda a praticidade e a evolução das linguagens de programação deram-nos a possibilidade de geração de programas que foi extremamente relevante para o conceito que temos hoje em programas utilitários diários, que facilitam muito a vida dos usuários.


O gráfico abaixo mostra em ordem cronológica a criação das linguagens de programação:





BIBLIOGRAFIA

  • http://pt.wikipedia.org/wiki/Linguagem_de_programa%C3%A7%C3%A3o

  • http://blog.uncovering.org/archives/2007/09/linguagens_de_p.html

  • http://www.guiadohardware.net/artigos/linguagens/