Notas de Aula – Prof. Jorge Habib.
Transcrição
Notas de Aula – Prof. Jorge Habib.
Este material aborda o conteúdo de uma disciplina de introdução à programação para cursos de Ciência da Computação e Engenharias. A linguagem de programação utilizada é Pascal. A primeira parte engloba o histórico da computação e os fundamentos para compreender a representação de dados no mundo digital. A segunda parte contempla os principais elementos de programação do paradigma imperativo e procedural. Introdução à Programação: Conceitos e Práticas. Jorge Habib Hanna El Khouri Introdução à Programação: Conceitos e Práticas. (notas de aulas) Introdução à Programação: Conceitos e Práticas. Página 1 Introdução à Programação: Conceitos e Práticas. (notas de aulas) Jorge Habib Hanna El Khouri Professor Assistente do Centro de Engenharias e Ciências Exatas Universidade Estadual do Oeste do Paraná Campus de Foz do Iguaçu Revisão Técnica: Jun-2015 Introdução à Programação: Conceitos e Práticas. Página 2 CONTEÚDO 1. História da Computação ..................................................................................................... 11 1.1. Principais Eventos ............................................................................................................... 11 1.2. Gerações de Computadores .............................................................................................. 17 1.2.1. Primeira Geração (1938 - 1953) ............................................................................... 17 1.2.2. Segunda Geração (1952 - 1963) .............................................................................. 19 1.2.3. Terceira Geração (1962 - 1975)................................................................................ 21 1.2.4. Quarta Geração (1972 - ...)........................................................................................ 23 2. Sistemas de Numeração .................................................................................................... 25 2.1. Sistema de Numeração Egípcio ........................................................................................ 25 2.2. Sistema de Numeração Romano ...................................................................................... 26 2.3. Sistema de Numeração Maia ............................................................................................ 26 2.4. Sistema de Numeração Decimal....................................................................................... 28 3. Conceito de Bases .............................................................................................................. 32 3.1. Sistema Decimal .................................................................................................................. 32 3.2. Sistema Hexadecimal ......................................................................................................... 33 3.3. Sistema Binário .................................................................................................................... 34 3.4. Base Qualquer ..................................................................................................................... 35 3.5. Conversão entre Bases ...................................................................................................... 36 3.5.1. Conversão de Qualquer Base para a Base 10 ....................................................... 36 3.5.2. Conversão da Base 10 para Qualquer Base .......................................................... 38 3.6. Exercícios ............................................................................................................................. 41 3.6.1. Conversão da Base 2n para a Base 2 ...................................................................... 41 3.6.2. Conversão da Base 4 para a Base 2 ....................................................................... 42 3.6.3. Conversão da Base 8 para a Base 2 ....................................................................... 42 3.6.4. Conversão da Base 16 para a Base 2 ..................................................................... 43 3.7. Exercícios ............................................................................................................................. 44 4. Operações com Números Binários ................................................................................... 45 4.1. Operações Aritméticas ....................................................................................................... 45 4.1.1. Soma ............................................................................................................................. 45 4.1.2. Subtração...................................................................................................................... 46 4.1.3. Multiplicação ................................................................................................................. 47 Introdução à Programação: Conceitos e Práticas. Página 3 4.1.4. Divisão ........................................................................................................................... 47 4.1.5. Exercícios ..................................................................................................................... 48 4.2. Operações Lógicas ............................................................................................................. 49 4.2.1. AND (E) ......................................................................................................................... 51 4.2.2. OR (OU) ........................................................................................................................ 54 4.2.3. NOT (NÃO) ................................................................................................................... 50 4.2.4. XOR / OU EXCLUSIVO .............................................................................................. 56 4.2.5. Exemplos ...................................................................................................................... 57 4.2.6. Exercícios ..................................................................................................................... 59 4.3. Operações Aritméticas Binárias com Circuitos Lógicos ................................................ 59 4.3.1. Somador Completo ..................................................................................................... 60 4.3.2. Subtrator Completo ..................................................................................................... 61 5. Representação de Dados................................................................................................... 64 5.1. Representação de Números Inteiros ................................................................................ 65 5.1.1. Inteiros Sem Sinal – Binário Puro ............................................................................. 65 5.1.2. Inteiro Com Sinal ......................................................................................................... 65 5.1.2.1. Módulo Sinal ............................................................................................................. 65 5.1.2.2. Complemento de 1 .................................................................................................. 66 5.1.2.3. Complemento de 2 .................................................................................................. 67 5.1.2.4. Excesso de 2N-1........................................................................................................ 68 5.1.2.5. Visualização ............................................................................................................. 69 5.1.3. Soma em Complemento de 2 .................................................................................... 71 5.1.4. Exercícios ..................................................................................................................... 72 5.2. Representação de Números Reais................................................................................... 73 5.2.1. Formato IEEE 754 ....................................................................................................... 77 5.2.2. Exercícios ..................................................................................................................... 78 5.3. Representação de Informações Textuais ........................................................................ 80 6. Introdução a Arquitetura ..................................................................................................... 83 6.1. Processador - CPU ............................................................................................................. 85 6.1.1. Seção de Controle ....................................................................................................... 86 6.1.2. Seção de Processamento .......................................................................................... 87 6.1.3. Barramentos ................................................................................................................. 87 6.1.4. O sinal de Clock ........................................................................................................... 90 6.1.5. Organização do Computador .................................................................................... 90 6.1.5.1. Pipeline...................................................................................................................... 91 6.1.5.2. Cache ........................................................................................................................ 91 6.1.5.3. Outras Técnicas ....................................................................................................... 93 7. Introdução à Programação................................................................................................. 94 8. O Processo de Tradução ................................................................................................... 97 9. A Linguagem Pascal ........................................................................................................... 98 10. Estrutura de um Programa Pascal .................................................................................. 101 11. Identificadores .................................................................................................................... 101 12. Variáveis e Tipos de Dados ............................................................................................. 102 13. Comando de Atribuição .................................................................................................... 103 14. Expressões Numéricas ..................................................................................................... 104 14.1. Tipos Numéricos ........................................................................................................ 104 Introdução à Programação: Conceitos e Práticas. Página 4 14.2. Constantes.................................................................................................................. 107 14.3. Operadores Matemáticos ......................................................................................... 113 14.4. Funções Matemáticas ............................................................................................... 114 14.5. Exemplos .................................................................................................................... 119 15. Comandos de Entrada e Saída ....................................................................................... 121 15.1. Entrada ........................................................................................................................ 121 15.2. Saída ........................................................................................................................... 122 15.3. Exemplos .................................................................................................................... 122 16. Implementando Funções .................................................................................................. 124 16.1. Exemplos .................................................................................................................... 125 16.2. Exercícios ................................................................................................................... 127 17. Passagem de Parâmetros ................................................................................................ 134 17.1. Por Cópia .................................................................................................................... 134 17.2. Por Referência ........................................................................................................... 137 18. Implementando Procedures ............................................................................................. 141 19. Expressões Textuais ......................................................................................................... 145 19.1. Tipos Textuais ............................................................................................................ 145 19.2. Constantes.................................................................................................................. 145 19.3. Operadores ................................................................................................................. 150 19.4. Funções....................................................................................................................... 151 19.5. Exemplos .................................................................................................................... 156 20. Expressões Lógicas .......................................................................................................... 159 20.1. Tipo de Dado Boolean .............................................................................................. 159 20.2. Operadores Lógicos .................................................................................................. 159 20.3. Operadores Relacionais ........................................................................................... 161 20.4. Exemplos .................................................................................................................... 162 21. Instruções de Controle ...................................................................................................... 165 21.1. Seleção – IF-THEN-ELSE........................................................................................ 166 21.1.1. Sintaxe e Semântica ................................................................................................. 166 21.1.2. Exemplos .................................................................................................................... 168 21.2. Seleção – CASE ........................................................................................................ 176 21.2.1. Sintaxe e Semântica ................................................................................................. 176 21.2.2. Exemplos .................................................................................................................... 178 21.3. Repetição – FOR⋯DO.............................................................................................. 183 21.3.1. Sintaxe e Semântica ................................................................................................. 183 21.3.2. Exemplos .................................................................................................................... 185 21.4. Repetição – WHILE⋯DO ......................................................................................... 202 21.4.1. Sintaxe e Semântica ................................................................................................. 202 21.4.2. Exemplos .................................................................................................................... 204 21.5. Repetição – REPEAT⋯UNTIL ................................................................................ 216 21.5.1. Sintaxe e Semântica ................................................................................................. 216 21.5.2. Exemplos .................................................................................................................... 217 22. Array .................................................................................................................................... 221 22.1. Vetor ............................................................................................................................ 222 22.2. Sintaxe e Semântica ................................................................................................. 223 22.3. Exemplos .................................................................................................................... 225 Introdução à Programação: Conceitos e Práticas. Página 5 22.4. Matriz ........................................................................................................................... 246 22.5. Sintaxe e Semântica ................................................................................................. 247 22.6. Exemplos .................................................................................................................... 249 23. Arquivos .............................................................................................................................. 270 23.1. Arquivo Texto: Funções e Procedimentos............................................................. 274 23.2. Exemplos .................................................................................................................... 281 24. Conjuntos ............................................................................................................................ 302 24.1. Operadores ................................................................................................................. 302 24.2. Exemplos .................................................................................................................... 304 25. Registro ............................................................................................................................... 309 25.1. Sintaxe......................................................................................................................... 311 25.2. Exemplos .................................................................................................................... 314 26. Tipos Ordinais Definidos pelo Usuário ........................................................................... 319 26.1. Enumerados ............................................................................................................... 319 26.2. Subdomínio................................................................................................................. 322 27. Expressões Binárias ......................................................................................................... 325 27.1. Operadores Binários ................................................................................................. 325 27.2. Operadores de Deslocamento de Bits ................................................................... 326 27.3. Exemplos .................................................................................................................... 328 28. Ponteiros ............................................................................................................................. 334 28.1. Sintaxe......................................................................................................................... 334 28.2. Operação com Ponteiros .......................................................................................... 335 28.3. Exemplos .................................................................................................................... 336 29. Estrutura de dados dinâmica ........................................................................................... 346 29.1. Organização da memória de um programa em execução .................................. 346 29.2. Alocação dinâmica .................................................................................................... 348 29.3. Exemplos .................................................................................................................... 350 30. Interpretador da Linha Comando .................................................................................... 359 31. Recursividade..................................................................................................................... 361 31.1. Conceito ...................................................................................................................... 361 31.2. Exemplos .................................................................................................................... 363 32. Referências Bibliográficas................................................................................................ 368 Introdução à Programação: Conceitos e Práticas. Página 6 FIGURAS Figura 1: Ábaco. ............................................................................................................ 11 Figura 2: Ábaco e o seu uso. ......................................................................................... 11 Figura 3: O mecanismo de Anticítera. ........................................................................... 12 Figura 4: Réplica do mecanismo de Anticítera. ............................................................. 12 Figura 5: Esquema da máquina de Anticítera................................................................ 12 Figura 6: Tabela de Logaritmo de Napier ...................................................................... 12 Figura 7: Manuscrito Original de Schickard. .................................................................. 13 Figura 8: Réplica da Calculadora de Schickard. ............................................................ 13 Figura 9: La Pascaline. .................................................................................................. 13 Figura 10: La Pascaline. ................................................................................................ 13 Figura 11: Régua de Cálculo. ........................................................................................ 14 Figura 12: Máquina de Leibniz. ..................................................................................... 14 Figura 13: Tear Automático de Jacquard. ..................................................................... 14 Figura 14: Máquina Analítica de Babbage. .................................................................... 15 Figura 15: Formulário: Primeiro algoritmo de Ada. ............Erro! Indicador não definido. Figura 16: Cartão perfurado de Hollerith. ...................................................................... 16 Figura 17: Máquina de Tabulação. ................................................................................ 16 Figura 18: ENIAC. ......................................................................................................... 17 Figura 19: Memória de Núcleo. ..................................................................................... 19 Figura 20: Ambiente de Programação da Segunda Geração. ....................................... 20 Figura 21: Evolução no nível de Programação. ............................................................. 22 Figura 22: Principais características de Software e Hardware por geração. ................. 24 Figura 23: Circuitos Digitais: Contribuições ................................................................... 45 Figura 24: Circuito Somador Completo de 1 bit. ............................................................ 61 Figura 25: Circuito da Soma de N bits. .......................................................................... 61 Figura 26: Circuito Subtrator Completo de 1 bit. ............................................................ 63 Figura 27: Mundo Real x Mundo Digital. ....................................................................... 64 Figura 28: Métodos de Representação de Dados ......................................................... 64 Figura 29: Representação em Módulo-Sinal ................................................................. 66 Figura 30: Faixa de Representação de Números Reais. ............................................... 76 Figura 31: Organização típica de um computador. ........................................................ 83 Introdução à Programação: Conceitos e Práticas. Página 7 Figura 32: Programa na Memória RAM: Código + Dados. ............................................ 84 Figura 33: Unidade Central de Processamento típica. .................................................. 85 Figura 34: Passos na execução de uma instrução. ....................................................... 86 Figura 35: A sincronização da execução de uma instrução com o sinal de clock. ........ 90 Figura 36: Execução e Pipeline. .................................................................................... 91 Figura 37: Memória Cache. ........................................................................................... 92 Figura 38: Genealogia das Linguagens – Base FORTRAN. ......................................... 94 Figura 39: Genealogia das Linguagens. ........................................................................ 95 Figura 40: Níveis de Programação. ............................................................................... 95 Figura 41: Ciclo de Desenvolvimento de Software. ....................................................... 96 Figura 42: Processo de Tradução ................................................................................. 97 Figura 43: Estilo de Programação Spaghetti. ................................................................ 98 Figura 44: Evolução no estilo de programação. ............................................................ 99 Figura 45: Modos do Compilador. ............................................................................... 105 Figura 46: Acesso a tela com as diretivas de compilação. .......................................... 106 Figura 47: Alocação das variáveis na memória. .......................................................... 107 Figura 48: Programa fonte e janela de execução ........................................................ 122 Figura 49: Estrutura de uma função. ........................................................................... 125 Figura 50: Contexto da chamada da função foo. ......................................................... 135 Figura 51: Ponto 1 – Passagem por Cópia. ................................................................. 136 Figura 52: Ponto 2 – Passagem por Cópia. ................................................................. 136 Figura 53: Ponto 3 – Passagem por Cópia. ................................................................. 137 Figura 54: Ponto 1 – Passagem por Referência. ......................................................... 138 Figura 55: Ponto 2 – Passagem por Referência. ......................................................... 139 Figura 56: Ponto 3 – Passagem por Referência. ......................................................... 139 Figura 57: Ponto 1 – Calculo Raizes. .......................................................................... 142 Figura 58: Ponto 2 – Calculo Raizes. .......................................................................... 143 Figura 59: Ponto 3 – Calculo Raizes. .......................................................................... 144 Figura 60: Diagrama lógico para o cálculo das raízes de uma equação do 2º grau. ... 170 Figura 61: Instrução CASE .......................................................................................... 177 Figura 62: Repetição do tipo FOR. .............................................................................. 184 Figura 63: Estrutura de Controle do tipo FOR ............................................................. 184 Figura 64: Instrução BREAK........................................................................................ 197 Figura 65: Conversão Decimal - Binário ...................................................................... 205 Figura 66: Correspondência entre Definição do Vetor e a Atribuição. ......................... 225 Figura 67: Cópia de Sub-Vetor. ................................................................................... 241 Figura 68: Posição de Sub-Vetor................................................................................. 241 Figura 69: Eliminação de um Sub-Vetor. ..................................................................... 242 Figura 70: Comparação Escalar, Vetor e Matriz .......................................................... 246 Figura 71: Entrada/Saída de grande volume de dados sem o uso de arquivos .......... 270 Figura 72: Entrada/Saída de grande volume de dados com o uso de arquivos .......... 271 Figura 73: Visualização de um arquivo texto como sequência de caracteres. ............ 272 Figura 74: Visualização de um arquivo executável como sequência de caracteres. ... 272 Figura 75: Dado em Memória gravado em arquivo como binário ................................ 273 Figura 76: Dado em Memória gravado em arquivo como texto ................................... 273 Figura 77: Manipulação de Arquivo por um programa ................................................. 274 Figura 78: Organização dos dados da disciplina na memória ..................................... 289 Introdução à Programação: Conceitos e Práticas. Página 8 Figura 79: Abstrações típicas de uma linguagem procedural ...................................... 310 Introdução à Programação: Conceitos e Práticas. Página 9 PREFÁCIO O conteúdo deste material é fruto de um conjunto de notas de aulas preparadas para a disciplina introdutória à programação da grade do curso de Ciência da Computação e posteriormente estendido para cursos de engenharias, principalmente Engenharia Elétrica e Mecânica. A estrutura do material não está centrada em uma linguagem de programação, mas sim no estabelecimento de uma sequência natural de conteúdos que proporcione, desde o seu princípio, o suporte logico para a construção de programas elementares. No avançar das temáticas o conhecimento vai sendo refinado e ao mesmo tempo testado com problemas mais elaborados. Antecedendo o conteúdo de programação são apresentados tópicos que ilustram os pontos na história que marcaram a evolução da computação, onde conhecimentos das mais diversas áreas foram combinados a fim de delinear o estado atual da tecnologia da informação. Uma parte fundamental da computação está na compreensão da natureza dos dados e como eles circulam no ambiente computacional. Assim, os sistemas de numeração são abordados visando alcançar um entendimento sobre a notação binária que predomina no mundo digital. Uma breve exposição sobre a destaca o trânsito da informação entre as camadas de e de , e expõe a importância de se compreender toda a transformação a que é submetido um determinado problema até se transformar efetivamente em um programa ativo na arquitetura do computador. Um estudante de computação ou de engenharia (relacionada ao tema) deve ter a clara noção de todas as camadas de abstração que formam o contexto computacional. Os principais métodos de representação de dados elementares são expostos e proporcionam conhecimentos essenciais ao entendimento dos seus impactos nos algoritmos. A parte de programação estabelece uma sequência de exposição do conteúdo tanto nos aspectos conceituais quanto nas aplicações práticas. Os recursos de programação apresentados são os típicos da família de linguagens denominada imperativa ou procedural, e a linguagem é a escolhida para concretizar os ensinamentos. A grande similaridade entre as linguagens procedurais atuais faz com que a transposição dos conhecimentos apresentados para outras linguagens seja muito facilitada. Introdução à Programação: Conceitos e Práticas. Página 10 1. História da Computação Este capítulo resume os principais eventos históricos que culminaram com o estágio atual da tecnologia digital. Os desenvolvimentos ocorreram nas mais variadas áreas da Ciência e o processo de evolução tecnológica foi gerado em grande parte pelo cruzamento destes conhecimentos. Destaca-se como fonte de consulta para ] . Além disso, o aprofundamento dos temas aqui citados a publicação [ acervo do e do site , principalmente as fotos, também serviram de fonte de consulta. 1.1. Principais Eventos O uso de ferramentas para apoiar as atividades do homem não é recente, e desde os primórdios de nossa história tem-se observado um constante aumento no uso de equipamentos e instrumentos que auxiliam nas tarefas normalmente realizadas por seres humanos. No campo da informática, o processo de evolução se deu de forma lenta durante séculos, experimentando uma explosão tecnológica acentuada na segunda metade do século passado. Na busca por alternativas para o trabalho realizado pelo homem, principalmente na mecanização do processo de contagem e na realização de trabalhos exaustivos, obstáculos importantes foram sendo superados, a saber: a) b) a.C. - uso do ábaco no vale entre os rios Tigre e Eufrates; a.C. - aparecimento do ábaco chinês, chamado Suan-Pan, e do Soroban no Japão; Figura 1: Ábaco. Introdução à Programação: Conceitos e Práticas. Figura 2: Ábaco e o seu uso. Página 11 c) a.C. – uso de um mecanismo denominado Anticítera1 com um complexo sistema de engrenagens provavelmente aplicado à navegação. Foi resgatado em na costa da ilha grega de Anticítera. O sistema de engrenagens simulava as órbitas da lua, sol e de mais cinco planetas, além de prever eclipses da lua e do sol. Figura 3: O mecanismo de Anticítera. Figura 4: Réplica do 2 mecanismo de Anticítera . Figura 5: Esquema da máquina de Anticítera. d) séc. - John Napier (Edimburgo - Escócia, — de abril de ) inventou o logaritmo e uma máquina capaz de multiplicar e dividir automaticamente; Figura 6: Tabela de Logaritmo de Napier 1 http://pt.wikipedia.org/wiki/Máquina_de_Anticítera Disponível no Museu Arqueológico Nacional de Atenas (feita por Robert J. Deroski, com base em Derek J. de Solla Price. http://pt.wikipedia.org/wiki/Máquina_de_Anticítera 2 Introdução à Programação: Conceitos e Práticas. Página 12 e) - Wilhelm Schickard ( de abril de , Herrenberg, Alemanha — de outubro de , Tübingen) construiu uma calculadora mecânica, baseada em rodas dentadas, capaz de realizar as operações básicas com números de seis dígitos e indicar um overflow através do toque de um sino. Figura 7: Manuscrito Original de Schickard. f) - Blaise Pascal (Clermont-Ferrand - França, de Junho de — Paris, de Agosto de ) utilizando o princípio do ábaco, implementou uma máquina automática de calcular - La pascaline. Efetuava somas e subtrações; Figura 9: La Pascaline. g) Figura 8: Réplica da Calculadora de Schickard. Figura 10: La Pascaline. – Padre Willian Oughtred (Eton-Inglaterra, de Março de — Albury, de Junho de ) inventou a régua de cálculo (Slide rule/ slipstick) baseada nos logaritmos de Napier e divulgou o uso do sinal para multiplicação, tendo introduzido os termos , e . Introdução à Programação: Conceitos e Práticas. Página 13 h) – Seth Patridge, Inglês, desenvolveu a régua de cálculo deslizante. Este dispositivo foi muito utilizado até os anos setenta; Figura 11: Régua de Cálculo. i) - Gottfried Wilhelm von Leibniz (Leipzig - Alemanha, de julho de — Hanover, de novembro de ) aprimorou a máquina de Pascal e obteve uma calculadora que somava, subtraía, multiplicava e dividia; Figura 12: Máquina de Leibniz. j) – Joseph Marie Charles - apelido Jacquard (Lyon – França, de Julho de – de Agosto de ), mecânico, construiu um tear automático com entrada de dados via cartões perfurados para controlar a confecção dos tecidos e seus desenhos; Figura 13: Tear Automático de Jacquard. Introdução à Programação: Conceitos e Práticas. Página 14 k) – Charles Babbage (Londres - Inglaterra, de Dezembro de — Londres, de Outubro de ) projetou a sua Máquina Analítica ou Diferencial, que possuía memória, programa, unidade de controle e periféricos; Figura 14: Máquina Analítica de Babbage. l) - Ada Augusta Byron King, Condessa de Lovelace (Londres – Inglaterra, de Dezembro de de Novembro de ), conhecida como Ada Lovelace, fez os primeiros estudos sobre aritmética binária. Escreveu o primeiro algoritmo para ser processado por uma máquina, a máquina analítica de Charles Babbage. Figura 15: Formulário: Primeiro algoritmo de Ada. Figura 16: Detalhe de parte do Formulário. Introdução à Programação: Conceitos e Práticas. Página 15 m) - George Boole (Lincoln - Inglaterra, de Novembro de — Ballintemple, de Dezembro de ) desenvolveu a teoria da Álgebra de Boole (livro: An Investigation of the Laws of Thought), que permitiu o desenvolvimento da Teoria dos Circuitos Lógicos; n) - Herman Hollerith (Buffalo - EUA, de Fevereiro de — Washington, D.C., de Novembro de ) empresário norte-americano, construiu a Máquina de recenseamento utilizada no censo de dos EUA. A máquina efetuava a leitura dos cartões de papel perfurados com as informações em código (Decimal Codificado em Binário). Os dados do censo de foram tabulados em apenas um ano. O censo de levou oito anos para ser tabulado; Figura 17: Cartão perfurado de Hollerith. o) Figura 18: Máquina de Tabulação. - Hollerith e Watson fundam a IBM; p) - Alan Mathison Turing (Londres – Inglaterra, de Junho de — de Junho de ) desenvolveu a teoria de uma capaz de resolver qualquer problema computável. Foi denominada de ; q) - Claude Elwood Shannon (Petoskey, Michigan – EUA, de Abril de — de Fevereiro de ) escreve sua tese de mestrado “A Symbolic Analysis of Relay and Switching Circuits” onde utiliza aritmética binária e Álgebra de Boole para implementar lógicas através de circuitos com chaves e relés, tornando-se um marco para os circuitos digitais; r) - sob a liderança de Alan Turing foi projetado o , computador inglês utilizado na Segunda Guerra Mundial para quebrar o segredo alemão codificado pela máquina . Introdução à Programação: Conceitos e Práticas. Página 16 Este processo evolutivo culminou com o surgimento das primeiras máquinas capazes de realizar cálculos mais complexos do que as operações básicas. A partir de então, várias gerações de computadores foram surgindo até os dias atuais. 1.2. Gerações de Computadores Na sequência são listadas as principais contribuições em cada geração de computadores, enfatizando-se a tecnologia utilizada para a construção do hardware e a forma de programação. Trata-se apenas de um sumário bastante resumido, que serve como referência de termos para pesquisas mais detalhadas. 1.2.1. Primeira Geração (1938 - 1953) A primeira geração foi marcada pela construção do primeiro computador eletrônico, o V (Electronic Numerical Integrator and Computer), em , tornando-se um marco na evolução da computação. O começou a operar em , sendo concluído totalmente em e encerrando suas operações em . O desenvolvimento do se deu na Universidade da Pensilvânia (Filadélfia, EUA), e contou com o apoio de John von Neumann. Figura 19: ENIAC. O continha válvulas, relés, resistores, capacitores o que resultava em mais de toneladas de equipamentos distribuídos em . Seu consumo chegava a . A entrada e saída de dados eram feitas por meio de leitora de cartões de perfurados, enquanto que a programação se dava pela construção de Introdução à Programação: Conceitos e Práticas. Página 17 circuitos específicos para tratar o problema por ajustes manuais de chaves e conexões de cabos. A unidade de sincronização trabalhava com ciclos de microsegundos ( ciclos de clock de ), sendo capaz de executar adições/subtrações ou multiplicações ou divisões ou raiz quadrada por segundo. A memória era capaz de conter números ( bytes). Sua utilização inicial foi em trabalhos com soluções numéricas de problemas relacionados com trajetórias balísticas e no desenvolvimento da bomba de hidrogênio. Em junho de , von Neumann publicou o trabalho sobre o (Electronic Discrete Variable Automatic Computer), e estabeleceu as bases para as arquiteturas de computadores das gerações seguintes, atualmente conhecida como ―arquitetura de von Neumann‖, que trazia o conceito de programa armazenado. O esforço de guerra levou à construção, em , pelos ingleses da máquina , concebida para quebrar o código da máquina alemã , utilizada na transmissão de mensagens. Pelo fato de ser uma máquina estratégica no campo militar, a sua divulgação foi bastante limitada, o que restringiu o reconhecimento da sua importância no desenvolvimento da computação. Os trabalhos de Alan Turing sobre computabilidade foram consideravelmente relevantes na construção dos algoritmos da máquina . Nesta geração, a programação era em linguagem de máquina composta da codificação binária das instruções da máquina. Por exemplo, a soma de dois números produzindo um resultado na memória tinha um código semelhante à: O entendimento desta lógica era restrito aos profissionais com conhecimento da arquitetura do computador. Em resumo, a primeira geração se caracterizou por: Surgimento do ; Utilização de relés como dispositivos de chaveamento; Utilização de válvulas a vácuo; Hardware caro, grande e de difícil manutenção; Programação em linguagem de máquina; permitiu o uso de software. Introdução à Programação: Conceitos e Práticas. Página 18 1.2.2. Segunda Geração (1952 - 1963) A segunda geração foi caracterizada pela invenção do por John Bardeen, Walter Brattain e William Shockley. Em dezembro de Brattain e Moore apresentaram o dispositivo para um grupo da Bell Labs. Em a Bell Labs conclui o , tido como o primeiro computador totalmente transistorizado. As vantagens dos transistores em relação às válvulas a vácuo são principalmente: menos consumo de energia, peso e tamanho menores, reduzida perda de calor e aquecimento, maior confiabilidade e robustez, maior duração e resistência a impacto e vibração. Também nesta geração foram consolidados dispositivos como as impressoras, as fitas magnéticas e os discos para armazenamento, dentre outros. A memória do computador passou a utilizar tecnologia de núcleo magnético, também denominada , composta por diminutos anéis magnéticos conectados por fios que permitiam a leitura e gravação de informação. Os valores e eram associados ao sentido de magnetização deste núcleo. Figura 20: Memória de Núcleo. O surgimento dos sistemas operacionais e das linguagens de programação acelerou o desenvolvimento dos computadores. A forma de programação evoluiu da para , onde os códigos binários eram associados a mnemônicos que indicavam o tipo de operação realizada. Assim, a soma de dois números produzindo um resultado na memória tinha um código semelhante à: LINGUAGEM ASSEMBLY LINGUAGEM DE MÁQUINA A percepção do poder de codificação em linguagens mais acessíveis levou a um importante salto das linguagens de alto nível com a criação do FORTRAN em e COBOL em , iniciando assim um forte ciclo de desenvolvimento de novas linguagens. Introdução à Programação: Conceitos e Práticas. Página 19 O mesmo problema podia agora ser codificado em uma muito semelhante à sua notação matemática: LINGUAGEM DE ALTO NÍVEL LINGUAGEM ASSEMBLY de forma LINGUAGEM DE MÁQUINA A forma de utilização dos computadores era semelhante a de uma calculadora, onde o programa e dados eram introduzidos via cartões perfurados, fita de papel perfurado ou fita magnética e os resultados eram gerados para estes mesmos dispositivos. Em a VI primeira impressora , fabricada pela Remington-Rand, foi utilizada como dispositivo de saída integrado ao computador , dos mesmos criadores do . Neste período, os computadores ocupavam salas específicas que acomodavam toda a estrutura de equipamentos e sua operação demandava técnicos especializados. É importante lembrar que os computadores eram caros e de uso restrito a governos, universidades e grandes corporações. Os programas eram produzidos e escritos em formulário próprio e levados à sala de perfuração de cartão. O maço de cartões era levado á sala com a leitora, onde um operador procedia a leitura e processamento do pacote com o programa. Ao encerrar o processamento deste lote de cartões então o operador se dirigia a sala de impressora para retirar o relatório para entrega ao usuário. A seguinte figura esboça este fluxo: Figura 21: Ambiente de Programação da Segunda Geração. Introdução à Programação: Conceitos e Práticas. Página 20 Cada programa que necessitava de processamento tinha que seguir este fluxo. A necessidade de melhores mecanismos de interação entre usuário e o computador, facilidades para administrar o processamento dos programas e formas de gerenciar o computador e seus periféricos levou ao desenvolvimento de um conjunto de programas que foi denominado de . Assim, surgiu a ideia de processamento em lote onde grupos de programas (maços de cartões) eram lidos e gravados em uma fita. A fita era levada à sala de processamento e lida para o computador, que processava toda a sequência e gerava uma saída também em fita, cuja impressão acontecia em uma unidade separada (impressão off-line). Em resumo, a segunda geração se caracterizou por: 1.2.3. invenção do transistor; 1º computador a transistor ( ) pela BELL Labs; uso de transistores e diodos; uso de memória de núcleo; uso de linguagem Assembly até , e de FORTRAN e COBOL a partir daí; surgimento dos Sistemas Operacionais para Processamento em (Lote) de programas. Terceira Geração (1962 - 1975) Na geração anterior, o elemento básico para construção dos processadores evoluiu da válvula a vácuo para os transistores. Na terceira geração, o transistor continuou a ser o dispositivo de construção elementar, porém surgiu o circuito integrado ( ou chip) em pequena ( componentes eletrônicos por ) e média escala ( componentes eletrônicos por ), que são circuitos eletrônicos em miniatura compostos principalmente por dispositivos semicondutores. A invenção do circuito integrado ou ( ) é atribuída a Jack Kilby da Texas Instruments e Robert Noyce, da Fairchild Semiconductor, cabendo a Kilby o Prêmio Nobel de Física no ano 2000 pela participação na criação. O uso de circuitos integrados permitiu maior compactação e confiabilidade dos computadores com expressiva redução de custos. A tecnologia de fabricação da memória de acesso aleatório (RAM) evolui da para dispositivos de estado sólido. O tinha as maiores atenções no projeto de um computador, porém o estava ganhando importância rapidamente. Foram criadas várias linguagens de programação, entre elas , , , , , dentre outras. O processo de produção de programas começou a ser visto como atividade de engenharia, com todo o rigor que isto significa. Introdução à Programação: Conceitos e Práticas. Página 21 A figura a seguir ilustra como o programador foi se afastando da necessidade de codificar diretamente em binário na medida em que surgiam novas linguagens de programação. Inicialmente a permitiu maior produtividade na programação, e a tradução para a era feita por um software tradutor denominado . Em seguida, com o surgimento da , como o , o programador codificava de forma mais adequada aos problemas matemáticos, sendo que a tradução para o era feita por um software denominado . Figura 22: Evolução no nível de Programação. Houve importante melhoria nos compiladores, que passaram a produzir códigos tão eficientes quanto um bom programador de linguagem assembly. O mercado de computadores era majoritariamente dominado pelos produtos da IBM, com destaque para os ou computadores de grande porte. Os sistemas operacionais apresentaram significativa melhora e incorporaram recursos de multiprogramação, time-sharing e memória-virtual. A multiprogramação permitia que o sistema operacional trabalhasse com vários programas ativos. Neste caso, se um determinado programa demandava dados armazenados em fita, então outro programa entrava em processamento, até que o recurso do programa anterior fosse disponibilizado. O , similar ao multiprocessamento, permitiu que um único computador tivesse seu uso compartilhado por múltiplos usuários com acesso via terminal on-line. A memória virtual é um técnica que permite que programas utilizem mais memória do que o disponível fisicamente na memória principal. A memória secundária (em disco, fita ou outro meio de armazenamento) é utilizada como suporte para esta técnica de gerenciamento de memória. Outro recurso típico da terceira geração permitiu que programas fossem carregados diretamente dos cartões para discos. Assim, logo que um determinado programa tivesse sua execução encerrada, o sistema operacional iria carregar um dos programas do disco para a partição de memória destinada aos serviços em execução. Este recurso recebeu o nome de ( ). Em resumo, a terceira geração se caracterizou por: Introdução à Programação: Conceitos e Práticas. Página 22 1.2.4. uso de circuitos integrados em pequena (SSI) e média escala (MSI); uso de circuitos impressos em multicamadas; uso de memória de estado sólido; linguagens de alto nível com compiladores inteligentes; uso de multiprogramação sistemas operacionais para uso em (tempo-compartilhado); utilização de memória virtual. Quarta Geração (1972 - ...) Os circuitos integrados apresentaram um aumento significante na quantidade de transistores. A tecnologia foi denominada de Integração em Escala Muito Grande ( – ), incorporando de a transistores. As escalas seguintes de integração, ultra e super, comportam a e a dispositivos semicondutores respectivamente. Este nível de integração e compactação permitiu que os CI´s incorporassem cada vez mais blocos funcionais da arquitetura do computador, ao ponto que todos os elementos básicos de uma CPU (Central Processing Unit) coubessem em um único chip, surgindo o microprocessador. A Intel teve importante papel no desenvolvimento dos microprocessadores, tendo iniciado com o modelo Intel produzido em . Este processador era um dispositivo de bits que continha uma ALU (Aritmetic and Logic Unit), uma unidade de controle e alguns registradores. O marco importante foi o lançamento em , do seu primeiro processador de bits, o . Logo em seguida, a IBM lança em o IBM PC que se tornou padrão para microcomputadores. O respaldo da IBM foi essencial para a aceitação e o crescimento deste equipamento. Uma série de fabricantes passou a produzir componentes para esta arquitetura, enquanto que a própria IBM se manteve na linha dos computadores de grande porte, os . Os meios de armazenamentos tais como unidades de disco e fita ocupavam espaços consideráveis nos CPD´s (Centro de Processamento de Dados), tornando assim um desafio produzir uma unidade que fosse compatível com uso pessoal. Neste ponto, as unidades de discos flexíveis (floppy disk) foram fundamentais para a viabilização dos PC´s. Os sistemas operacionais incorporavam cada vez mais facilidades de gerenciamento dos recursos computacionais, tais como CPU, memória, processos e arquivos. Os sistemas operacionais DOS e Unix tiveram seu período de consolidação, sendo o DOS no ambiente PC Intel e o UNIX basicamente nas RISC. A Microsoft iniciou sua trajetória com o DOS, e esta plataforma estimulou o surgimento de um novo mercado de desenvolvimento de softwares. Introdução à Programação: Conceitos e Práticas. Página 23 A necessidade de compartilhar informações e itens de impulsionou o surgimento das redes de computador e do processamento distribuído. E os sistemas operacionais tiveram que ampliar suas capacidades para incluir o gerenciamento de mais estes recursos, fazendo que toda esta complexidade fosse transparente ao usuário. O nível de integração dos circuitos integrados alavancou arquiteturas de computadores com múltiplos processadores permitindo o processamento paralelo. Em resumo, a quarta geração se caracterizou por: uso de circuitos integrados em escala muito grande (VLSI); compactação em alta densidade (computadores menores); Linguagens de alto nível que manuseiam tanto dados escalares como vetores; sistema operacionais em Time-sharing e Memória Virtual; sistemas com multiprocessadores; Processadores paralelos; aparecimento dos microprocessadores; uso de disquetes (floppy disk) como meio de armazenamento; Algumas literaturas qualificam o estágio atual como sendo , principalmente pelo forte emprego da tecnologia de . Porém, é difícil classificar uma geração fazendo parte dela, pois o avanço tecnológico atual traz inovações a todo o momento. Por exemplo, poderíamos qualificar como marco extraordinário a abertura da internet e o surgimento da e do primeiro browser em , por Tim Berners-Lee. O interessante é que seria uma geração caracterizada principalmente por criações de . O quadro a seguir destaca os principais elementos de caracterizaram as gerações: Software Linguagem de Máquina Hardware Válvulas e Relés 1ª geração 1935 Linguagem Assembly Diodo e Transistor Linguagem de Alto Nível Circuitos Integrados 2ª geração 1945 e de 1955 3ª geração 1965 que Sistemas Operacionais Compiladores Inteligentes Circuitos Integrados VLSI Microprocessadores 4ª geração 1975 Figura 23: Principais características de Software e Hardware por geração. Introdução à Programação: Conceitos e Práticas. Página 24 2. Sistemas de Numeração Há cerca de anos o homem começou a ter necessidade de contar (agricultura e pastoreio), onde os pastores de ovelhas tinham necessidades de controlar os rebanhos. Precisavam saber se não faltavam ovelhas. (III) Alguns vestígios indicam que os pastores faziam o controle de seu rebanho usando conjuntos de pedras. Ao soltar as ovelhas, o pastor separava uma pedra para cada animal que passava e guardava o monte de pedras. Quando os animais voltavam, o pastor retirava do monte uma pedra para cada ovelha que passava. Se sobrassem pedras, ficaria sabendo que havia perdido ovelhas. Se faltassem pedras, saberia que o rebanho havia aumentado. Desta forma mantinha tudo sob controle. Uma ligação do tipo: para cada ovelha, uma pedra chama-se, em Matemática, correspondência um a um. Provavelmente passou a utilizar o dedo como apoio à contagem, sem, no entanto poder guardar a informação. O processo de contagem evoluiu para o agrupamento quando tratava de grandes quantidades, p. ex: João fez pontos ( pontos). 2.1. Sistema de Numeração Egípcio Essa ideia de agrupar marcas foi utilizada nos sistemas mais antigos de numeração. Os egípcios da antiguidade criaram um sistema muito interessante para escrever números, baseado em agrupamentos. era representado por uma marca que se parecia com um bastão | por duas marcas || E assim por diante. Símbolo descrição valor bastão calcanhar rolo de corda flor de lótus dedo apontando peixe homem Introdução à Programação: Conceitos e Práticas. Página 25 Os números eram compostos pela associação destes símbolos e a quantidade representada era obtida pela soma dos valores individuais. Por exemplo, é representado por: 2.2. Sistema de Numeração Romano O sistema de numeração romano possui um conjunto símbolos, conforme tabela abaixo, sendo que um número é representado pela combinação destes símbolos e o valor é a soma de cada uma das parcelas. A fim de evitar representações muito extensas, um determinado valor pode ser representado a partir de um símbolo de referência acompanhado de símbolos a esquerda (subtração do valor principal) ou a direita (soma ao valor principal). Por exemplo, ( )e ( – ). Este sistema é bastante limitado para aplicações da matemática que vão além da representação de números, onde mesmo as operações básicas são difíceis de serem efetuadas. 2.3. Sistema de Numeração Maia A cultura teve destaque nos últimos anos principalmente pelas questões relativas ao calendário e o suposto fim dos tempos. Porém, os também dispunham de um sistema de numeração bem estruturado que se caracterizava como posicional, vigesimal e símbolo para o . Os valores dos símbolos vão de a , sendo eles: Introdução à Programação: Conceitos e Práticas. Página 26 ● ● ● ● ●● ●● ●● ●● ●●● ●●● ●●● ●●● ●●●● ●●●● ●●●● ●●●● Os números são estruturados pela combinação destes símbolos, sendo cada símbolo contribuindo com o seu valor multiplicado por elevado a sua posição. A soma destas parcelas fornece o valor do número. Por exemplo, o número , que no sistema fica: 𝟐 ●● 𝟗 ●●●● Este sistema permite a execução das operações básicas com a mesma facilidade que fazemos no nosso sistema decimal. Vejamos o exemplo que segue: A coluna indica a operação desejada. As colunas e indicam a decomposição do e no sistema vigesimal. As colunas e contém o e no sistema . A coluna indica a soma provisória que se Introdução à Programação: Conceitos e Práticas. Página 27 obtém juntando as parcelas. Na sequência vêm os ajustes necessários, transferindo os excedentes a , ou seja, cada unidades excedentes em uma casa equivale a uma unidade na casa superior. Como o símbolo equivale a ●●●●●, então na coluna são transferidas unidades ( e ●) da posição para a posição . Isto faz surgir um símbolo ● a mais na posição . Na coluna tem-se os ●●●●● da posição substituídos por um . Na coluna tem-se a notação convertida para uma expressão convencional e o resultado da soma destas parcelas é indicado na coluna , tal como se esperava da soma . 2.4. Sistema de Numeração Decimal Os sistemas numéricos egípcio, romano e maia apresentados são apenas parte dos muitos sistemas adotados por diferentes civilizações da antiguidade, tais como os babilônios, gregos, chineses, hindus entre outros. Cabe destacar, que com exceção dos maias, que habitavam a América, as civilizações da Europa, Oriente e Oriente Médio mantinham um sistema de troca de mercadorias e comumente impérios que se sucediam, onde um dos efeitos era a difusão de conhecimentos. A ciência dos gregos atingiu grande desenvolvimento no século com Euclides, cuja obra sobre Geometria (Os Elementos) é importante até hoje no ensino dessa matemática. Os gregos tinham seu próprio sistema de numeração, com base , utilizando letras para representar os números, o que não facilitava os cálculos. Os romanos, que expandiram seus domínios a partir do século ., foram aos poucos ocupando o espaço do império deixado por Alexandre, ao mesmo tempo que foram assimilando parte da ciência grega. O interesse dos romanos foi pelas aplicações práticas na engenharia (construção de estradas e aquedutos) e na medicina, com pouca contribuição na matemática. As invasões bárbaras, nos séculos e , colocaram fim ao Império Romano no ocidente e conduziu a Europa a um período de trevas no campo da Ciência. Entretanto, enquanto o Império Romano declinava, uma grande civilização florescia no Oriente, no vale do rio Indo, entre as regiões que atualmente constituem o Paquistão e a Índia. É importante destacar que os sistemas de numeração egípcio e romano não eram propícios para efetuar cálculos. As dificuldades destes sistemas foram superadas pelos hindus, que desenvolveram as bases do moderno sistema de numeração. Eles souberam reunir três características que estavam dispersos em outros sistemas numéricos da antiguidade, a saber: Decimal: uso de dez símbolos para representar os dígitos de zero a dez (o egípcio, o romano e o chinês também o eram); Posicional: os símbolos assumem diferentes valores de acordo com a posição que ocupa na formação do número (o babilônio e o maia também eram); Presença do Zero: Foi um importante avanço a adoção de um símbolo para o nada. Introdução à Programação: Conceitos e Práticas. Página 28 Estas três características, reunidas, tornaram o sistema de numeração hindu o mais prático de todos. Não é sem motivo que hoje ele é usado quase no mundo todo. Enquanto os hindus, que habitavam o vale do rio Indo, desenvolviam seu sistema de numeração, grandes acontecimentos tiveram início na Península Arábica. No século floresce o Islamismo, e estabelece um grande império que abrangia a região do rio Indo, a leste, o norte da África e a Península Ibérica. A expansão Islâmica através da conquista veio na sequência de grandes impérios, tais como o babilônio, persa, grego (macedônio) e romano. A matemática era uma das áreas de interesse dos estudiosos árabes e o contato com o saber oriental e toda a história de conhecimento dos impérios que o antecedeu permitiu um salto importante na ciência. Um grande passo foi dado na direção do extremo oriente, quando os árabes entraram em contato com a cultura hindu e logo manifestaram particular interesse pelo seu sistema numérico, reconhecendo sua simplicidade e praticidade. Em outra frente, os árabes haviam ocupado parte da Península Ibérica, o que serviu de ponte para a ciência oriental ser introduzida na Europa medieval. A expansão árabe resultou também no surgimento, entre os séculos e , de várias universidades e bibliotecas, desde Bagdá, no atual Iraque, até Granada e Córdoba, na atual Espanha. Um marco relevante na tentativa de introduzir o sistema hindu-arábico na Europa foi a publicação de Leonardo de Pisa (também conhecido por ), intitulada (O Livro do Ábaco). O pai de Fibonacci era dono de casas de comércio ultramarino, sendo uma no norte da África. Ainda jovem Fibonacci mudou-se para esta região e conviveu com esta nova matemática e aprofundou seus conhecimentos no sistema árabe de numerais e cálculo. Ao retornar a Pisa, Leonardo deu sequência a seus estudos e publicou pela primeira vez em o destacado livro, cujo conteúdo introduzia a notação árabe, as operações fundamentais com números inteiros, cálculo envolvendo séries, proporções, raízes quadradas e cúbicas, e uma breve abordagem sobre geometria e álgebra. Neste livro foi apresentado o problema de crescimento da população de coelhos de acordo com uma série que levou o nome de . As páginas do livro tornavam difíceis a reprodução de exemplares dado que este processo era totalmente manual, o que limitou sua difusão.IV O início da retomada, em , do território ibérico dos mouros pelo rei da Espanha, permitiu que populações árabes passassem ao domínio europeu, facilitando a absorção do conhecimento escrito em árabe, principalmente os trabalhos de . Entretanto, foram necessários vários séculos para que o sistema hindu-arábico fosse aceito de fato na Europa. O intervalo entre a queda do império romano e a era dos descobrimentos se caracterizou como período das trevas em função da pouca produção de conhecimento. Não foi diferente em relação ao sistema de numeração hindu e da nova forma de realizar as operações aritméticas, que enfrentou resistência frente aos métodos baseados no ábaco, herança dos romanos. Uma parte do espaço deixado pela queda do império romano no ocidente no século foi ocupada pela Igreja, que praticamente deu continuidade a utilização do sistema numérico criado pelos romanos. A chegada dos árabes à Europa no século trouxe Introdução à Programação: Conceitos e Práticas. Página 29 consigo o sistema de numeração hindu, que certamente em algum momento iria levar à confrontação de sistemas. Basta efetuar algumas operações aritméticas utilizando algarismos romanos, para perceber as inegáveis vantagens que o sistema numérico hindu-arábico tem sobre o sistema romano. Mesmo assim, o novo sistema de numeração acabou prevalecendo na Europa somente no século . Atualmente quando comparamos as representações dos dígitos numéricos em árabe e a forma ocidental observamos uma diferença sensível, e muitas vezes não atentamos que possuem a mesma raiz. Porém, as grandes distâncias entre o Oriente Médio, Península Ibérica e o extremo oriente combinadas com os séculos de história fizeram que sequências de pequenas alterações na forma de representar os dígitos resultassem em diferenças significativas na configuração final. Até o século todo o material produzido era escrito a mão, e diferentes copistas possuíam diferentes caligrafias que associadas á direção da escrita (direita para a esquerda ou esquerda para a direita) contribuíram para as sucessivas alterações nas aparências dos dígitos numéricos. A invenção da imprensa por Gutenberg no século permitiu que tanto as letras quanto os símbolos utilizados para representar os dígitos numéricos adquirissem uma forma mais estável e definitiva. A seguinte tabela ilustra as modificações sofridas na forma de representação dos dígitos utilizados no sistema de numeração criado pelos hindus, adotado pelos árabes e passado aos europeus: Por volta do século , os hindus representavam os algarismos assim, ainda sem um símbolo para o nada: No século , já com representação evoluiu para: o , a No século os hindus representavam os dez dígitos assim: No mesmo século , os árabes que estavam no Ocidente representaram assim: No século os árabes empregavam esta representação: orientais Introdução à Programação: Conceitos e Práticas. Página 30 As formas usadas pelos europeus nos séculos e : A representação ocidental atual: 1 2 3 4 5 6 7 8 9 0 A representação árabe escrita da esquerda para a direita: ۱۲۳٤٥٦٧٨٩۰ Introdução à Programação: Conceitos e Práticas. Página 31 3. Conceito de Bases Em nosso cotidiano lidamos com números representados na base decimal. Isto se deve a razões e históricas, talvez fundamentadas na quantidade de dedos nas mãos. Porém, os computadores consolidaram o sistema binário como elemento de referência no projeto dos circuitos. A utilização da base proporciona grande facilidade na representação eletrônica e digital dos números e . A possibilidade de representar os dígitos binários através de valores de tensões ou correntes, por exemplo, e , simplifica a construção dos processadores e demais elementos de uma arquitetura. Combinado com este fato há toda uma álgebra desenvolvida no contexto da informação binária/digital, que inclui uma série de operações sobre dados binários/lógicos que serviu de base para a construção das principais funcionalidades computacionais (memória, circuitos aritméticos, ...). Um sistema de numeração é determinado fundamentalmente pela base, que é o número de símbolos utilizados. A base é o coeficiente que estabelece o valor de cada símbolo de acordo com a sua posição. Na notação posicional os dígitos assumem valores diferentes para cada posição que ocupa na representação do número. Tomando o ponto como separador para a parte inteira e a parte fracionária, temos que a cada posição que se desloca o dígito a esquerda do ponto o seu valor é multiplicado cumulativamente pela base. Da mesma forma, a cada posição que se desloca o dígito a direita do ponto o seu valor é dividido cumulativamente pela base. 3.1. Sistema Decimal O sistema decimal é o mais comumente utilizado nas operações do dia a dia. O conjunto de dígitos utilizado é: { } → Base → Símbolos. Por exemplo, partindo do número decimal , observa-se que cada dígito possui um valor diferente dependendo da posição que ocupa no número, a saber: Posição 2 Posição 1 Introdução à Programação: Conceitos e Práticas. Posição 0 Posição -1 Página 32 Esta característica é um dos pilares do sistema decimal utilizado, dando-lhe o aspecto , que atribui a cada posição do número um peso distinto. Decompondo em uma somatória, levando em conta o peso da posição, tem-se: As parcelas mostram claramente, através da , a contribuição de cada elemento sistema de numeração utilizado. sua forma geral do EXERCÍCIOS: Representar na forma de polinômio os seguintes números na base : i. ii. iii. 3.2. Sistema Hexadecimal O sistema hexadecimal tem todas as características do sistema decimal, porém utiliza símbolos para a representação dos dígitos. Logo: { Os símbolos utilizados são os com seus respectivos valores: } → Base → Símbolos. do sistema decimal, acrescidos dos seguintes símbolos, Símbolo Valor Absoluto EXERCÍCIOS: Representar na forma de polinômio os seguintes números na base : i. ii. iii. Introdução à Programação: Conceitos e Práticas. Página 33 3.3. Sistema Binário O sistema binário também tem todas as características do sistema decimal porém utilizando dois dígitos: { } → Base → Símbolos. É o sistema de numeração utilizado internamente pelos computadores modernos. Em informática, cada dígito de um número representado neste sistema é denominado de bit (binary digit). Além disso, alguns conjuntos destes bits recebem denominação específica, a saber: = conjunto de (exemplo: ); ( ) = conjunto de , ou mais bytes, que determina a capacidade de processamento do computador (exemplo: palavra = bits, palavra = , Arquitetura Pentium = ); = conjunto de (KBytes); = conjunto de (MBytes); = conjunto de : Quantities and units – Part 13: Information , foram estabelecidos os seguintes prefixos: De acordo com a norma science and technology, de Potência de 10 (GBytes); Potência de 2 Nome Símbolo Nome Símbolo Potência Valor quilo k kibi Ki 1024 mega M mebi Mi 1 048 576 giga G gibi Gi 1 073 741 824 tera T tebi Ti 1 099 511 627 776 peta P pebi Pi 1 125 899 906 842 624 exa E exbi Ei 1 152 921 504 606 846 976 zetta Z zebi Zi 1 180 591 620 717 411 303 424 yotta Y yobi Yi 1 208 925 819 614 629 174 706 176 Apesar de diferentes, é comum utilizar os termos das potências de potências de . Introdução à Programação: Conceitos e Práticas. para se referir às Página 34 EXERCÍCIOS: Representar na forma de polinômio os seguintes números na base : i. ii. iii. 3.4. Base Qualquer É possível generalizar os conceitos anteriores para números em qualquer base, bastando selecionar a quantidade de símbolos adequada, e seguir os princípios do sistema de numeração, cuja essência é a . Assim, uma base qualquer, pode ser estabelecida da seguinte forma: { } → Base representa o valor Denotando um número → Símbolos, onde o último símbolo . na base com seus dígitos, temos: , sendo a parte inteira com dígitos e a parte fracionária com . Expandindo na forma de polinômio, tem-se: ⋯ dígitos, sendo que ⋯ Ou, colocando este somatório, tem-se: sob a forma de ∑ Desta forma, temos todos os elementos para formar um sistema em qualquer base: os dígitos a base e as posições em . Por exemplo, para a , teria: { } → Base → Símbolos/Dígitos. Uma determinada quantidade pode ser representada por diferentes números em qualquer base. Quanto menor a base mais dígitos são necessários para representar a mesma quantidade. Introdução à Programação: Conceitos e Práticas. Página 35 3.5. Conversão entre Bases Uma determinada quantidade pode ser representada de forma distinta dependendo da base utilizada. Por exemplo, a quantidade pode ser representada na base como , enquanto que em outras bases sua representação será diferente. Mais especificamente, no mundo computacional é muito comum sermos requisitados a interpretar informações codificadas em binário ( ). Porém, estarmos habituados ao sistema decimal, o que frequentemente demandará o uso de metodos de conversão nos dois sentidos. Além disso, é fundamental que um profissional da área de computação ou engenharia relacionada, compreenda as transformações aplicadas a uma informação em todo o seu trajeto, desde a forma natural no mundo real até a completa representação no ambiente digital, tanto por software quanto pelo hardware. Desta forma, serão apresentados métodos para realizar as seguintes conversões de quantidades: i. ii. iii. 3.5.1. Conversão de Qualquer Base para a Base 10 Para converter de uma base qualquer para a base , basta aplicar o de um número na base : ∑ , e efetuar as operações tal como estamos habituados na base , e o resultado obtido será nesta base . Exemplos: O número com as posições dos dígitos: ( ) O número com as posições dos dígitos: ( O número com as posições dos dígitos: ( Introdução à Programação: Conceitos e Práticas. ) ) Página 36 É importante conhecer os limites estabelecidos em termos de quantidade de dígitos em uma determinada base. Por exemplo, sabemos que com três dígitos decimais é possível representar quantidades distintas, que vão do número ao número . Depreende-se que a maior quantidade representável é quando todas as posições forem ocupadas pelo dígito de maior valor. Assim, para o sistema binário e para o tamanho de um , a maior quantidade possível de ser representada é obtida quando todos os bits forem iguais a . A questão é: ―Quanto é este valor em decimal ?‖. A resposta vem da conversão deste binário para decimal, conforme segue: De uma forma geral, o equivalente decimal do maior número possível representado por bits é obtido através da fórmula: de ser ⋯ ⋯ Observa-se que temos o somatório de uma Progressão Geométrica de Para o caso em que (1 termos: ) temos: . Introdução à Programação: Conceitos e Práticas. Página 37 3.5.2. Conversão da Base 10 para Qualquer Base A conversão da base para uma base qualquer é feita de forma distinta para a Parte inteira e para a Parte fracionária: i. Parte inteira: Divide-se a parte inteira pela base desejada, obtendo-se assim um quociente e um resto. Repita esta divisão com o quociente, e assim sucessivamente com os demais quocientes até obter quociente zero. Agrupe os restos, conforme exemplo a seguir, para obter o número convertido para a base desejada, sabendo que o primeiro resto é o dígito menos significativo; Exemplos: 47 1 2 23 1 2 11 1 33 1 2 5 1 2 2 0 2 1 1 2 16 0 2 8 0 2 4 0 2 2 0 2 0 2 1 1 2 0 O dígito menos significativo de um número inteiro é aquele que ocupa a posição , sendo que o dígito mais significativo é aquele que ocupa a posição de maior ordem . Quando se trata de um número binário, os termos utilizado são e , ou seus equivalentes em inglês: e . Também podem ser encontrados os termos bit de mais baixa ou alta ordem. 235 3 8 29 5 8 3 3 2030 1 8 0 Introdução à Programação: Conceitos e Práticas. 16 126 14 16 7 7 16 0 Página 38 Vamos comprovar este processo utilizando a forma polinomial de representação do número inteiro. ⋯ Efetuando a divisão deste polinômio por , temos: ⋯ ⋯ ⋯ Esta primeira divisão de inteiros produziu como resto o dígito . O quociente obtido foi ⋯ , que é um polinômio que representa um número composto pelos demais dígitos, deslocados uma posição à direita . De fato, a divisão por faz com que todos os dígitos sejam deslocados uma posição à direita e elimina o dígito mais a direita. Assim, a lógica de fundo é uma sequência de deslocamentos à direita e a obtenção de um dígito através do resto. O término deste processo ocorre quando o quociente é zerado. Neste caso a próxima divisão seria: ⋯ ⋯ ii. ⋯ Parte fracionária Multiplica-se a parte fracionária pela base desejada, obtendo-se assim um número composto por uma parte inteira e uma parte fracionária. Repita este processo para esta parte fracionária, e assim sucessivamente até que o resultado obtido seja um inteiro ou quando a quantidade de dígitos desejada foi alcançada. Agrupe as partes inteiras, conforme exemplo a seguir, para obter o número convertido para a base desejada: Introdução à Programação: Conceitos e Práticas. Página 39 0.75 x 2 = 1.5 0.5 x 2 = 1.0 0.15 0. 0.60 0.20 0.40 0.80 0.60 0.20 0.40 0.80 x2= x2= x2= x2= x2= x2= x2= x2= x2= 2= 0.0378 0.6048 0.6768 0.8288 0.2608 0.1728 0.7648 0.2368 0.7888 0.6208 0.9328 0.924805 0.796875 0.75 0.44 0.52 0.16 0.28 0.24 0.92 0.36 0.88 0.04 0.32 0.3000 0.6000 1.2000 0.4000 0.8000 1.6000 1.2000 0.4000 0.8000 .6000 x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x8= x8= x8= x8= x8= x8= x8= x8= x8= x8= 0.6048 9.6768 10.8288 13.2608 4.1728 2.7648 12.2368 3.7888 12.6208 9.9328 14.9248 14.7969 12.7500 12.0000 3.5200 4.1600 1.2800 2.2400 1.9200 7.3600 2.8800 7.0400 0.3200 2.5600 ⋯ ⋯ Observe a dízima Podemos verificar facilmente este método de conversão partindo da expansão do número na forma de polinômio. ⋯ ⋯ Considerando somente a parte fracionária, temos: ⋯ Introdução à Programação: Conceitos e Práticas. Página 40 Multiplicando o polinômio pela base B, temos: ⋯ ⋯ = Podemos interpretar através da parcela que o dígito foi deslocado para a posição , ou seja, foi deslocado para assumir uma posição na parte inteira. De fato, a multiplicação por faz com que todos os dígitos sejam deslocados uma posição à esquerda. Logo, a lógica de fundo é uma sequência de deslocamentos à esquerda e a obtenção de um dígito por vez. Aplica-se novamente este processo para a nova parte fracionária . O término deste ciclo ocorre quando a parte fracionária é zerada , ou a quantidade de dígitos obtida já alcançou a precisão estabelecida. 3.6. Exercícios a) Converter para a base os seguintes números que estão na base informada: i. ii. iii. iv. v. vi. b) Converter para base informada os seguintes números que estão na base : i. ii. iii. iv. 3.6.1. Conversão da Base 2n para a Base 2 A conversão de uma base com potência inteira de através do uso de uma tabela. ( ) para a base pode ser feita Sem a tabela, a conversão demandaria as seguintes transformações: O uso da tabela evita os cálculos das duas conversões. Esta tabela de duas colunas pode ser facilmente montada, como segue: Introdução à Programação: Conceitos e Práticas. Página 41 i. ii. 3.6.2. Relacionar na primeira coluna os dígitos da base Escrever na segunda coluna o equivalente binário do valor de cada dígito utilizando ; Conversão da Base 4 para a Base 2 A tabela para a conversão da Base Ao converter um número na base respectiva combinação de bits. para a Base fica: para a base , basta substituir cada dígito pela Ao converter um número na base para a base , primeiro ajusta-se a quantidade de bits da parte inteira e parte fracionária para uma quantidade múltipla de , e em seguida substitui-se cada agrupamento de pelo equivalente dígito da base . Este ajuste não pode alterar o valor do número. Assim, quando necessário a parte inteira é complementada com zeros a esquerda, e a parte fracionária é ajustada com zeros a direita. Exemplo: Os agrupamentos e os complementos estão indicados em negrito. 3.6.3. Conversão da Base 8 para a Base 2 A tabela para a conversão da Base para a Base Introdução à Programação: Conceitos e Práticas. fica: Página 42 Ao converter um número na base respectiva combinação de bits. para a base , basta substituir cada dígito pela Ao converter um número na base para a base , tomar os mesmos cuidados anteriores e ajustar a quantidade de bits para um número múltiplo de . Exemplo: Os agrupamentos e os complementos estão indicados em negrito. 3.6.4. Conversão da Base 16 para a Base 2 A tabela para a conversão da Base Ao converter um número na base respectiva combinação de bits. para a Base para a base fica: , basta substituir os dígitos pela Ao converter um número na base para a base , tomar os mesmos cuidados anteriores e ajustar a quantidade de bits para um número múltiplo de . Exemplo: Introdução à Programação: Conceitos e Práticas. Página 43 A Conversão entre base duas tabelas, ou seja: 3.7. e base , por exemplo, pode ser feita pela aplicação de Exercícios 1. Converter para a base i. ii. iii. iv. v. vi. vii. viii. ix. x. xi. xii. xiii. ⋯ os seguintes números que estão na base informada: ⋯ ⋯ ⋯ 2. Converter para a base informada os seguintes números que estão na base : i. ii. iii. iv. v. 3. Converter os seguintes números: i. ii. Introdução à Programação: Conceitos e Práticas. Página 44 4. Operações com Números Binários O conjunto de dígitos binários combinado com as operações aritméticas dão forma ao sistema binário, cuja praticidade serviu de base para o surgimento da e dos circuitos digitais. O seguinte diagrama ilustra as contribuições fundamentais para o desenvolvimento da tecnologia digital. Figura 24: Circuitos Digitais: Contribuições 4.1. Operações Aritméticas Na sequência são apresentadas as quatro operações aritméticas aplicadas a números binários. O entendimento dos detalhes relativos a estas operações contribui para uma melhor compreensão do funcionamento dos computadores, uma vez que a aritmética binária é a base para a lógica digital. 4.1.1. Soma A soma de números binários é simplificada em relação à soma decimal, uma vez que é necessário conhecer as somas possíveis para dois dígitos binários. Porém se o resultado da soma de dois bits não couber em um bit, é produzido o bit de . Este é o caso da soma de com , que resulta em com . Pode ocorrer que somando com surja o bit da soma anterior, produzindo . Neste caso, o resultado em binário é , ou em decimal. A interpretação do é que o resultado é e ocorre o . Assim, têm-se as seguintes combinações: Introdução à Programação: Conceitos e Práticas. Página 45 Exemplos: 11 1 1 111010101 + 10010100 ---------1001101001 4.1.2. 469 148 --617 11111111 11111111 + 1 ---------100000000 255 1 --256 111111 10111100 + 1111111 ---------100111011 188 127 --315 1 11 10011001 + 10110100 ---------101001101 153 180 --333 Subtração A subtração de números binários é semelhante à soma binária, com atenção especial quando é necessário o empréstimo para habilitar a subtração. Assim, têm-se as seguintes combinações: – – – – -1 2 2 -1-1 0 - 1 --1 0 - 1 --0 Neste caso – não é possível, então empresta 1 da conta futura. Este empréstimo vale 2, o que gera , produzindo 1 como resultado. Neste caso – , tendo ainda que pagar um empréstimo anterior não é possível, então empresta 1 da conta futura. Este empréstimo vale 2, o que gera , produzindo 0 como resultado. Exemplos: - 111010101 10010100 ---------101000001 O símbolo 469 148 --321 11111111 11111111 1 ---------11111110 255 1 --254 222222 *******2 10111100 1111111 ---------00111101 188 127 --315 2 2 **2**2 10110100 - 10011001 ---------00011011 180 153 --27 indica que houve um empréstimo para viabilizar a subtração. Introdução à Programação: Conceitos e Práticas. Página 46 4.1.3. Multiplicação O produto de dois números binários também fica simplificado uma vez que a tabela de multiplicação dos dígitos se reduz a quatro combinações. A composição das parcelas é feita através da soma. As combinações possíveis são: Exemplos: 1101 13 11 3 ------- --1101 1101 -------- --100111 39 11010 10 ------00000 11010 -------110100 x x 26 2 --- x --52 1001 9 101 5 ------- --1001 0000 1001 -------- --101101 45 10111 100 ------00000 00000 10111 -------1011100 x 23 4 --- --92 Estes exemplos permitem constatar que a multiplicação binária é a soma de parcelas oriundas da multiplicação de um dígito do primeiro operando pelo segundo operando. Como o dígito será ou , logo as parcelas serão compostas ou por zero ou pelo segundo operando. Além disso, observa-se o deslocamento dos bits a cada parcela. Estas características reforça a simplificação proporcionada pela notação binária. 4.1.4. Divisão A divisão entre binários, apesar de não ser tão direta quanto à multiplicação, também é simplificada, uma vez que o quociente final é montado a partir de quocientes parciais que vão agregando e . Além disso, cada dígito do quociente inicia uma subtração para completar o cálculo. Desta forma, a divisão é executada através de uma série de subtrações. Observe o seguinte exemplo: -1+2 1 0 1 1 1 1 1 0 1 1 1 0 1 0 1 0 0 0 resto 0 1 1 1 1 1 quociente 22 21 1 resto 3 7 quociente 0 1 1 Introdução à Programação: Conceitos e Práticas. Página 47 O quadro da esquerda apresenta o processo de divisão binária e o quadro da direita mostra os equivalentes decimais. A divisão de por quantidade mínima de ̅̅̅̅̅ . inicia pelo agrupamento da esquerda para a direita de uma que seja divisível por , que no caso seriam três bits: 10110 11 10 11 1 A divisão prossegue abaixando o próximo dígito e verificando se o grupo é maior ou igual ao divisor. Não sendo, é abaixado o próximo dígito e adicionado o bit ao quociente. Segue outro exemplo: 101101 101 000101 101 0 101 1001 O resultado pode ser certificado pelo equivalente decimal, onde neste caso é dividido por , resultando em quociente e resto . 4.1.5. Exercícios 1. Somar os seguintes números em binário e hexadecimal: i. ii. iii. iv. v. vi. vii. 2. Subtrair os seguintes números binários: i. ii. iii. – – – Introdução à Programação: Conceitos e Práticas. Página 48 3. Multiplicar os seguintes números binários: i. ii. iii. 4. Dividir os seguintes números binários: i. ii. iii. 4.2. Operações Lógicas A ou combina uma série de operações, denominadas operações lógicas, aplicáveis ao conjunto de números { } produzindo como resultado ou . Constitui-se em um dos fundamentos mais importantes na construção de circuitos digitais e consequentemente da arquitetura de um computador. As operações lógicas podem ser implementadas em circuitos, denominados portas lógicas, que por sua vez podem ser combinados na construção de circuitos mais complexos, como circuito de soma, subtração dentre outros. Além disso, o conjunto { { }, { }, também pode ser mapeado como { }, { } ... }, Na sequência a descrição das operações lógicas. Introdução à Programação: Conceitos e Práticas. Página 49 4.2.1. NOT (NÃO) A operação requer apenas um argumento e produz um resultado. Esta operação retorna o complemento de (negação ou inversão) do bit de argumento. Circuito Equivalente A lâmpada acenderá ( chave estiver aberta ( Circuito Eletrônico ) quando a ). 𝑉𝑐𝑐 𝑋 𝐴̅ 𝐴 Símbolo para o Circuito Lógico Representação da Operação ̅ Tabela Verdade NOT 0 1 Introdução à Programação: Conceitos e Práticas. 1 0 Página 50 4.2.2. AND (E) A operação requer dois argumentos e produz um resultado. O resultado será quando as duas entradas forem , e para outras combinações. Circuito Equivalente A lâmpada chaves e acenderá ( ) quando as forem fechadas ( ). Circuito Eletrônico 𝑉𝑐𝑐 𝑋 𝑋 𝐴𝐵 ̅̅̅̅ 𝐴𝐵 𝐴 𝐵 Símbolo para o Circuito Lógico Introdução à Programação: Conceitos e Práticas. Página 51 Representação da Operação Tabela Verdade AND 0 0 0 1 0 1 0 1 Característica da Operação A representa todas as combinações da expressão lógica onde os valores de , e estão representados nas seguintes células: , Nesta representação podemos constatar que terá valor somente quando os valores de e também forem . As características da operação podem ser extraídas da tabela, observando o comportamento de . Na operação , quando tem valor , a linha correspondente em resulta inteira em , o que permite deduzir a característica . Esta expressão pode ser lida como: independente do valor do bit , quando fazemos um com , o resultado será sempre . Assim, o operador tem a característica de ser utilizado sempre que se precisa anular ( , , , ...) bits. Uma outra característica pode ser extraída analisando a linha de correspondente ao valor de igual . Neste caso, esta segunda linha de é idêntica à linha do argumento , o que permite deduzir a expressão , ou seja: independente do valor do bit , quando fazemos um com , o resultado será sempre o valor do próprio bit ( ). Observamos que a operação se assemelha à operação aritmética de multiplicação, o que lhe confere também a denominação de produto lógico. Introdução à Programação: Conceitos e Práticas. Página 52 Em muitas situações a tabela verdade pode ser construída no seguinte formato: AND A B 0 0 0 1 1 0 1 1 X 0 0 0 1 A coluna expressa os resultados da operação aplicada linha a linha para cada valor das colunas e . A tabela possui quatro linhas ( ) em virtude de estarmos trabalhando com a expressão lógica contendo dois ( ) argumentos ( e ). Neste caso, as colunas e representam todas as combinações de e possíveis para duas variáveis. Em uma expressão lógica envolvendo variáveis a tabela verdade conteria linhas, e assim por diante. Exemplo: Supor que temos um número inteiro com : , e desejamos obter um composto desta forma: , ou seja com os quatro bits altos iguais aos de e os quatro bits baixos zerados. Isto pode ser obtido fazendo a operação de com um número bit a bit, tal que o resultado produza a configuração desejada, ou seja: ou O número utilizado como argumento é normalmente chamado de Introdução à Programação: Conceitos e Práticas. ( ). Página 53 4.2.3. OR (OU) A operação ( ) requer dois argumentos e produz um resultado. O resultado será quando as duas entradas forem , e para outras combinações. Circuito Equivalente A lâmpada apagará ( e forem abertas ( Circuito Eletrônico ) quando as chaves ). 𝑉𝑐𝑐 𝑋 𝑋 𝐴 𝐴 𝐵 ̅̅̅̅̅̅̅̅ 𝐴 𝐵 𝐵 Símbolo para o Circuito Lógico Introdução à Programação: Conceitos e Práticas. Página 54 Representação da Operação Tabela Verdade OR 0 1 0 0 1 1 1 1 Característica da Operação Analisando a características. da operação podemos extrair suas principais Na operação , a primeira linha de , quando é igual a , é idêntica à linha do argumento , o que permite deduzir a expressão , ou seja: independente do valor do bit , quando fazemos um com , o resultado será sempre o valor do próprio bit ( ). Quando tem valor , a linha correspondente em resulta inteira em , o que permite deduzir a característica . Esta expressão pode ser lida como: independente do valor do bit , quando fazemos um com , o resultado será sempre . O operador tem a característica de ser utilizado quando que se precisa ( , , ...) bits. Exemplo: Supor que temos um número inteiro com : , e desejamos obter um composto desta forma: , ou seja com os quatro bits altos iguais aos de e os quatro bits baixos ligados. Isto pode ser obtido fazendo a operação de com um número bit a bit, tal que o resultado produza a configuração desejada, ou seja: ou Introdução à Programação: Conceitos e Práticas. Página 55 4.2.4. XOR / OU EXCLUSIVO A operação ( ) requer dois argumentos e produz um resultado. É semelhante ao , porém quando os dois argumentos forem o resultado será . O termo se refere ao fato que dois argumentos produz o resultado . Símbolo para o Circuito Lógico Representação da Operação Tabela Verdade XOR 0 0 0 1 1 Característica da Operação Analisando a características. 1 1 0 ̅ , onde ̅ significa a negação de da operação podemos extrair suas principais Na operação , a primeira linha de , quando é igual a , é idêntica à linha do argumento , o que permite deduzir a expressão , ou seja: independente do valor do bit , quando fazemos um com , o resultado será sempre o valor do próprio bit ( ). Quando tem valor , a linha correspondente em resulta em valores invertidos em ̅ . O operador relação ao argumento , o que permite deduzir a característica Introdução à Programação: Conceitos e Práticas. Página 56 tem a característica de ser utilizado quando se quer inverter ( o valor de um bit específico. , , ) Exemplo: Supor que temos um número inteiro com : , e ̅̅̅̅̅̅̅̅̅̅̅̅̅, ou seja com os desejamos obter um composto desta forma: quatro bits altos iguais aos de e os quatro bits baixos invertidos. Isto pode ser obtido fazendo a operação de A com um número bit a bit, tal que o resultado produza a configuração desejada, ou seja: ou A operação 4.2.5. i. pode ser utilizada para realizar uma criptografia básica. Efetuando , e em seguida calculando , produz-se novamente o valor de . Exemplos Montar a tabela verdade para a seguinte expressão lógica circuito lógico equivalente. . Desenhar o A tabela deverá considerar os valores possíves para , e e o resultado . A quantidade de linhas da tabela deve conter todas as combinações para os valores de , e . Como cada uma destas variáveis admitem os valores , logo as três variáveis produzirão ou linhas. Por conveniência podem ser adicionadas colunas com valores intermediários o que confere maior clareza na obtenção da coluna com o resultado final. Assim, a tabela final seria: 0 0 0 0 1 1 1 1 0 0 1 1 0 0 1 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 1 1 0 1 0 1 0 1 1 1 O circuito lógico equivalente é: Introdução à Programação: Conceitos e Práticas. Página 57 ii. Montar a tabela verdade para a seguinte expressão lógica circuito lógico equivalente. A tabela deverá considerar os valores possíves para linhas. Assim, a tabela final seria: 0 0 1 1 0 1 0 1 1 1 0 0 1 0 1 0 0 0 1 0 e 0 1 0 0 . Desenhar o e o resultado . Serão ou 0 1 1 0 Observe que o resultado de coincide com a tabela verdade do operador a operação entre e equivale a expressão lógica . . De fato O circuito lógico equivalente é: Uma notação alternativa mais compacta utiliza um círculo na entrada da porta principal para denotar a operação , conforme ilustra o seguinte diagrama: Introdução à Programação: Conceitos e Práticas. Página 58 4.2.6. Exercícios Montar a tabela verdade e o circuito lógico para cada uma das expressões abaixo: i. ii. iii. iv. v. vi. vii. viii. ix. x. xi. xii. xiii. xiv. xv. xvi. xvii. 4.3. ̅̅̅̅̅̅̅̅ Operações Aritméticas Binárias com Circuitos Lógicos Este tópico faz a correlação entre as operações de soma e subtração de binários com expressões lógicas e finalmente com circuitos lógicos. É importante destacar que este material apresenta uma breve introdução aos conceitos de circuitos lógicos e não se aprofunda nas questões relacionadas com redução de expressões. Introdução à Programação: Conceitos e Práticas. Página 59 4.3.1. Somador Completo É possível relacionar a operação de soma aritmética com as operações lógicas. Para isto, temos que montar uma tabela verdade completa para a soma de com e , produzindo como resultado e um novo . A tabela abaixo mostra todas as combinações para . A coluna resultado da soma e a coluna o resultante. 0 0 0 0 1 1 1 1 0 0 1 1 0 0 1 1 0 1 0 1 0 1 0 1 0 1 1 0 1 0 0 1 contém o 0 0 0 1 0 1 1 1 Apenas com os conhecimentos até o momento apresentados, é possível observar que a coluna pode ser obtida por . Já pode ser obtido por para as quatro primeiras linhas, quando ,e para as quatro últimas, quando . Combinando as duas equações para , tem-se que também pode ser escrita como . A seguinte tabela verdade mostra que o valor de é o mesmo para as duas expressões e idêntico aos valores obtidos na tabela verdade da soma binária completa: 0 0 0 0 1 1 1 1 0 0 1 1 0 0 1 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 0 1 1 1 0 0 0 1 0 1 1 1 Utilizando as portas lógicas é possível montar facilmente um circuito somador completo, que receba e produza , como na figura abaixo: Introdução à Programação: Conceitos e Práticas. Página 60 Figura 25: Circuito Somador Completo de 1 bit. Este circuito é capaz de realizar a soma de bit. Para somar um inteiro com mais bits, basta sequenciar vários somadores completos. Por exemplo, se o projeto da prevê a soma de dois inteiros de , então o circuito poderia ser similar á: Figura 26: Circuito da Soma de N bits. 4.3.2. Subtrator Completo A abordagem feita para o somador completo pode ser aplicada para o subtrator completo. Para isto, temos que montar uma Tabela Verdade completa para a subtração de por com eventual pagamento do , produzindo como resultado e um novo indicador de . Introdução à Programação: Conceitos e Práticas. Página 61 A tabela abaixo mostra todas as resultado da subtração 0 0 0 0 1 1 1 1 combinações para e a coluna o 0 0 1 1 0 0 1 1 0 1 0 1 0 1 0 1 0 1 1 0 1 0 0 1 Por exemplo, as linhas e com os valores entendidas como sendo as seguintes operações: . A coluna resultante. 0 1 0 0 1 1 0 1 e 2 -1 0 2 -1-1 0 - 1 --1 0 - 1 --0 Neste caso – não é possível, então empresta da conta futura. Este empréstimo vem somando , o que gera , produzindo como resultado. contém o para são Neste caso – , tendo ainda que pagar um empréstimo anterior não é possível, então empresta da conta futura. Este empréstimo vem somando , o que gera , produzindo como resultado. De modo similar à soma, é possível observar que a coluna pode ser obtida por . Já pode ser obtido por para as quatro primeiras linhas e para as quatro últimas. Combinando as duas equações para , temse . Também é possível obter para as quatro primeiras linhas e para as quatro últimas. Combinando as duas equações para , tem-se ( ). Esta última expressão pode ser trabalhada para reduzir o número de portas lógicas do circuito final e aproveitar uma operação do cálculo de e chega-se a ( ). Introdução à Programação: Conceitos e Práticas. Página 62 Um circuito lógico para o subtrator completo seria: Figura 27: Circuito Subtrator Completo de 1 bit. Introdução à Programação: Conceitos e Práticas. Página 63 5. Representação de Dados A figura abaixo ilustra a separação entre as informações que circulam no mundo real e seus equivalentes no mundo digital. Estão exemplificadas informações clássicas, tais como números e textos. Para entrar no mundo digital é fundamental que o dado exterior seja convertido para e através de algum método ou convenção. Figura 28: Mundo Real x Mundo Digital. Os métodos aqui apresentados serão agrupados conforme segue: Representação de Dados Números Inteiros Reais Inteiro sem sinal Binário Puro Textos Inteiro com sinal MóduloSinal Complemento de 1 Complemento de 2 Excesso de 2n-1 Figura 29: Métodos de Representação de Dados Introdução à Programação: Conceitos e Práticas. Página 64 Na representação de dados no mundo digital é necessário definir uma quantidade de bits disponíveis para o enquadramento do dado. No caso de números inteiros, normalmente as arquiteturas e as linguagens disponibilizam os tamanhos com e . Para os exemplos que seguem adotaremos o tamanho de , até que se informe o contrário. 5.1. Representação de Números Inteiros Na sequência são apresentados os métodos mais referenciados para representação de números inteiros. Inicialmente será considerado tamanho . 5.1.1. Inteiros Sem Sinal – Binário Puro A representação de inteiros sem sinal é simplesmente a conversão do número desejado para no tamanho estabelecido. Exemplo Representação 00001010 01011101 01111111 00000000 11111111 100000000 Não foi possível representar em 8 bits. Assim, a faixa de representação é dada por: 5.1.2. Inteiro Com Sinal A representação de inteiros com sinal demanda algum artifício para codificar o sinal no padrão digital. Na sequência são apresentados os quatro métodos mais conhecidos. 5.1.2.1. Módulo Sinal O método ( ) utiliza o bit mais significativo para codificar o sinal do número e os demais bits codificam a magnitude do número em binário. Introdução à Programação: Conceitos e Práticas. Página 65 Figura 30: Representação em Módulo-Sinal Exemplo Representação 00001010 10001010 01111111 00000000 10000000 010000000 Não foi possível representar em 8 bits. 110000000 Não foi possível representar em 8 bits. 11111111 01100100 Assim, a faixa de representação é dada por: 5.1.2.2. Complemento de 1 O método consiste na conversão do valor absoluto (magnitude) de para binário. Se o número for negativo, segue-se com a inversão dos bits (toma-se o complemento do que falta para que cada bit de seja ). Introdução à Programação: Conceitos e Práticas. Página 66 Exemplo Representação 00001010 : 00001010 C-1: 11110101 Ok 01111111 00000000 : 00000000 C-1: 11111111 Ok 10000000 Sinal inconsistente. Não foi possível representar em 8 bits. : 10000000 C-1: 01111111 Sinal inconsistente. Não foi possível representar em 8 bits. : 01111111 C-1: 10000000 Ok 01100100 Assim, a faixa de representação é dada por: 5.1.2.3. Complemento de 2 O método consiste na conversão do valor absoluto de para binário. Se o número for negativo, segue-se com a inversão dos bits (toma-se o complemento do que falta para que cada bit de seja ) e soma-se a este .O último é desprezado. Introdução à Programação: Conceitos e Práticas. Página 67 Exemplo Representação 00001010 : C-1: + C-2: 01111111 00000000 : C-1: 00001010 11110101 1 -------11110110 Ok 00000000 11111111 + 1 -------C-2: 00000000 Ok 10000000 Sinal inconsistente. Não foi possível representar em 8 bits. : 10000000 C-1: 01111111 + 1 -------C-2: 10000000 Ok : 01111111 C-1: 10000000 + 1 -------C-2: 10000001 Ok 01100100 Assim, a faixa de representação é dada por: O método proporciona a mesma representação para o 5.1.2.4. Excesso de 2N-1 e . O método de Excesso de consiste em somar a o valor de , e então codificar esta soma em binário. Se o resultado desta soma for negativo ou exceder a quantidade de bits permitida, então o valor de estará fora da faixa para os definidos para a representação. Introdução à Programação: Conceitos e Práticas. Página 68 , verificar coerência do sinal O método também proporciona a mesma representação para o e . A notação de sinal na representação final é invertida em relação aos demais métodos. Exemplo Representação 10001010 Ok 01110110 Ok 11111111 Ok : 10000000 Ok : 10000000 Ok : 100000000 Não foi possível representar em 8 bits. : 00000000 Ok : 00000001 Ok : 11100100 Ok : : : Assim, a faixa de representação é dada por: 5.1.2.5. Visualização Podemos facilmente visualizar a relação entre um número do mundo real e sua representação no mundo digital. O quadro abaixo ilustra uma sala com várias cadeiras, onde as cadeiras são numeradas em binário com . Inteiro sem sinal Binário Puro 0 00..00 1 00.01 ... ... 127 01..11 128 10..00 129 10.01 ... ... 255 11..11 Introdução à Programação: Conceitos e Práticas. Página 69 Do lado do mundo real uma determinada família de números quer adentrar ao mundo digital e ocupar uma cadeira. A primeira restrição é o tamanho da sala. Não há cadeiras para todos os elementos da família, apenas alguns poderão ocupar um assento. No caso da família dos , a sala típica com cadeiras numeradas com , estarão disponíveis cadeiras, desde a cadeira até a .O de valor ocupará a cadeira , o valor ocupará a cadeira , e assim por diante até o valor que ocupará a cadeira . O protocolo de conversão determina qual a cadeira a ser ocupada por um determinado valor. No caso dos vimos que o método utilizado para conversão é o . As características da sala permitem que apenas os valores definidos por , poderão ser representados. Caso seja necessário abranger uma faixa maior, deverá ser utilizada uma sala com cadeiras maiores, ou seja com maior quantidade de bits. Para a família dos conversão: , temos uma sala para cada protocolo de , e , Inteiro com sinal Módulo-Sinal Complemento de 1 +0 00..00 +1 00.01 ... ... +127 01..11 +0 00..00 +1 00.01 ... ... +127 01..11 -0 10..00 -1 10.01 ... ... -127 11..11 -127 10..00 -126 10.01 ... ... -0 11..11 Complemento de 2 Excesso ±0 00..00 +1 00.01 ... ... +127 01..11 -128 00..00 -127 00.01 ... ... -1 01..11 -128 10..00 -127 10.01 ... ... -1 11..11 ±0 10..00 +1 10.01 ... ... +127 11..11 Nas salas do e do podemos visualizar que o número , tanto o quanto o , ocupam distintas cadeiras, o que caracteriza uma desvantagem do método, pois uma destas cadeiras poderia ser ocupada por mais um elemento dos . Nas salas do e do constatamos que o , quer seja o ou o , são levados, como consequência do método, para a mesma cadeira, o que acaba liberando um assento para o . Introdução à Programação: Conceitos e Práticas. Página 70 5.1.3. Soma em Complemento de 2 A representação em traz uma grande vantagem para a operação de soma, pois trata de forma transparente os inteiros sem sinal e com sinal. Tomemos como exemplo a seguinte soma de binários, para um Exemplo 1111 00001111 + 10001001 -------10011000 Inteiro sem Sinal Binário Puro 15 + 137 ----152 Inteiro com Sinal Complemento de 2 C-2 10001001 1 -------C-1 10001000 |X| 01110111 X = -119 C-2 10011000 1 -------C-1 10010111 |X| 01101000 X = -104 15 + -119 -----104 O que se observa é que independente da interpretação dada ao binário, se inteiro sem sinal ou inteiro com sinal em , o resultado é coerente. Na sequência são apresentados exemplos com algumas incoerências nos resultados das somas de inteiros sem sinal e nas somas de inteiros com sinal. Pode ser observada facilmente a lógica destas incoerências, a saber: Quando somamos dois inteiros sem sinal e ocorre o no último bit então estamos diante de um , ou estouro de capacidade. Quando somamos dois inteiros com sinal deve ser observada a coerência dos sinais: Soma de positivos deve gerar resultado positivo e soma de negativos deve gerar resultado negativo. Exemplo 1111 01111001 + 01101010 -------11100011 Inteiro sem Sinal Binário Puro 121 + 106 ----227 (ok) Introdução à Programação: Conceitos e Práticas. Inteiro com Sinal Complemento de 2 121 + 106 ------29 C-2 11100011 1 -------C-1 11100010 |X| 00011101 X = -29 (overflow) Página 71 11 1 11001001 + 11101110 -------10110111 201 + 238 ----183 -55 + -18 ------73 (overflow) 1 11 10000101 + 10001110 -------00010011 133 + 142 ----19 (ok) -123 + -114 -----+ 19 (overflow) (overflow) C-2 11001001 1 -------C-1 11001000 |X| 00110111 X = -55 C-2 11101110 1 -------C-1 11101101 |X| 00010010 X = -18 C-2 10110111 1 -------C-1 10110110 |X| 01001001 X = -73 C-2 10000101 1 -------C-1 10000100 |X| 01111011 X = -123 C-2 10001110 1 -------C-1 10001101 |X| 01110010 X = -114 5.1.4. Exercícios 1. Obtenha os valores de X que a sequência de binário representa para cada um dos métodos: BP 0000 1000 1111 0111 1000 1111 MS C–1 C–2 1111 1101 0000 1111 0000 1111 Introdução à Programação: Conceitos e Práticas. Página 72 5.2. Representação de Números Reais A representação de números reais é mais complexa e exige a codificação de vários componentes de . Os passos para se chegar a representação digital incluem: i. ii. iii. Converter para a base Normalizar Enquadrar Exemplo: i. Converter para a base 101110. ii. 0.32 0.12 0.92 0.72 0.52 x16 = 5.12 x16 = 1.92 x16 = 14.72 x16 = 11.52 x16 = 8.32 Normalizar Normalizar equivale a reescrever o número binário na notação científica, reposicionando o ―ponto decimal‖ a frente do primeiro dígito e fazendo a devida compensação multiplicando-se o binário por , onde é o número de casas deslocadas, sendo quando para esquerda e quando para a direita. Assim, tem-se a seguinte forma geral para um número normalizado: , onde: , com exceção quando Introdução à Programação: Conceitos e Práticas. . Todas as partes serão . Página 73 será um inteiro com sinal. Os componentes que devem ser representados são o iii. eo . Enquadrar Agora, resta enquadrar as partes de em um determinado padrão codificado em binário. Para os nossos exemplos, até que se coloque em contrário, adotaremos o seguinte padrão para números reais: Serão 24 bits assim divididos: S Expoente ( ) Mantissa ( o O bit de Sinal será para positivo ou ) para negativo o Os bits do campo Expoente acomodarão a codificação de como inteiro com sinal em algum dos métodos apresentados. Normalmente utiliza-se ou ; Nos exemplos que seguem utilizaremos . o O campo Mantissa deverá acomodar os decimal‖ da notação normalizada. S 0 0 0 Expoente ( ) 0 0 1 1 0 1 0 o O Sinal ficou com , pois o O Expoente ficou 1 1 1 0 bits que veem após o ―ponto Mantissa ( ) 0 1 0 1 0 0 0 1 1 1 é positivo; , pois o A Mantissa ficou pela notação. , e a codificação é em , pois são os ; bits comportados Exemplo: i. Converter para a base 0.004600000 0.073600000 0.177600000 0.841600000 Introdução à Programação: Conceitos e Práticas. x 16 = x 16 = x 16 = x 16 = 0.073600000 1.177600000 2.841600000 13.465600000 Página 74 0.465600000 x 16 = 0.449600000 x 16 = 7.449600000 7.193600000 ⋯ ii. Normalizar iii. Enquadrar Agora, resta enquadrar as partes de S 1 1 1 Expoente (7) 1 1 0 0 1 1 0 no padrão estabelecido em binário. 0 1 0 1 Mantissa (16) 1 0 1 0 1 1 1 0 1 1 Onde a parte do Expoente foi obtida da seguinte forma: BP: 0000111 C-1: 1111000 + 1 -------C-2: 1111001 Ok Ou simplesmente: No caso de representação de números reais, é importante conhecer a capacidade da notação, principalmente as características de amplitude e precisão. A estrutura do exemplo não é capaz de representar todos os números reais com precisão infinita. Logo, a técnica utilizada mapeia apenas um subconjunto dos números reais. Considere a reta dos reais abaixo indicada: Introdução à Programação: Conceitos e Práticas. Página 75 Figura 31: Faixa de Representação de Números Reais. Nem todos os pontos das retas dos reais são representados. Neste caso interessa conhecer os maiores e os menores números representáveis. Estes limites são calculados a partir da equação normalizada: O Maior Positivo ( ) é obtido com o produto da maior Mantissa ( ⋯ ) com o maior valor de , onde e a representação em . Neste caso temos a seguinte faixa para o Expoente ( ): . Assim, tem-se: Como: Logo: O Menor Positivo ( ) é obtido com o produto da menor Mantissa ( valor de . Assim, tem-se: ) com o menor Logo: Introdução à Programação: Conceitos e Práticas. Página 76 5.2.1. Formato IEEE 754 O IEEE padronizou algumas representações de números reais na norma IEEE (IEC ). A maioria dos computadores modernos adota este padrão. O seguinte quadro relaciona os tipos disponibilizados nesta norma: Tipo Half Single Double Double extended (80-bit) Quad Detalhando o tipo S Sinal Expoente Mantissa Total bits 1 1 1 5 8 11 10 23 52 16 32 64 15 127 1023 11 24 53 Dígitos Significativos Decimal ~3.3 ~7.2 ~15.9 1 15 64 80 16383 64 ~19.2 1 15 112 128 16383 113 ~34.0 Expoente bias Bits Precisão , tem-se a seguinte estrutura: Expoente ( ) o O bit de Sinal é Mantissa ( para positivo ou para ) negativo o Os do campo acomodam a codificação de como inteiro com sinal utilizando um método semelhante ao , porém com um de , que para é . A faixa de representação é de . o O campo acomoda , porém com um diferencial. Quando se normaliza um número binário, a mantissa sempre iniciará com o dígito , com exceção para o número zero. Assim, este dígito fica implícito, sem a necessidade de ocupar fisicamente um posição na notação. Logo, os representam uma precisão real de . Exemplo: i. Converter X para a base 2 11. 0.141592654 0.265482457 0.247719319 0.963509104 0.416145661 Introdução à Programação: Conceitos e Práticas. x 16 = x 16 = x 16 = x 16 = x 16 = 2.265482457 4.247719319 3.963509104 15.416145661 6.658330571 Página 77 0.658330571 0.533289135 0.532626152 0.522018433 x 16 = x 16 = x 16 = x 16 = 10.533289135 8.532626152 8.522018433 8.352294922 ⋯ ⋯ ii. Normalizar Observe que o primeiro bit da mantissa reposicionado na parte inteira ficará oculto na representação. iii. Enquadrar Agora, resta enquadrar as partes de X no padrão binário IEEE. Onde a parte do Expoente foi obtida da seguinte forma: , em Excesso com bias de Ou simplesmente: 5.2.2. Exercícios i. Representar em números reais os seguintes números: – Introdução à Programação: Conceitos e Práticas. Página 78 – ii. Que números estão representados pelas seguintes codificações binárias: Introdução à Programação: Conceitos e Práticas. Página 79 5.3. Representação de Informações Textuais A representação de informações textuais é feita através de um acordo que convenciona atribuir para cada caractere um código numérico. A convenção mais aceita é dada por uma tabela denominada Tabela (acrônimo para American Standard Code for Information Interchange, ou em português "Código Padrão Americano para o Intercâmbio de Informação"). Desenvolvida a partir de , grande parte das codificações de caracteres modernas tomam esta tabela como base. Caracteres não imprimíveis ( ): Representados como a parte não imprimível da tabela , os caracteres de controle tiveram sua origem nos primórdios da computação, quando se usavam máquinas teletipo e fitas de papel perfurado. Binário 0000 0000 0000 0001 0000 0010 0000 0011 0000 0100 0000 0101 0000 0110 0000 0111 0000 1000 0000 1001 0000 1010 0000 1011 0000 1100 0000 1101 0000 1110 0000 1111 0001 0000 0001 0001 0001 0010 0001 0011 0001 0100 0001 0101 0001 0110 0001 0111 0001 1000 0001 1001 0001 1010 0001 1011 0001 1100 0001 1101 Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Hexa 0 1 2 3 4 5 6 7 8 9 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D Controle ^@ ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K ^L ^M ^N ^O ^P ^Q ^R ^S ^T ^U ^V ^W ^X ^Y ^Z ^[ ^\ ^] Abreviação NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS Introdução à Programação: Conceitos e Práticas. Descrição Null Nulo Start of Header Início do cabeçalho Start of Text Início do texto End of Text Fim do texto End of Tape Fim de fita Enquire Interroga Term ID Acknowledge Reconhecimento Bell Campainha Back-space Espaço atrás Horizontal Tab Tabulação horizontal Line-Feed Alimenta linha Vertical Tabulation Tabulação vertical Form-Feed Alimenta formulário Carriage-Return Enter Shift-Out Saída do shift Shift-In Entrada no shift Data-Link Escape Device-Control 1 Device-Control 2 Device-Control 3 Device-Control 4 Neg-Acknowledge Não-reconhecimento Synchronous Idle End-of-Transmission Cancel End-Of-Medium Substitute Escape File Separator Group Separator Página 80 0001 1110 0001 1111 0111 1111 30 31 127 1E 1F 7F ^^ ^_ ^? Caracteres não imprimíveis ( Estendidos ( ): 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Caracter 0 1 2 3 4 5 6 7 8 9 A B C D E F RS US DEL Record Separator Unit Separator Delete ), Caracteres imprimíveis ( ) e Caracteres 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 A B C D E F BEL BS HT LF VT FF CR SO SI ETB CAN EM SUB ESC FS GS RS US ' 7 G W g w ç ù º À Ã Î þ ¸ ) 9 I Y i y ë Ö ® ╣ ╔ ┘ Ú ¨ * : J Z j z è Ü ¬ ║ ╩ ┌ Û · + ; K [ k { ï ø ½ ╗ ╦ █ Ù ¹ , < L \ l | î £ ¼ ╝ ╠ ▄ ý ³ = M ] m } ì Ø ¡ ¢ ═ ¦ Ý ² . > N ^ n ~ Ä × « ¥ ╬ Ì ¯ ▔ / ? O _ o • Å ƒ » ┐ ¤ ▀ ´ NUL SOH STX DLE 0 @ P ` p Ç É á ░ └ ð Ó ETX EOT ENQ ACK DC1 DC2 DC3 DC4 NAK SYN ! 1 A Q a q ü æ í ▒ ┴ Ð ß ± " 2 B R b r é Æ ó ▓ ┬ Ê Ô ‗ # 3 C S c s â ô ú │ ├ Ë Ò ¾ $ 4 D T d t ä ö ñ ┤ ─ È õ ¶ % 5 E U e u à ò Ñ Á ┼ ı Õ § & 6 F V f v å û ª Â ã Í µ ÷ ( 8 H X h x ê ÿ ¿ © ╚ Ï Þ ° . Caracter . Exemplo: O texto seria codificado como uma sequência de bytes equivalentes aos seguintes códigos: J o a o d a 74 111 97 111 32 100 97 Introdução à Programação: Conceitos e Práticas. 32 S i l v a 83 105 108 118 97 Página 81 Exemplo: O texto equivalentes aos seguintes códigos: A v B r a s I l , seria codificado com os binários 3 2 2 . A p t º 1 0 65 118 32 66 114 97 115 73 108 44 32 51 50 50 46 32 65 112 116 186 32 49 48 6 54 67 Os caracteres estendidos podem ser diferentes dependendo da fonte. Neste último exemplo, os códigos foram obtidos através de um recurso da planilha Excel, o que justifica algumas diferenças em relação à tabela acima. Introdução à Programação: Conceitos e Práticas. C Página 82 6. Introdução a Arquitetura A arquitetura de um computador é um modelo da organização e funcionamento de um sistema de processamento. A descrição da arquitetura destaca as funções dos componentes básicos de um computador, a interconexão destes componentes e o modo como os componentes interagem. A maioria das arquiteturas está baseada na proposta de : Figura 32: Organização típica de um computador. A dinâmica da execução de um programa inicia quando o usuário submete um determinado arquivo executável para ser processado pelo computador. Inicialmente este programa está contido em um arquivo e o primeiro passo é carrega-lo para a memória principal ( – ) onde ocupará um espaço designado pelo Sistema Operacional. Neste espaço, uma área conterá os bytes correspondentes às instruções do programa e outra área é reservada ao armazenamento de resultados provenientes da execução destas instruções. Esta última área também é denominada área de dados. A seguinte figura ilustra de forma simplificada o uso da memória por um programa em execução: Introdução à Programação: Conceitos e Práticas. Página 83 INSTRUÇÕES (código) DADOS RAM A: B: C: X: Y: Z: T: mov add mov mov mov sub mov add mov eax, A eax, B C, eax ebx, eax eax, X eax, Y Z, eax ebx, eax T, eax 1111 1110 1101 1100 1011 1010 1001 1000 0111 0110 0101 0100 0011 0010 0001 0000 Figura 33: Programa na Memória RAM: Código + Dados. A execução do programa inicia pelo processamento da instrução que estiver no endereço de memória designado como início do código. A primeira instrução é então levada da memória para a onde é tratada e processada, e quando for o caso, um resultado é armazenado na memória ou na própria . Em seguida o mesmo tratamento é dado para a segunda instrução e assim até o término do programa, quando o mesmo é liberado da memória. A gestão deste ambiente é feita pelo Sistema Operacional, enquanto que a é responsável pelo ciclo de execução da instrução. Observa-se que durante a execução de um programa há um forte trânsito de dados entre a e a memória principal ( ). A trabalha em uma velocidade muito superior a velocidade da memória RAM. Assim, durante a execução do programa a passa muito tempo aguardando os bytes chegarem da/na , o que acarreta desperdício deste recurso precioso que é a . Este limitante onde é comumente chamado de Gargalo de Von Newmann. Segue uma descrição resumida dos elementos que formam a arquitetura apresentada: Processador ( ) é o componente ativo, controlador de todo o sistema. É o processador que realiza todas as operações sobre os dados, de acordo com o indicado pelas instruções no código do programa. memória principal ( ) armazena as instruções que são executadas pelo processador, e os dados que serão manipulados. interfaces de entrada e de saída ( ) são as portas de comunicação para o mundo externo, às quais estão conectados os dispositivos periféricos tais como vídeo, teclado, discos e impressora. Introdução à Programação: Conceitos e Práticas. Página 84 Estes componentes estão interconectados por meio de um , através do qual o processador realiza o acesso a instruções e dados armazenados na memória principal. É também através deste mesmo barramento que o processador recebe ou envia dados de ou para as interfaces de entrada/saída. Instruções e dados estão armazenados em principal. 6.1. , na memória Processador - CPU A estrutura básica de um processador é composta normalmente por uma e uma . A figura a seguir ilustra uma – Central Processing Unit – simplificada: Figura 34: Unidade Central de Processamento típica. A pode ser vista como uma fábrica que processa matéria prima gerando produtos. Neste cenário a é a responsável pelo gerenciamento da fábrica, incluindo a entrada da matéria prima, a fabricação do produto e a entrega deste produto. Cada instrução recebida pela se assemelha a chegada de uma ordem de serviço para fabricação de um produto. Inicialmente o gerente da fábrica busca compreender a instrução de fabricação do produto, partindo em seguida Introdução à Programação: Conceitos e Práticas. Página 85 para o preparo da linha de produção para a fabricação, e uma vez pronto, o produto é encaminhado para o depósito, de onde tomará seu destino final. 6.1.1. Seção de Controle As operações básicas que ocorrem dentro da seção de processamento são todas comandadas pela unidade de controle. Ao efetuar a busca da instrução, a unidade de controle interpreta a instrução de modo a identificar quais as operações básicas que devem ser realizadas, bem como ativa os sinais de controle que fazem uma operação básica de fato acontecer. Os sinais de controle que são ativados, bem como a sequência com que são ativados, dependem de cada instrução em particular. Assim, a execução de uma instrução envolve a realização de uma sequência de passos, que podemos chamar de . Em geral, a execução de uma instrução envolve quatro passos, como mostra a seguinte: busca decodificação execução resultado Figura 35: Passos na execução de uma instrução. No primeiro passo, denominado busca ( ), o processador realiza o acesso ao código binário da instrução, armazenado na memória principal. A etapa seguinte é a decodificação ( ) da instrução, na qual as informações contidas no código da instrução são interpretadas. No terceiro passo, denominado execução, no qual a operação indicada pela instrução (por exemplo, uma operação na ) é efetuada. Finalmente no quarto passo, chamado resultado ( ), é armazenado em um registrador ou na memória o resultado produzido pela instrução. Cada passo de execução envolve a realização de várias operações básicas. transferência de dados entre os registradores e a ; transferência de dados entre os registradores; transferência de dados entre os registradores e a memória; operações aritméticas e lógicas realizadas pela . Introdução à Programação: Conceitos e Práticas. Página 86 6.1.2. Seção de Processamento A seção de processamento é formada basicamente pela unidade lógica e aritmética ( ) e por diversos registradores. A realiza as operações aritméticas, tais como adição e subtração, e operações lógicas, tais como . Os registradores são utilizados para armazenar informações internamente no processador. Um registrador pode ser utilizado tanto para acesso de leitura quanto para acesso de escrita. Os diversos registradores possuem um uso bem definido dentro da arquitetura, e de uma maneira geral podem ser classificados em três tipos: registradores de uso geral, registradores de uso específico e registradores auxiliares. Os registradores de uso geral normalmente são usados para armazenar dados que serão processados pela , bem como resultados produzidos pela . O registrador de estado (status register) associado à é um registrador de uso específico, e contém informações sobre o resultado produzido pela . Este registrador possui bits sinalizadores que são ativados ou desativados3 de acordo com o tipo de resultado produzido pela ( ...). Um outro exemplo de registrador de uso específico é o contador de programa ( ), cuja função é guardar o endereço da locação de memória onde se encontra a próxima instrução a ser executada pelo processador. 6.1.3. Barramentos A interligação entre a e os registradores é feita através de barramentos internos. Os barramentos internos permitem a transferência de dados dos registradores para a e permite a transferência do resultado produzido pela , de um registrador temporário, para outro registrador. Uma arquitetura de processador é uma arquitetura de n bits quando todas as operações da podem ser realizadas sobre operandos de até . 3 Considera-se aqui que um bit é ativado quando ele recebe o valor lógico 1, sendo desativado ao receber o valor lógico 0. Introdução à Programação: Conceitos e Práticas. Página 87 Normalmente, em uma arquitetura de os registradores de dados e os barramentos internos também são de , de forma a permitir que os dados sejam armazenados e transferidos de forma eficiente. A arquitetura Intel, desde o Barramento de Dados ( Barramento de Controle ( até o Pentium, inclui três barramentos distintos: ), Barramento de Endereço ( ) e ). Barramento de Dados: por onde trafega informação qualificada como dado; Barramento de Endereço: trafega informação qualificada como endereço; O movimento de dados é uma das instruções mais utilizadas em um programa. Equivale ao comando de atribuição em uma linguagem de alto nível, como Pascal e C. É comum trazer dados da memória para a e vice-versa. Exemplo: Supondo que queiramos executar a seguinte instrução: [ Que corresponde a guardar o número seguintes passos serão realizados: i. ii. iii. iv. v. ] na célula de memória do endereço . Os Colocar no barramento de endereço; Colocar no barramento de dados; Ativar o sinal no barramento de controle; Aguardar enquanto o controlador da memória armazena o dado na célula de indicada; Desativar o sinal no barramento de controle; Esta sequência culmina com uma ordem ao controlador da memória em pegar estas informações dos barramentos e efetuar a operação de gravar na memória. De modo semelhante, a seguinte instrução: [ ], que determina que a informação contida no endereço da memória seja copiada para um registrador, iniciará a seguinte sequência de utilização dos barramentos: i. ii. iii. iv. v. Colocar no barramento de endereço; Ativar o sinal no barramento de controle; O controlador da memória lê este pedido e copia a informação solicitada para o barramento de dados; Transferir o número que está no barramento de dados para o Registrador; Desativar o sinal no barramento de controle Introdução à Programação: Conceitos e Práticas. Página 88 O tamanho em bits dos barramentos de dados e de endereço influenciam diferentes aspectos da arquitetura. O tamanho do barramento de dados está ligado diretamente a capacidade de transferência de informações entre partes distintas da e entre a e outros componentes do sistema de processamento, como a memória . Por exemplo, se deseja trazer da memória para um registrador da , e o barramento de dados comporta simultâneos, então este trabalho será feito em duas instruções, Caso este mesmo barramento comportasse simultâneos o trabalho seria feito em uma instrução. O tamanho do barramento de endereços influencia diretamente na capacidade de endereçamento físico de memória . Uma analogia pode ser feita com os Códigos de Endereçamento Postal (CEP). Se cada cidade tivesse um CEP com apenas dígitos decimais, então este sistema permitiria endereçar até cidades ( ). Quando relacionamos cidades com células ( ) de memória e o CEP com a informação colocada no barramento de endereços identificando um destino na memória, logo vemos que quanto mais bits estiverem disponíveis no barramento de endereços mais bytes de memória poderão ser endereçados. Por exemplo: se tivermos de tamanho do barramento de endereços, então a capacidade de endereçamento será de células (bytes). O endereço da primeira célula seria e da última . Na arquitetura Intel o tamanho dos barramentos evoluiu da seguinte forma: Processador 8086 80286 80386 80486 Pentium 4 Barramento de Endereço Tamanho Quantidade de Bytes de Memória 20 24 32 32 32/36/644 Primeiras implementações utilizam http://en.wikipedia.org/wiki/X86-64 parte dos Introdução à Programação: Conceitos e Práticas. 64 bits. O Tamanho dos Registradores Barramento de Dados 16 16 32 32 32/64 16 16 32 32 32/64 uso integral permitiria 16 EBytes, Página 89 6.1.4. O sinal de Clock Para atender as relações de tempo requeridas na ativação dos sinais de controle, a unidade de controle opera em sincronismo com um sinal de . 1 ciclo clock leitura decodificação execução resultado Figura 36: A sincronização da execução de uma instrução com o sinal de clock. A execução de uma instrução consome certo número de ciclos de . O número de ciclos de por instrução não é o mesmo para todas as instruções, já que cada instrução pode envolver um número diferente de operações básicas em cada passo de execução. O tamanho do ciclo de é um dos fatores que determinam diretamente o desempenho de um processador. Quanto menor o tamanho do ciclo de , menor será o tempo de execução das instruções, e assim maior será o número de instruções executadas por unidade de tempo. Em um processador com frequência de clock de , o período será: A frequência de clock dos processadores Intel era de processadores chegando a no . 6.1.5. nos primeiros Organização do Computador A busca por melhor desempenho impulsionou não só o desenvolvimento de processadores com melhores características de frequência de , tamanho de barramento, tamanho de registradores e quantidade de memória, mas também levou a pesquisa de novos elementos de arquitetura que pudessem impactar no desempenho do processador e na sua interação com a memória principal. Segue um resumo das principais técnicas. Introdução à Programação: Conceitos e Práticas. Página 90 6.1.5.1. Pipeline Uma destas técnicas foi inspirada no conceito da linha de produção. Foi visto que o processamento de uma instrução na passa pelas etapas de: , , e ; ou Busca, Decodificação, Execução e Resultado. Assim, uma instrução quando trazida da memória para a ocupava a até o término da sua execução. Então, os projetistas trabalharam no redesenho da de tal forma que os circuitos fossem reagrupados em partes independentes, uma para cada etapa do ciclo de execução. Esta técnica, inspirada na linha de produção, foi denominada de . O seguinte diagrama ilustra o processamento de instruções na CPU com pipeline. Figura 37: Execução e Pipeline. Inicialmente uma instrução ocupa a unidade de Busca, e na sequencia a unidade de Decodificação, liberando o estágio anterior, que pode ser ocupada pela instrução . Enquanto avançam de estágio novas instruções podem ir ocupando os estágios liberados. Neste exemplo, vemos que no tempo quatro instruções ocupam a , melhorando sensivelmente o desempenho do processamento. A pipeline perde desempenho quando processa instrução de desvio, e somente terá a confirmação que o desvio será feito no estágio de execução, e as instruções anteriormente trabalhadas serão desperdiçadas. Isto se deve ao fato de que o desvio para outro trecho do programa demandará uma nova sequencia de instruções. Processadores modernos concentram esforços para contornar este problema, geralmente adotando mais de uma pipeline. 6.1.5.2. Cache Citamos anteriormente a existência de um limitante derivado do aspecto onde , denominado . Para assegurar a disponibilidade de uma Introdução à Programação: Conceitos e Práticas. Página 91 grande quantidade de memória , fundamental às aplicações atuais, os fabricantes tiveram que utilizar tecnologias que contrastam custo quantidade tempo de acesso. Para obter um custo reduzido e permitir o uso de grandes quantidades de memória, foi utilizada tecnologia de memória que resulta em maior tempo de acesso, quando comparada aos tempos de uma . O ideal seria que a memória tivesse o mesmo desempenho de um Registrador da . Porém, os registradores são poucos e específicos para alguns fins. Desta forma, os projetistas avaliaram o comportamento dos programas quando estão sendo executados e observaram um aspecto, onde trechos dos programas quando processados permaneciam manipulando segmentos bem limitados (endereços próximos) da memória por um longo tempo. É importante destacar que um programa na memória tem o seu fluxo de processamento composto basicamente por instruções executadas em sequencia ou por instruções de desvios. As instruções de desvios normalmente se referem a uma seleção de caminhos de processamento ou uma iteração. É neste último caso que situações típicas envolvem o processamento repetitivo de uma área de memória com endereços próximos. Logo os projetistas concluíram que era possível desenvolver uma memória com tempo de acesso menor que o tempo de acesso da , e em menor quantidade, o suficiente para acomodar o trecho da que está sendo referenciado num determinado momento. Figura 38: Memória Cache. Assim, a memória se situa entre a e a memória , e possui um chip controlador que implementa uma política que decide sobre a ocupação deste espaço de memória. Esta lógica tem que decidir, quando encontra um acesso a um endereço da não disponível no seu domínio, como se dará a substituição para que este novo bloco passe a ocupar a . Um programa com excesso de desvios para trechos de códigos que manipulam áreas de memória distintas degradam o desempenho, uma vez que são produzidas muitas faltas na . Introdução à Programação: Conceitos e Práticas. Página 92 6.1.5.3. Outras Técnicas Outras técnicas são empregadas visando elevar ao máximo o desempenho global de um sistema de processamento. Muito resumidamente, seriam: i. Processamento Paralelo: Esta técnica visa multiplicar uma parte ou toda uma permitindo que trechos distintos de um programa ou mesmo distintos programas possam ser processados simultaneamente. Além de ser uma característica da , também os Sistemas Operacionais e as Linguagens de Programação devem ser capazes de explorar este recurso. Processadores modernos com tecnologia empregam esta técnica. ii. Processamento Vetorial: A com processamento escalar é capaz de operar sobre apenas um dado. Por exemplo: a instrução de soma é capaz de somar um número com outro número produzindo um resultado. Em um processador vetorial, um único registrador da é capaz de conter múltiplos dados, e as instruções podem operar com estes múltiplos dados. Por exemplo: em uma com processamento vetorial é possível trazer quatro inteiros da para a e acomodá-los em um registrador; em seguida uma instrução de soma poderá adicionar estes quatro inteiros a outros quatro inteiros produzindo como resultado quatro inteiros, tudo ao mesmo tempo. As tecnologias dos processadores incorporam esta característica, onde uma instrução pode operar múltiplos dados. Introdução à Programação: Conceitos e Práticas. Página 93 7. Introdução à Programação Atualmente não restam mais dúvidas sobre o sucesso da tecnologia digital, pois em curto espaço de tempo alcançou um grau de disseminação que transcende fronteiras, culturas e classes sociais. Certamente o nível de desenvolvimento tecnológico que presenciamos hoje representa apenas uma amostra do que há por vir. Este avanço se deve ao surgimento do computador nos anos , que de forma inegável marcou o início de uma revolução que se propagaria em toda a sociedade. Porém, a linguagem de programação poderia ser igualmente qualificada como revolucionária, pois a sua utilização proporcionou uma incrível flexibilidade aos computadores. 5 A linguagem teve um papel relevante nesta revolução pelo fato ter sido a primeira linguagem com grande aceitação, sucesso este em grande parte creditado a sua maior apoiadora, a Após este sucesso, teve início uma onda de criação de novas linguagens, cuja amostra pode ser observada na seguinte árvore genealógica. FORTRAN (54) COBOL (59) 1960 ALGOL 60 BASIC (64) Simula 67 PL/I (65) ALGOL 68 1970 C (72) Pascal (71) Modula 2 (75) Conc Pascal (75) CLU (75) Ada (79) 1980 Ada (83) ANSI C++(85) Eifell (86) 1990 Java (95) Ada (95) Figura 39: Genealogia das Linguagens – Base FORTRAN. 5 http://pt.wikipedia.org/wiki/Fortran Introdução à Programação: Conceitos e Práticas. Página 94 Esta árvore destaca claramente a influência da linguagem , gerando uma grande quantidade de descendentes, o que não se observa em linguagens com diferentes propostas de estilos de programação, como ilustra a figura abaixo: LISP (59) APL (62) SNOBOL (62) Smalltalk (69) PROLOG (72) OPS5 (77) Perl (87) Tcl/Tk (88) Figura 40: Genealogia das Linguagens. Um dos fatores que foram determinantes no sucesso das linguagens de programação foi a possibilidade de fazer com que o computador desempenhe diferentes funções, bastando para isto programar o comportamento desejado. Além disso, a produtividade proporcionada por uma linguagem de alto nível, como o , estimulou largamente o emprego de programas para realizar funções das mais variadas. A expansão e os aprimoramentos incorporados às linguagens fizeram com que a atividade de programação saísse dos ambientes de pesquisa e começasse a permear os mais diferentes setores da economia e incorporasse programadores originários de várias outras profissões. A seguinte figura ilustra como a produção de programas foi facilitada pelas linguagens de programação de alto nível. Figura 41: Níveis de Programação. Introdução à Programação: Conceitos e Práticas. Página 95 Tanto o código quanto o código de máquina da figura são de uma compilação real de uma expressão simples em linguagem de alto nível, que produziu três linhas em e dezesseis bytes para o código de máquina. Assim, com a facilidade proporcionada pelas linguagens de alto nível, as décadas de e experimentaram uma expansão em larga escala no uso de programas de computadores, gerando uma nova situação que era a preocupação em como gerenciar este novo elemento, chamado programa de computador. A ausência de um processo de engenharia no desenvolvimento de programas e a ausência de boas práticas de gerenciamento levaram ao surgimento do termo , inicialmente citado por Edsger Dijkstra em . Esta constatação despertou para a necessidade de abordar a atividade de programação como um processo de engenharia, demandando a aplicação de metodologias, técnicas, ferramentas e padrões durante todo o ciclo de vida do software. A figura a seguir ilustra um processo de desenvolvimento de software: Figura 42: Ciclo de Desenvolvimento de Software. O software passou a ser visto como um produto e rapidamente se converteu em tecnologia presente nos mais diversificados dispositivos, tais como: aviões, carros, televisões, telefone, micro-ondas, foguetes, trens, dentre outros. Introdução à Programação: Conceitos e Práticas. Página 96 8. O Processo de Tradução A Linguagem de Programação reduziu a lacuna entre o problema e o programa, pois permitiu que o código ficasse mais próximo da formulação do problema. No caso particular do a forma de programar se assemelhava a descrição dos problemas em notação matemática. Isto lhe valeu o acrônimo de . Porém, os computadores continuavam a compreender apenas a linguagem binária. As instruções eram entendidas pelas apenas no formato numérico. Logo, era necessário traduzir o programa expresso na forma textual para um formato adequado para processamento na . A figura a seguir ilustra os diversos conversores e montadores necessários para efetuar esta tradução: Figura 43: Processo de Tradução Atualmente estão disponíveis avançados ambientes de programação denominados – que tornam transparente ao programador as etapas de tradução. O programador interage com o digitando o programa em uma linguagem de alto nível e aciona o comando ou a tecla de compilação. Assim obtém o código final, que pode ser executado ou depurado no próprio ambiente do . O depurador é uma importante ferramenta que permite ao programador executar pausadamente cada linha de código, permitindo inspecionar a memória em busca de erros na lógica. Introdução à Programação: Conceitos e Práticas. Página 97 9. A Linguagem Pascal A linguagem foi por muito tempo utilizada no ensino de programação. Porém, as versões iniciais da linguagem não proporcionavam mecanismos que ajudassem de forma natural na escrita de programas com qualidade de produto. De forma simplificada um programa em execução na memória pode ser visto em dois blocos, sendo um composto pelas instruções e outro por uma área de dados para armazenamento de informações. As instruções, por sua vez podem ser de duas naturezas: e . A de tipo é aquela que concluída a sua execução é dada a vez para a próxima na memória. A de tipo é aquela que testa uma condição para decidir qual a próxima instrução a ser executada. Conforme o resultado da condição testada poderá ser executada a instrução subsequente ou uma instrução em outro endereço. Assim, programas nos primórdios do seu uso tinham a seguinte aparência, onde uma série de instruções de desvio ( ou ) alterava o fluxo de execução do programa, dificultando o entendimento da lógica: L01 L02 L03 L04 L05 L06 L07 L08 L09 L10 L11 L12 L13 L14 L15 L16 L17 L18 instrução instrução jump L06 instrução jump L16 instrução jump L10 instrução jump L04 instrução jump L15 instrução jump L02 instrução jump L01 instrução jump L10 instrução Figura 44: Estilo de Programação Spaghetti. Esta aparência rendeu a denominação de estilo de programação em função da aparência confusa e semelhante a este típico prato italiano. Neste período, fins dos anos e década de , o que norteava o desenvolvimento de programas era a necessidade de economizar os recursos computacionais (memória e tempo de ), pois o custava muito mais do que o tempo dos desenvolvedores. Para tal fim, os programadores utilizavam técnicas de reaproveitamento de memória e otimização das instruções que resultava no médio prazo em grande dificuldade de manutenção. Nas décadas seguintes foram surgindo novos estilos de programação com o objetivo de melhorar a engenharia dos programas, e consequentemente elevar o grau de qualidade Introdução à Programação: Conceitos e Práticas. Página 98 e confiabilidade destes produtos. Estes estilos visavam a melhor organização das entidades que compõem um programa, principalmente dados e rotinas, e também almejavam melhorar a produtividade do programador através do reuso de códigos. O seguinte quadro ilustra a evolução nos estilos de programação. Figura 45: Evolução no estilo de programação. Após o , o esforço seguinte que mereceu destaque na história das linguagens 6 foi o projeto do , que nasceu da intenção de prover uma linguagem que conduzisse a programas melhores construídos. O era uma linguagem grande e complexa e dadas as condições da época, não houveram implementações completas de compiladores. Porém, um dos participantes nos trabalhos do observou que os princípios desta linguagem poderiam ser utilizados para compor uma nova linguagem mais simples que auxiliasse na formação acadêmica e no ensino de programação. Assim surgiu a linguagem Pascal em , como proposta do prof. Niklaus Wirth. A linguagem Pascal7 alcançou a popularização nos anos 8 da , denominados . A linha 6 7 através dos compiladores foi descontinuada na http://pt.wikipedia.org/wiki/ALGOL http://pt.wikipedia.org/wiki/Pascal_(linguagem_de_programação) Introdução à Programação: Conceitos e Práticas. Página 99 metade dos anos ainda em plataforma de , sendo sucedida pelo ambiente integrado de desenvolvimento que suportava a linguagem Porém, um grupo de seguidores do Turbo Pascal iniciou um esforço para desenvolver um compilador multiplataforma que acompanhasse a evolução dos processadores. 9,10 Nasceu assim a linha ainda na década de e disponibiliza compiladores de . 8 http://pt.wikipedia.org/wiki/Turbo_Pascal http://pt.wikipedia.org/wiki/Free_Pascal 10 http://www.freepascal.org/ 9 Introdução à Programação: Conceitos e Práticas. Página 100 10. Estrutura de um Programa Pascal A Figura 33: Programa na Memória RAM: Código + Dados. ilustrou um programa na memória separado em duas áreas distintas, sendo uma para acomodar a codificação das instruções e a outra para conter os resultados produzidos por estas instruções. Esta organização reflete também o título do livro , de Niklaus Wirth, publicado em , afirmando que um programa é a combinação de algoritmo e estrutura de dados, ou em termos mais simples: . De forma semelhante, um programa Pascal tem a seguinte estrutura básica: Program exemplo; Var . . . Begin . . . End. Nome do programa Trecho destinado aos dados Trecho destinado às instruções Assim, para iniciar a programação em Pascal é necessário conhecer tanto os recursos disponíveis para definição de dados quando os recursos para manipulação dos dados. Uma das vantagens das linguagens de alto nível em relação às linguagens de baixo nível foi a possibilidade de trabalhar com nomes em vez de endereços físicos de memória. Assim, na sequência são apresentadas as regras para a criação de nomes ou identificadores. 11. Identificadores O programador terá que se habituar a dar nomes para as entidades de um programa, tais como constantes, tipos, variáveis, procedures, funções, units e programas. A regra para nomes ou identificadores é dada por: Introdução à Programação: Conceitos e Práticas. Página 101 A notação11 acima indica que um identificador válido inicia com um dos caracteres listados em seguido por zero ou mais caracteres em A definição admite letra ou dígito. A linguagem não distingue entre maiúsculo e minúsculo, ou seja: um nome todo em maiúsculo ou todo em minúsculo ou misto é entendido pelo Pascal como sendo o mesmo identificador. Seguem alguns exemplos: Identificadores válidos Identificadores não válidos A B Soma SOMA_TOTAL B20 A1 Qdade I _J K_ 12. SOMA TOTAL 9AUX SOMA-TOTAL Dado% Valor$ #qdade aluno@nome Variáveis e Tipos de Dados Associar nomes com endereços de memória facilitou a atividade de programação e deu origem ao conceito de variável. Assim, variável é uma referência a um endereço de uma área de memória, e através da qual se pode armazenar ou recuperar valor. A seguinte figura ilustra uma sequência de bytes na memória (endereços de a ) e a associação com os nomes , , e , formando o conceito de variáveis. As células de memória podem ser trabalhadas através destes nomes. Nome A: B: C: X: Célula - RAM Endereço 303 302 301 300 A reserva destas células de memória em se dá pela declaração de variáveis no trecho destinado aos dados. Além do nome que se deseja associar à área de memória também é necessário indicar a natureza do dado que se deseja acomodar nesta área, que em termos de linguagem de programação é o . Assim, a sintaxe para declaração de variáveis é: 11 Notação semelhante à BNF: O símbolo “::=” significa “é definido como”. O símbolo “*” significa “repetir 0 ou mais vezes”. Introdução à Programação: Conceitos e Práticas. Página 102 { } Alguns exemplos de declaração de variáveis numéricas: Program exemplo; Var A : Integer; B : LongInt; C : Word; D : Byte; X : Real; Y : Double; Z : Extended; I, J, K : Integer; Soma, Produto : Real; Begin . . End. Na sequência serão detalhados os significados dos tipos numéricos. 13. Comando de Atribuição A primeira instrução a ser apresentada e uma das mais utilizadas é a instrução de atribuição, cuja função é armazenar valores na memória. A sintaxe é: O símbolo representa a instrução de atribuição, cujo significado é armazenar na célula de memória indicada pela variável o valor final da expressão. A aparece no da instrução de atribuição e o aparece no . Para fazer uso deste recurso é preciso aprender a escrever expressões, que podem ser de natureza numérica/aritmética, textuais, lógicas ou binárias. Introdução à Programação: Conceitos e Práticas. Página 103 14. Expressões Numéricas As expressões numéricas combinam constantes, variáveis, operadores e funções a fim de obter um novo valor calculado. A sintaxe da linguagem de programação deve ser entendida para que uma expressão escrita em notação matemática normal seja corretamente convertida para a forma computacional. Alguns exemplos de expressões que serão reescritas em Pascal: √ √ 14.1. Tipos Numéricos A linguagem Pascal disponibiliza tipos de dados numéricos que permitem a representação de inteiros sem sinal, inteiros com sinal e reais, com diversos tamanhos e precisões. A seguinte tabela apresenta os tipos inteiros disponíveis no Tipo Byte Shortint Smallint Word Integer Faixa smallint ou longint depende da arquitetura Longint Longword Int64 QWord 12 12 : #bytes 1 1 2 2 2 ou 4 4 4 8 8 O Pascal padrão disponibiliza alguns destes tipos Introdução à Programação: Conceitos e Práticas. Página 104 Pela faixa de representação distinguem-se os inteiros com sinal e sem sinal. Os inteiros sem sinal são armazenados como , enquanto os inteiros com sinal são representados em . A seguinte tabela apresenta os tipos reais: Tipo Real Single Faixa dependente da plataforma Dígitos Significativos --- #bytes 4 or 8 4 Double 8 Extended Comp 10 8 Agora temos mais elementos para compreender o conceito de variável. Assim, ao declararmos uma variável estamos fornecendo elementos ao compilador sobre a natureza da célula de memória e orientando sobre as regras permitidas para o manuseio deste espaço. No caso do compilador é necessário observar as diretivas de compilação que influenciam no tamanho de alguns tipos. O quadro a seguir ilustra as alternativas oferecidas pelo compilador: Figura 46: Modos do Compilador. O acesso a estas opções é feito conforme ilustração a seguir: Introdução à Programação: Conceitos e Práticas. Página 105 Figura 47: Acesso a tela com as diretivas de compilação. Por exemplo, se compilarmos o programa exemplo, com as declarações de variáveis indicadas, tem-se as seguintes informações: Program exemplo; Var A : Integer; B : LongInt; C : Word; D : Byte; X : Real; Y : Double; Z : Extended; I, J, K : Integer; Soma, Produto : Real; #bytes 4 4 2 1 8 8 10 4 4 4 8 8 Endereço de Memória Var Decimal Hexa Offset @A 4243456 40C000 4 @B 4243460 40C004 4 @C 4243464 40C008 2 @D 4243466 40C00A 6 @X 4243472 40C010 8 @Y 4243480 40C018 8 @Z 4243488 40C020 12 @I 4243500 40C02C 4 @J 4243504 40C030 4 @K 4243508 40C034 4 @Soma 4243512 40C038 8 @Produto 4243520 40C040 8 65 72 A coluna indica a quantidade de bytes efetivamente ocupada pela variável. As colunas e indicam o endereço de memória associado a cada variável no momento da execução. A coluna indica a diferença dos endereços de duas variáveis alocadas consecutivamente. Teoricamente este número deveria coincidir com a coluna , o que não ocorre na prática em função do compilador alinhar as variáveis em posições de memória que forneça o melhor desempenho de acesso. Introdução à Programação: Conceitos e Práticas. Página 106 Normalmente isto ocorre quando posicionadas em endereços múltiplos de ou , conforme o tipo da variável. A tabela abaixo indica as alocações, bem como os bytes de preenchimentos: End. 1 40C000 Var Célula A End. Var 25 40C018 Célula Y End. 49 40C030 2 40C001 26 40C019 50 40C031 3 40C002 27 40C01A 51 40C032 4 40C003 28 40C01B 52 40C033 29 40C01C 53 40C034 6 40C005 30 40C01D 54 40C035 7 40C006 31 40C01E 55 40C036 5 40C004 B 8 40C007 9 40C008 32 40C01F C 10 40C009 33 40C020 57 40C038 58 40C039 35 40C022 59 40C03A 12 40C00B 36 40C023 60 40C03B 13 40C00C 37 40C024 61 40C03C 14 40C00D 38 40C025 62 40C03D 15 40C00E 39 40C026 63 40C03E 16 40C00F 40 40C027 64 40C03F 41 40C028 65 40C040 18 40C011 42 40C029 66 40C041 19 40C012 43 40C02A 67 40C042 20 40C013 44 40C02B 68 40C043 21 40C014 45 40C02C 22 40C015 46 40C02D 70 40C045 23 40C016 47 40C02E 71 40C046 24 40C017 48 40C02F 72 40C047 17 40C010 D X Célula J K 56 40C037 Z 34 40C021 11 40C00A Var I Soma Produto 69 40C044 Figura 48: Alocação das variáveis na memória. Observar que a variável ocupa um byte de memória e que na sequência vem cinco bytes de preenchimento a fim de posicionar a próxima variável ( ) em endereço adequado. Esta estratégia pode variar conforme o compilador e a plataforma. 14.2. Constantes Agora podemos utilizar estas células de memória para armazenar valores ou recuperálos conforme a conveniência da lógica do processamento. Para guardar valores, o nome da variável deve aparecer à esquerda do comando de atribuição, enquanto que Introdução à Programação: Conceitos e Práticas. Página 107 do lado direito a referência a variável é substituída pelo seu valor. O seguinte exemplo ilustra as formas mais usais de representar constantes numéricas: Declaração das Variáveis Program exemplo; Var A : Integer; B : LongInt; C : Word; D : Byte; X : Real; Y : Double; Z : Extended; I, J, K : Integer; Soma, Produto : Real; Programa Principal Begin A B C D X Y Z I Soma Produto X End. := := := := := := := := := := -1; 32767; 1000; 38; 1.25e+10; -0.0003e-30; 30.67; $3f8; 720; -1000.; := 0.01; As notações se assemelham muito ao que estamos habituados a utilizar na aritmética convencional. Cabe destacar a notação de números inteiros em hexadecimal, onde o símbolo precede a constante. Os números reais aceitam a notação com potência de , e neste caso deve se interpretar que o símbolo ou equivale a ― ‖. Assim, a constante equivale a . Apresentaremos na sequência três momentos distintos da execução deste programa e os respectivos conteúdos de memória em hexadecimal a fim de melhor visualizar os equivalentes binários: Introdução à Programação: Conceitos e Práticas. Página 108 i. Início do programa, porém nenhuma instrução executada: End. 1 40C000 Var Célula Var Célula Var Célula $00 25 40C018 $00 49 40C030 $00 26 40C019 $00 50 40C031 $00 3 40C002 $00 27 40C01A $00 51 40C032 $00 4 40C003 $00 28 40C01B $00 52 40C033 $00 $00 29 40C01C $00 53 40C034 6 40C005 $00 30 40C01D $00 54 40C035 $00 7 40C006 $00 31 40C01E $00 55 40C036 $00 8 40C007 $00 32 40C01F $00 56 40C037 $00 $00 33 40C020 $00 57 40C038 $00 34 40C021 $00 58 40C039 $00 $00 35 40C022 $00 59 40C03A $00 12 40C00B $00 36 40C023 $00 60 40C03B $00 13 40C00C $00 37 40C024 $00 61 40C03C $00 14 40C00D $00 38 40C025 $00 62 40C03D $00 15 40C00E $00 39 40C026 $00 63 40C03E $00 16 40C00F $00 40 40C027 $00 64 40C03F $00 9 40C008 B C 10 40C009 11 40C00A 17 40C010 D X Y End. 2 40C001 5 40C004 A End. Z J K Soma $00 $00 $00 41 40C028 $00 65 40C040 18 40C011 $00 42 40C029 $00 66 40C041 $00 19 40C012 $00 43 40C02A $00 67 40C042 $00 20 40C013 $00 44 40C02B $00 68 40C043 $00 21 40C014 $00 45 40C02C $00 69 40C044 $00 22 40C015 $00 46 40C02D $00 70 40C045 $00 23 40C016 $00 47 40C02E $00 71 40C046 $00 24 40C017 $00 48 40C02F $00 72 40C047 $00 I Produto $00 $00 Observa-se que neste instante todos os bytes estão com o valor zero. O programador não pode tomar como certa a inicialização de variáveis pela linguagem. Sempre que for necessário assegurar um valor inicial, o programador deve explicitamente atribuir o valor desejado. Introdução à Programação: Conceitos e Práticas. Página 109 ii. Após a execução da primeira instrução ( End. 1 40C000 Var Célula $FF 25 40C018 2 40C001 $FF 3 40C002 $FF 4 40C003 Var Célula Var Célula 49 40C030 26 40C019 $00 50 40C031 $00 27 40C01A $00 51 40C032 $00 $FF 28 40C01B $00 52 40C033 $00 $00 29 40C01C $00 53 40C034 6 40C005 $00 30 40C01D $00 54 40C035 $00 7 40C006 $00 31 40C01E $00 55 40C036 $00 8 40C007 $00 32 40C01F $00 56 40C037 $00 $00 33 40C020 $00 57 40C038 $00 34 40C021 $00 58 40C039 $00 $00 35 40C022 $00 59 40C03A $00 12 40C00B $00 36 40C023 $00 60 40C03B $00 13 40C00C $00 37 40C024 $00 61 40C03C $00 14 40C00D $00 38 40C025 $00 62 40C03D $00 15 40C00E $00 39 40C026 $00 63 40C03E $00 16 40C00F $00 40 40C027 $00 64 40C03F $00 $00 41 40C028 $00 65 40C040 18 40C011 $00 42 40C029 $00 66 40C041 $00 19 40C012 $00 43 40C02A $00 67 40C042 $00 20 40C013 $00 44 40C02B $00 68 40C043 $00 21 40C014 $00 45 40C02C $00 69 40C044 $00 22 40C015 $00 46 40C02D $00 70 40C045 $00 23 40C016 $00 47 40C02E $00 71 40C046 $00 24 40C017 $00 48 40C02F $00 72 40C047 $00 9 40C008 B C 10 40C009 11 40C00A 17 40C010 D X Y End. $00 5 40C004 A End. ): Z I J K Soma Produto $00 $00 $00 $00 Neste novo cenário logo após a execução da primeira instrução, onde é atribuído o valor à variável , pode ser observado que os bytes alocados para esta variável passaram a conter que equivale a com o valor para cada dígito binário. Isto decorre do fato que a variável está declarada como integer13 o que a define como inteiro com sinal e tamanho de . Assim, ao representar o em obtém-se esta sequência de bits, conforme ilustra a conversão abaixo: : C-1: C-2: 000...00001 111...11110 + 1 ----------111...11111 Ok 13 O tamanho do tipo integer depende muitas vezes da plataforma computacional e é configurável. O tamanho exato deve ser verificado. Introdução à Programação: Conceitos e Práticas. Página 110 Após a execução de todas as instruções, porém antes de encerrar o programa: End. Var 1 40C000 Célula A End. $FF 25 40C018 2 40C001 $FF 3 40C002 4 40C003 Var Célula Var Célula $6E 49 40C030 26 40C019 $F2 50 40C031 $00 $FF 27 40C01A $78 51 40C032 $00 $FF 28 40C01B $5C 52 40C033 $00 $FF 29 40C01C $4B 53 40C034 6 40C005 $7F 30 40C01D $EC 54 40C035 $00 7 40C006 $00 31 40C01E $F8 55 40C036 $00 8 40C007 $00 32 40C01F $B8 56 40C037 $00 $E8 33 40C020 $29 57 40C038 $03 34 40C021 $5C 58 40C039 $00 $26 35 40C022 $8F 59 40C03A $00 12 40C00B $00 36 40C023 $C2 60 40C03B $00 13 40C00C $00 37 40C024 $F5 61 40C03C $00 14 40C00D $00 38 40C025 $28 62 40C03D $80 15 40C00E $00 39 40C026 $5C 63 40C03E $86 16 40C00F $00 40 40C027 $F5 64 40C03F $40 $7B 41 40C028 $03 65 40C040 18 40C011 $14 42 40C029 $40 66 40C041 $00 19 40C012 $AE 43 40C02A $00 67 40C042 $00 20 40C013 $47 44 40C02B $00 68 40C043 $00 21 40C014 $E1 45 40C02C $F8 69 40C044 $00 22 40C015 $7A 46 40C02D $03 70 40C045 $40 23 40C016 $84 47 40C02E $00 71 40C046 $8F 24 40C017 $3F 48 40C02F $00 72 40C047 $C0 5 40C004 B 9 40C008 C 10 40C009 11 40C00A D 17 40C010 X Y End. Z I J K Soma Produto $00 $00 $00 $00 A configuração final mostra que todas as células (conjunto de bytes) foram alteradas pelas atribuições, com exceção dos bytes de preenchimento. A título de exercício vamos converter o valor de para a forma binária, conforme padrão IEEE 754 (IEC 60559) já explicado anteriormente: Tipo Sinal Expoente Mantissa Double extended (80-bit) 1 15 64 Total bits 80 Dígitos Expoente Bits Significativos bias Precisão Decimal 16383 64 ~19.2 Detalhando o tipo Double Extended, tem-se a seguinte estrutura: S Expoente (15) Introdução à Programação: Conceitos e Práticas. Mantissa (64) Página 111 Para i. Converter 30 14 ii. 16 1 1 para a base 0.67 0.72 0.52 0.32 0.12 0.92 0.72 0.52 0.32 0.12 0.92 0.72 0.53 0.50 16 0 x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = x 16 = 10.720 11.5200 8.3200 5.1200 1.9200 14.7200 11.5200 8.3200 5.1200 1.9200 14.7207 11.5313 8.5000 8.0000 Normalizar Observe que o padrão difere um pouco do método didático apresentado, pois há um ganho de ao se normalizar como em vez de . iii. Enquadrar Agora, resta enquadrar as partes de no padrão estabelecido em binário. Introdução à Programação: Conceitos e Práticas. Página 112 S Expoente (15) Mantissa (64) Onde a parte do Expoente foi obtida da seguinte forma: , em com Observe que há uma diferença apenas no último bit, provavelmente decorrente da precisão nos cálculos. Na memória os bytes são armazenados de forma invertida, ou seja o byte menos significativo fica no endereço , conforme: Fica a cargo de o leitor avaliar com mais detalhes os métodos de conversão das demais variáveis. 14.3. Operadores Matemáticos Vamos ampliar a capacidade de manipular dados através dos operadores aritméticos, aplicados a um ou dois operandos para produzir um resultado. Operador Binário Operação Observação Soma Subtração Multiplicação Divisão Divisão Inteira Os operandos podem ser expressões com inteiro ou real. Se ambos os operandos forem inteiros o resultado será inteiro. Se um dos operandos for real, então o resultado será real. Resto Independente do tipo do operando o resultado será real. Operandos e resultado são inteiros. Retorna o quociente da divisão. Operandos e resultado são inteiros. Retorna o resto da divisão. Os operadores e também podem ser aplicados a apenas um operando, passando a ter os seguintes significados: Operador Unário Operação Identidade Inversão Observação + Expressão retorna o mesmo valor de Expressão. - Expressão retorna o valor de Expressão com sinal trocado. Introdução à Programação: Conceitos e Práticas. Página 113 Quando estiverem presentes em uma expressão vários operadores, a linguagem executa primeiramente o operador com maior nível de precedência. De modo semelhante ao habitual os operadores tem precedência de execução em relação a e . A seguinte tabela antecipa os operadores e as respectivas precedências: Tabela 1: Precedência de Operadores. Operador14 Precedência Mais Alta (primeira) Segunda Terceira Mais Baixa (Última) Categoria Operadores Unários Operadores de Multiplicação Operadores de Soma Operadores Relacionais Considere a seguinte expressão: A operação de multiplicação é executada primeiro e depois a soma. Caso queira efetuar primeiro a soma então se deve fazer uso de parêntesis, como na expressão: Neste outro exemplo: quando escrita em Pascal tem-se: Foram utilizados dois níveis de parêntesis a fim de agrupar adequadamente as operações. 14.4. Funções Matemáticas A linguagem Pascal disponibiliza algumas funções aritméticas que podem ser utilizadas na construção de expressões matemáticas. Segue uma descrição resumida de cada uma delas: 1) 14 Alguns destes operadores fazem parte do Free Pascal e não do Pascal padrão. Introdução à Programação: Conceitos e Práticas. Página 114 Retorna o valor absoluto de uma variável. O resultado será do mesmo tipo do argumento. Exemplo: Program ExABS; Var r : real; i : integer; begin r := abs(-3.0); i := abs(-65); end. { r := 3.0 } { i := 65 } 2) Retorna o ângulo cuja é . O resultado será expresso em radianos. Exemplo: Program ExARCTAN; Var R : Real; begin R:=ArcTan(0); R:=ArcTan(1); end. { R := 0.0 } { R := 0.785... que equivale a PI/4} 3) Retorna o de um ângulo , onde deve estar em radianos. Exemplo: Program ExCOS; Var R : Real; begin R := Cos(Pi); R := Cos(Pi/2); R := Cos(Pi/3); R := Cos(0); end. { { { { R:=-1.0 R:=0.0 R:=0.5 R:=1.0 } } } } Introdução à Programação: Conceitos e Práticas. Página 115 4) Retorna o de um ângulo , onde deve estar em radianos. Exemplo: Program ExSIN; Var R : Real; begin R := Sin(Pi); R := Sin(Pi/2); R := Sin(Pi/6); R := Sin(0); end. { { { { R R R R := := := := 0.0 1.0 0.5 0.0 } } } } { { { { R R R R := := := := 0.0 1.0 0.5 0.0 } } } } 5) Retorna o valor de Exemplo: Program ExPI; Var R : Real; begin R := Sin(Pi); R := Sin(Pi/2); R := Sin(Pi/6); R := Sin(0); end. 6) Retorna o valor de , onde Exemplo: Program ExEXP; begin Writeln (Exp(1)); { mostrará na tela o valor 2.7182818284590452} end. Introdução à Programação: Conceitos e Práticas. Página 116 7) Retorna a parte fracionária de . Exemplo: Program ExFRAC; Var R : Real; begin Writeln (Frac (123.456)); { Mostrará O.456 } Writeln (Frac (-123.456)); { Mostrará -O.456 } end. 8) Retorna a parte inteira de , como real. Exemplo: Program ExINT; Var R : Real; begin Writeln (Int (123.456)); { Mostrará 123.0 } Writeln (Int (-123.456)); { Mostrará -123.0 } end. 9) Retorna o de na base , onde .e deve ser positivo. Exemplo: Program ExLN; begin Writeln (Ln(1)); { Mostrará 0 } Writeln (Ln(Exp(1))); { Mostrará 1 } end. 10) Retorna o quadrado de . O resultado será do mesmo tipo que o argumento . Introdução à Programação: Conceitos e Práticas. Página 117 Exemplo: Program ExSQR; Var r : real; i : integer; begin r := sqr(-3.0); i := sqr(-3); end. { r := 9.0 } { i := 9 } 11) Retorna a raiz quadrada de ( √ ), onde deve ser positivo. Exemplo: Program ExSQRT; Var r : real; begin r := sqrt(4); { r := 2.0 } r := sqrt(2.0); { r := 1.4142.. } end. 12) Retorna o arredondamento de para o inteiro mais próximo. Quando a parte fracionária é , o arredondamento é feito na direção do par mais próximo. Exemplo: Program ExROUND; begin Writeln Writeln Writeln Writeln Writeln Writeln end. (Round(1234.56)); (Round(-1234.56)); (Round(12.3456)); (Round(-12.3456)); (Round(2.5)); (Round(3.5)); { { { { { { Introdução à Programação: Conceitos e Práticas. Mostrará Mostrará Mostrará Mostrará Mostrará Mostrará 1235 } -1235 } 12 } -12 } 2 (para baixo) } 4 (para cima) } Página 118 13) Retorna a parte inteira de X. Exemplo: Program ExTRUNC; begin Writeln Writeln Writeln Writeln end. (Trunc(123.456)); (Trunc(-123.456)); (Trunc(12.3456)); (Trunc(-12.3456)); { { { { Mostrará Mostrará Mostrará Mostrará 123 -123 12 -12 } } } } 14.5. Exemplos Os exemplos que seguem apresentam algumas expressões na forma usual e o respectivo programa: Equação i. √ ii. iii. Que pode ser reescrita como: Programa Program EqA; Var X, Y, Z : Real; begin Z := SQRT(SQR(X) + Y*Y*Y)/ABS(X+Y); end. Program EqA; Var X, Y, Z : Real; begin Z := SQRT(X*X + Y*Y*Y)/ABS(X+Y); end. Program EqB; Var X, Z : Real; begin Z := (1 + SIN(X))/(1 + COS(X)); end. Program EqC; Var X, Z : Real; begin Z := 1 + 1/X + 1/SQR(X) + 1/(X*X*X) + 1/(X*X*X*X); end. Program EqC; Var X, Y, Z : Real; Introdução à Programação: Conceitos e Práticas. Página 119 ( ( ( ))) √ √ iv. ( ) ( ) v. √ √ Begin Y := 1/X; Z := 1 + Y*(1 + Y*(1 + Y*(1 + Y))); end. Program EqD; Var X, Y, Z, W : Real; Begin W := X/Y; Z := W – (X + SQR(W))/(Y – SQR(W)); end. Program EqE; Var X, Z : Real; Begin Z := SQRT(PI + SQRT(EXP(3) + SQRT(4 + SQRT(X) ))); end. Observe na expressão do item uma codificação alternativa a partir da reescrita da expressão original. Além disso, uma variável auxiliar ( ) foi utilizada, o que evitou a repetição de uma mesma operação ao longo da expressão. Caso fossemos compilar e executar estes programas, não seria possível observar os efeitos do código, visto que o programa não inclui nenhuma forma de interação com o usuário. O fornecimento de dados via teclado e apresentação de resultados na tela do computador são meios usuais de interação entre programa e usuário. Na sequência serão apresentados os comandos que permitem incluir estes recursos. Introdução à Programação: Conceitos e Práticas. Página 120 15. Comandos de Entrada e Saída A seguinte figura ilustra um programa em execução interagindo com o usuário através dos dispositivos básicos de Entrada e Saída, que são o teclado e o monitor de vídeo. 15.1. Entrada O comando ou permite que o programa obtenha dados digitados pelo usuário. Ao processar o programa o sistema operacional abre uma janela denominada de ambiente de execução. Ao executar o comando o programa fica aguardando que o usuário digite uma linha com os dados e pressione a tecla . Em seguida, esta linha é interpretada e os dados são armazenados nas variáveis (endereços de memória) indicadas. A sintaxe do para leitura do teclado é: A lista de argumentos deve conter as variáveis (endereços de memória) onde o deve armazenar os dados digitados. Observe no exemplo da figura anterior que o comando digitação de uma linha cujo conteúdo será armazenado como Introdução à Programação: Conceitos e Práticas. irá aguardar a na variável . Página 121 15.2. Saída O comando ou permite que o programa escreva um ou mais valores na tela do computador. Ao processar o programa o sistema operacional abre um ambiente de execução com sua respectiva janela. Ao executar o comando o programa escreve a partir da posição atual do cursor os valores indicados nos argumentos. A diferença entre o eo é que o posiciona o cursor no início da próxima linha após escrever os valores. A sintaxe do para escrita na tela: A lista de argumentos deve conter as expressões cujos resultados o escreverá na tela. 15.3. Exemplos i. Elaborar um programa que leia dados como Nome, Peso, Altura e Sexo; e calcule o Índice de Massa Corpórea. Figura 49: Programa fonte e janela de execução Observe lado a lado o programa e a janela de execução. As setas indicam a relação entre a instrução e o respectivo efeito na tela. O comando faz com que uma informação seja escrita na janela, e o comando faz com que o texto digitado na janela seja armazenado nas variáveis indicadas. Na linha de código: a variável IMC é escrita com o formato indicado sendo duas para as casas decimais, o que resulta em: Introdução à Programação: Conceitos e Práticas. que significa seis posições, Página 122 0 2 2 . 0 9 Caso não tivesse a formatação, a saída seria: ii. Idem ao problema anterior com algumas variações na formatação da tela. O uso do em vez do permite escrever um valor na tela mantendo o cursor na mesma linha. Introdução à Programação: Conceitos e Práticas. Página 123 16. Implementando Funções Com os recursos já apresentados é possível construirmos nossas próprias funções em complemento às disponibilizadas com a linguagem. O uso de funções é fundamental tanto para a modularização dos programas quanto para facilitar a reutilização de códigos já escritos. Assim, ao depararmos com um problema devemos pensar na forma de decompô-lo em unidades funcionais simples, de modo que o programa final trata apenas de integrá-los adequadamente. No exemplo anterior, onde é calculado o – a partir do peso e altura de uma pessoa, o conceito de função permite que escondamos os cálculos atrás de um nome, complementado com os argumentos necessários à realização das contas. Deste modo, sempre que for preciso calcular este , basta referenciar este nome com seus respectivos argumentos. A forma geral de uma função é: A seguinte figura ilustra os principais elementos e conceitos relacionados com funções: Introdução à Programação: Conceitos e Práticas. Página 124 Figura 50: Estrutura de uma função. No exemplo que segue serão feitas associações entre o código em conceitos apresentados nesta figura. e os 16.1. Exemplos O programa e a função para cálculo do poderiam adquirir a seguinte estrutura: Var Altura, Peso, IMC : Real; function getIMC (P : Real; H : Real) : Real; Begin getIMC := P/Sqr(H); End; Begin Writeln ('Entre com Peso e Altura'); Readln (Peso, Altura); IMC := getIMC(Peso, Altura); Writeln ('O seu Indice de Massa Corporea é: ', IMC:6:2); Readln; End. Introdução à Programação: Conceitos e Práticas. Página 125 O passo a passo deste programa, iniciando pelo programa principal, é dado por: 1) Writeln ('Entre com Peso e Altura'); Escrever na tela o texto 'Entre com Peso e Altura' e posicionar o cursor na próxima linha. 2) Readln (Peso, Altura); Aguardar a digitação de uma linha até que seja teclado , para em seguida armazenar na memória conforme indicado nos argumentos, sendo o primeiro número na variável e o segundo número na variável . 3) IMC := getIMC(Peso, Altura); É feita a para executar os cálculos com e . Estes são copiados para definidos no . Neste ponto, a execução programa passa o controle para a função , que ao terminar, , que copia o para a variável . os os do da 4) Writeln ('O seu Indice de Massa Corporea é: ', IMC:6:2); Escrever uma linha na tela, composta , seguido pelo valor de seis posições sendo duas casas decimais. pelo texto formatado com 5) Readln; Aguardar que o usuário tecle . Esta linha serve para que o usuário tenha tempo de avaliar o resultado apresentado na tela. A estrutura da função ficou: 1) function getIMC (P : Real; H : Real) : Real; Éo onde são definidos: o , os eo . A forma de passagem dos parâmetros também é definida no cabeçalho. Neste caso, temos: a) Nome: b) Parâmetros: são dois do tipo real, onde: primeiro parâmetro chama-se e ficará vinculado com o argumento; Introdução à Programação: Conceitos e Práticas. do primeiro Página 126 segundo parâmetro chama-se argumento; c) Tipo do Resultado: Define que o e ficará vinculado com o será um do segundo . 2) Begin Início do . 3) getIMC := P/Sqr(H); É uma lógica simples onde o cálculo é dado por . Não foi necessário utilizar variáveis locais. O é atribuído ao seu . 4) End; O resultado da função é copiado para uma chamador poderá acessá-lo. Também indica o fim do onde o programa . 16.2. Exercícios i. Elaborar um programa que calcule a distância entre dois pontos dados pelas suas coordenadas Var D, Ax, Ay, Bx, By : Real; Function getDIST (X1, Y1, X2, Y2 : Real) : Real; Begin getDIST := Sqrt(Sqr(X2-X1)+Sqr(Y2-Y1)); End; Begin Writeln ('Entre com as coordenadas dos pontos:'); Readln (Ax, Ay, Bx, By); D := getDIST(Ax, Ay, Bx, By); Writeln ('Distancia = ', D:6:2); Readln; End. O programa principal implementa uma interface simples, onde é solicitado ao usuário informar as coordenadas de dois pontos. As coordenadas informadas são armazenadas nas variáveis . Em seguida é atribuído à variável o resultado de uma expressão que contém apenas a chamada a função com os argumentos . Na execução da função os argumentos já estão Introdução à Programação: Conceitos e Práticas. Página 127 copiados para os parâmetros os cálculos ( √ ii. . A única linha de código faz efetivamente ) para obtenção da distância. Escrever uma function que receba dois números ( e ) e retorne o valor de: . function power (base, expoente : real) : real; begin power := exp (expoente*ln(base)); end; begin writeln (power (2, 0.5):8:5); writeln (power (exp(1), 1):8:5); end. O não calcula diretamente o valor de uma base elevado a um expoente. As funções prontas mais próximas disto são as funções que corresponde a , e que corresponde ao logaritmo neperiano ou natural. Assim, lançaremos mão das seguintes transformações até que se chegue a uma combinação destas funções. ( ) ( A expressão final utiliza somente as funções função codifica esta lógica. Introdução à Programação: Conceitos e Práticas. ) e disponíveis no ea Página 128 iii. Escrever uma function que calcule a média aritmética entre dois números: Program ExMEDIA; function media(a, b : real) : real; begin media := (a+b)/2; end; var x, y, z : real; begin writeln ('entre com dois numeros'); readln (x, y); z := media (x, y); writeln ('media = ',z:8:5); end. Especificamente para a linha copiados para os e ; a função final é copiado para a variável . iv. os e são é executada e o resultado Escrever uma function que calcule a média aritmética entre quatro números: Program ExMEDIA; function media (a, b : real) : real; begin media := (a+b)/2; end; function media4 (a, b, c, d : real) : real; begin media4 := media(media (a,b), media (c,d)); end; var x, y, u, v, z : real; begin writeln ('entre com quatro numeros'); readln (x, y, u, v); Introdução à Programação: Conceitos e Práticas. Página 129 z := media4 (x, y, u, v); writeln ('media = ', z:8:5); end. A solução adotada para a média aritmética de quatro números foi o de aproveitar a função já pronta que calcula a média de dois números, ou seja: ( v. ) Escrever uma function que calcule o seno de um ângulo utilizando a seguinte série: ⋯ Vamos limitar a série aos cinco primeiros termos, pois não temos recursos adequados para ir muito além de forma estruturada. Program ExSENO; function power (base, expoente : real) : real; begin power := exp (expoente*ln(base)); end; Function GrausToRad (G : Real) : Real; Begin GrausToRad := G*PI/180; End; Function MySin (X : Real) : Real; Begin MySin := + X Introdução à Programação: Conceitos e Práticas. Página 130 + + Power(X, Power(X, Power(X, Power(X, 3)/(3*2) 5)/(5*4*3*2) 7)/(7*6*5*4*3*2) 9)/(9*8*7*6*5*4*3*2); End; Var Graus, Rad, Y1, Y2 : Real; Begin Writeln ('Entre com um ângulo em graus'); Readln (Graus); Rad := GrausToRad (Graus); Y1 := Sin (Rad); Y2 := MySin (Rad); Writeln ('Seno Real (',Graus:5:1,') = ', Y1:15:12); Writeln ('Seno Meu (',Graus:5:1,') = ', Y2:15:12); End. Para calcular o pela série dada, o ângulo deve estar em radianos. O programa principal, que testará a lógica, solicitará ao usuário que forneça um ângulo para o cálculo do . Não é razoável que o programa requeira a entrada em radianos, uma vez que não é a forma mais direta de compreensão. Assim, o programa solicita a entrada de um número que representa o ângulo em graus. Em seguida, uma expressão invocando a função converte o ângulo para radianos e armazena na variável . As duas linhas que seguem calculam o pela função da biblioteca ( ) e pela função implementada ( ). Na sequência os resultados são apresentados na tela. Observe a interface abaixo com três execuções seguidas, para os ângulos e graus. Entre com um ângulo em graus 30 Seno Real ( 30.0) = 0.500000000000 Seno Meu ( 30.0) = 0.500000000020 Entre com um ângulo em graus 60 Seno Real ( 60.0) = 0.866025403784 Seno Meu ( 60.0) = 0.866025445100 Entre com um ângulo em graus 1110 Seno Real (1110.0) = 0.500000000000 Seno Meu (1110.0) = 877678.043396671000 Podemos observar que os resultados para e graus apresentam uma boa precisão, mesmo com a grande simplificação feita nos cálculos. Porém, para o ângulo de graus, cujo seno deveria ser o mesmo que o seno de graus Introdução à Programação: Conceitos e Práticas. Página 131 tem-se uma discrepância alarmante. Isto se deve ao fato de que a série possui termos do tipo . No caso de temos uma convergência rápida dos termos. Quando o valor de for muito grande os cálculos divergem e levam a valores como o visto acima. A solução seria reduzir o ângulo para os quadrantes iniciais através da seguinte operação com o ângulo em radianos: ( ) A seguinte solução acrescenta a função que faz a redução do ângulo ( e inclui este cálculo ( ) na função : ) Program ExSENO; function power (base, expoente : real) : real; begin power := exp (expoente*ln(base)); end; Function Reduzir (X : Real) : Real; Begin Reduzir := Frac(X/(2*PI))*(2*PI); End; Function GrausToRad (G : Real) : Real; Begin GrausToRad := G*PI/180; End; Function MySin (X : Real) : Real; Begin X := Reduzir (X); MySin := + X - Power(X, 3)/(3*2) + Power(X, 5)/(5*4*3*2) - Power(X, 7)/(7*6*5*4*3*2) + Power(X, 9)/(9*8*7*6*5*4*3*2); End; Var Graus, Rad, Y1, Y2 : Real; Begin Writeln ('Entre com um ângulo em graus'); Readln (Graus); Rad := GrausToRad (Graus); Y1 := Sin (Rad); Y2 := MySin (Rad); Writeln ('Seno Real (',Graus:5:1,') = ', Y1:15:12); Writeln ('Seno Meu (',Graus:5:1,') = ', Y2:15:12); Introdução à Programação: Conceitos e Práticas. Página 132 End. Processando novamente este programa para as entradas de dados para os ângulos e graus tem-se: Entre com um ângulo em graus 30 Seno Real ( 30.0) = 0.500000000000 Seno Meu ( 30.0) = 0.500000000020 Entre com um ângulo em graus 60 Seno Real ( 60.0) = 0.866025403784 Seno Meu ( 60.0) = 0.866025445100 Entre com um ângulo em graus 1110 Seno Real (1110.0) = 0.500000000000 Seno Meu (1110.0) = 0.500000000020 O seno de de graus. graus pela função ficou mais adequado e igual ao Mesmo com todos estes ajustes, esta forma de implementação da lógica do seno ainda é muito primitiva e será consideravelmente melhorada na medida em que forem conhecidos outros recursos da linguagem. Introdução à Programação: Conceitos e Práticas. Página 133 17. Passagem de Parâmetros O método de passagem de parâmetros estabelece como se dá a ligação entre argumento e parâmetro. A linguagem Pascal permite duas formas de passagem de parâmetros, sendo elas por e por . 17.1. Por Cópia A passagem de parâmetro por cópia é o método conceito será detalhado a partir do seguinte exemplo: na linguagem Pascal. O function foo(a : integer; b : integer) : integer; 1 begin a := a + 1; b := 2*b; foo := a + b; 2 end; var x, y, z : integer; begin x := 7; y := 3; z := foo(x, y); Writeln (x); 3 Writeln (y); Writeln (z); end. Neste exemplo, tem-se a função que trabalha com dois parâmetros ( e ) e um programa principal que utiliza esta função através da linha de código: .A figura a seguir ilustra o contexto da função e de sua utilização. Introdução à Programação: Conceitos e Práticas. Página 134 Figura 51: Contexto da chamada da função foo. A ligação entre os argumentos e os parâmetros se dá pelo método da , onde simplesmente o valor do é copiado para o . Neste exemplo, no momento da chamada o valor do primeiro argumento ( ) é copiado para o parâmetro , o mesmo acontecendo para o segundo argumento, cujo valor é copiado para o parâmetro . Ao executar este programa, a seguinte tela de saída será gerada decorrente dos : 7 3 14 Para melhor compreender estes valores, vamos apresentar a configuração de memória em três momentos distintos, destacados no código fonte como pontos e . i. Início da função : Neste momento os valores dos argumentos foram copiados para os parâmetros. Nesta forma de passagem de parâmetro não há compartilhamento de memória. Introdução à Programação: Conceitos e Práticas. Página 135 Figura 52: Ponto 1 – Passagem por Cópia. ii. Final da função : Neste momento a função inteira já foi processada. Os valores dos parâmetros e foram alterados e não afetam os argumentos, pois são áreas distintas de memória. A área destinada ao resultado da função contém o valor final. Figura 53: Ponto 2 – Passagem por Cópia. iii. Após a execução da linha de código que utiliza a função : Após o término da função e retorno ao programa chamador as áreas de memória destinadas aos parâmetros e resultado da função são liberadas. Particularmente o resultado da função é copiado para uma área temporária onde o programa chamador pode acessar o valor e armazenar em uma área definitiva, no caso a variável . Introdução à Programação: Conceitos e Práticas. Página 136 Figura 54: Ponto 3 – Passagem por Cópia. 17.2. Por Referência Outra forma de passagem de parâmetros é por , onde o argumento deve ser necessariamente uma variável (endereço de memória) e o parâmetro ficará vinculado a este argumento. . Na linguagem o programador define esta forma de passagem de parâmetro acrescentando o termo a frente do nome do parâmetro. Segue um exemplo semelhante ao programa anterior. function foo(var a : integer; b : integer) : integer; 1 begin a := a + 1; b := 2*b; foo := a + b; 2 end; var x, y, z : integer; begin x := 7; y := 3; z := foo(x, y); 3 Writeln (x); Writeln (y); Writeln (z); end. Observe que a única alteração no código foi o uso do na definição do parâmetro . Ao executar este programa, a seguinte tela de saída será gerada decorrente dos : 8 3 Introdução à Programação: Conceitos e Práticas. Página 137 14 A diferença em relação ao programa anterior está na variável , que passou a conter o valor . Isto foi consequência do método de passagem de parâmetro. Observar na sequência a visualização do comportamento do programa, nos pontos e , perante esta modificação. i. Início da função : O diagrama abaixo apresenta uma diferença visual entre as duas formas de passagem de parâmetros. O parâmetro utiliza o mecanismo de passagem por referência e o parâmetro utiliza o mecanismo de passagem por cópia. A característica principal da passagem de parâmetro por referência está no . Logo, a representação gráfica utiliza uma única célula para destacar esta característica. As referências ao parâmetro afetam diretamente a célula da variável . Figura 55: Ponto 1 – Passagem por Referência. ii. Final da função : Neste momento a função já foi processada. Os valores dos parâmetros e foram alterados. Devido à forma distinta de passagem dos parâmetros, a alteração em afeta , enquanto que a alteração em não afeta . A área destinada ao resultado da função contém o valor final. Introdução à Programação: Conceitos e Práticas. Página 138 Figura 56: Ponto 2 – Passagem por Referência. iii. Após a execução da linha de código que utiliza a função : Após o término da função e retorno ao programa chamador as áreas de memória destinadas aos parâmetros e resultado da função são liberadas. Particularmente o resultado da função é copiado para uma área temporária onde o programa chamador pode acessar o valor e armazenar em uma área definitiva, no caso a variável . A variável que foi utilizada como argumento reterá o último valor do parâmetro a, como consequência da forma de passagem de parâmetro. Figura 57: Ponto 3 – Passagem por Referência. Assim, fica demonstrada a razão por que processamento da função. apresentará o valor ao final do A passagem de parâmetro por referência é utilizada quando se deseja capturar os cálculos feitos por um módulo. Nesta situação, basta o programador utilizar uma variável como argumento e estabelecer a ligação argumento/parâmetro pelo método de passagem por referência. Quando o módulo produz apenas um resultado que interessa ao programa chamador, é usual que o programador utilize uma para implementar a solução. Porém, se o módulo deve produzir mais de um resultado que interessa ao programa chamador, então a passagem de parâmetro por referência pode ser utilizada para capturar estes valores. Introdução à Programação: Conceitos e Práticas. Página 139 Nesta situação, recomenda-se codificar o módulo sob a forma de e não de . A diferença básica entre estas duas formas é que a procedure não permite associar um resultado ao seu nome, não podendo assim, ser invocada dentro de uma expressão. Dois bons exemplos de são os módulos e . A utiliza o mecanismo de passagem por referência para os seus parâmetros. Isto explica a exigência para que os argumentos do sejam variáveis (endereços de memória). A utiliza o mecanismo de passagem por cópia e por esta razão seus argumentos podem ser expressões. Cada uma das expressões é avaliada no momento da chamada e um valor é produzido para o argumento. Introdução à Programação: Conceitos e Práticas. Página 140 18. Implementando Procedures No tópico anterior sobre passagem de parâmetros foi visto que o mecanismo de passagem por referência combinado com permite que sejam transferidos vários resultados internos da para o módulo chamador. O problema do cálculo das raízes de uma equação do segundo grau ilustra bem o caso de um módulo que deve produzir mais de um resultado. É esperado que uma lógica deste tipo produzisse pelo menos as duas raízes. Assim, segue uma solução para o problema de obter as raízes de uma equação do segundo grau a partir dos coeficientes , e . As raízes são calculadas pela expressão: √ Segue a primeira versão de um programa para o cálculo das raízes. procedure raizes (a, b, c : real; var r1, r2 : real); var d : real; 1 begin d := b*b - 4*a*c; r1 := (-b + sqrt(d))/(2*a); r2 := (-b - sqrt(d))/(2*a); 2 end; var x1, x2 : real; begin raizes (1, -12, 35, x1, x2); writeln ('raiz 1 = ', x1:8:4); 3 writeln ('raiz 2 = ', x2:8:4); end. A solução adotada utiliza procedure, onde os parâmetros , e são passados por cópia e os parâmetros e são por referência. Estes últimos parâmetros servirão também como mecanismo para transferir o resultado para o programa chamador, que neste caso é o programa principal. O diagrama mais adiante ilustra o contexto da . Introdução à Programação: Conceitos e Práticas. Página 141 Observar na sequência a visualização do comportamento do programa, nos pontos e : i. Início da procedure : O diagrama abaixo destaca as duas formas de passagem de parâmetros. Os parâmetros , e utilizam o mecanismo de passagem por cópia e os parâmetros e utilizam o mecanismo de passagem por referência. A chamada da no programa principal é dada por: Os argumentos e são copiados para os parâmetros , e , enquanto que as variáveis e são passadas por referências, ficando ligadas aos parâmetros e . Os valores iniciais de e são desconhecidos, uma vez que não foram inicializadas. Figura 58: Ponto 1 – Calculo Raizes. Introdução à Programação: Conceitos e Práticas. Página 142 ii. Final da procedure : Neste momento a procedure já foi processada. As raízes foram calculadas e armazenadas nos parâmetros e . Figura 59: Ponto 2 – Calculo Raizes. iii. Após a execução da linha de código que utiliza a procedure : Após o término da execução da procedure e retorno ao programa chamador, imediatamente após a linha as áreas de memória destinadas aos parâmetros e variáveis locais são liberadas. As variáveis e que foram utilizadas como argumentos reterão os últimos valores dos parâmetros e , como consequência da forma de passagem de parâmetro. Introdução à Programação: Conceitos e Práticas. Página 143 Figura 60: Ponto 3 – Calculo Raizes. Ao imprimir os valores de e , será visualizada a seguinte tela: raiz 1 = raiz 2 = Introdução à Programação: Conceitos e Práticas. 7.0000 5.0000 Página 144 19. Expressões Textuais As expressões textuais expandem nossa capacidade de processar informações para além da manipulação de números. A linguagem apresentava nas suas primeiras versões uma característica bem marcante que era a vocação para codificar fórmulas matemáticas. Porém, a demanda por programas que inclui a necessidade de tratamento de textos impulsionou as linguagens no sentido de prover estes recursos. Desta maneira, as expressões textuais permitem combinar constantes, variáveis, operadores e funções de tal forma a prover transformações sobre este tipo de dados. 19.1. Tipos Textuais Os tipos de dados básicos para manipulação de textos disponíveis no são 15 o e a . O tipo ocupa de memória e permite armazenar um caractere . O tipo ocupa até de memória e permite armazenar uma sequencia de caracteres . 19.2. Constantes Observe o seguinte exemplo, onde são apresentadas as declarações de variáveis do tipo e , bem como as formas de se representar um valor literal, ou . As variáveis , , , uma delas. As variáveis 15 , , e e são do tipo são do tipo e ocupam de memória cada e foram definidas de tal forma a Não disponível no Pascal padrão, mas disponível nas principais implementações. Introdução à Programação: Conceitos e Práticas. Página 145 ocuparem , e de memória e permitem armazenar textos com até , e caracteres. Ao não indicar o tamanho de uma variável , é assumido o comprimento máximo de caracteres acrescido de caractere para controle da linguagem totalizando de memória. De modo semelhante ao que foi feito com as variáveis numéricas, vamos apresentar o mapa de memória resultante das atribuições feitas. A seguinte tabela apresenta os endereços de memória para cada uma das variáveis bem como a quantidade de ocupa por elas. Program exemplo; Var C1 : Char; C2 : Char; C3 : Char; C4 : Char; C5 : Char; C6 : Char; S1 : String; S2 : String[30]; S3 : String[80]; Total #bytes 1 1 1 1 1 1 256 31 81 Var @C1 @C2 @C3 @C4 @C5 @C6 @S1 @S2 @S3 374 Endereço de Memória Decimal Hexa Offset 4243456 40C000 1 4243457 40C001 1 4243458 40C002 1 4243459 40C003 1 4243460 40C004 1 4243461 40C005 1 4243462 40C006 256 4243718 40C106 31 4243749 40C125 81 374 A coluna indica a diferença entre os endereços de duas variáveis declaradas em sequência, permitindo observar se foram utilizados bytes de preenchimento decorrentes do alinhamento de endereço. Neste caso, tanto o tipo , quanto o tipo não tem seus endereços iniciais alinhados. Introdução à Programação: Conceitos e Práticas. Página 146 i. Início do programa, porém nenhuma instrução executada: End. Var Célula End. Var Célula End. Var Célula 1 40C000 C1 $00 25 40C018 $00 298 40C129 $00 2 40C001 C2 $00 26 40C019 $00 299 40C12A $00 3 40C002 C3 $00 263 40C106 $00 300 40C12B $00 4 40C003 C4 $00 264 40C107 $00 301 40C12C $00 5 40C004 C5 $00 265 40C108 $00 302 40C12D $00 6 40C005 C6 $00 266 40C109 $00 303 40C12E $00 7 40C006 S1 S2 $00 267 40C10A $00 304 40C12F $00 8 40C007 $00 268 40C10B $00 305 40C130 $00 9 40C008 $00 269 40C10C $00 306 40C131 $00 10 40C009 $00 270 40C10D $00 307 40C132 $00 11 40C00A $00 271 40C10E $00 308 40C133 $00 12 40C00B $00 272 40C10F $00 309 40C134 $00 13 40C00C $00 273 40C110 $00 310 40C135 $00 14 40C00D $00 274 40C111 $00 311 40C136 $00 15 40C00E $00 275 40C112 $00 312 40C137 $00 16 40C00F $00 276 40C113 $00 313 40C138 $00 17 40C010 $00 277 40C114 $00 314 40C139 $00 18 40C011 $00 278 40C115 $00 315 40C13A $00 19 40C012 $00 279 40C116 $00 316 40C13B $00 20 40C013 $00 280 40C117 $00 317 40C13C $00 21 40C014 $00 294 40C125 $00 318 40C13D $00 22 40C015 $00 295 40C126 $00 319 40C13E $00 23 40C016 $00 296 40C127 $00 320 40C13F $00 24 40C017 $00 297 40C128 $00 321 40C140 $00 S3 Pode ser observado que os estão com valor zero, porém cabe reforçar que esta pode não ser a ação de outros compiladores, ficando a recomendação para que o programador inicialize explicitamente as variáveis. A tabela ilustra apenas a parte inicial do espaço de memória ocupado pelas variáveis , e a fim de não tornar muito extenso o quadro. Introdução à Programação: Conceitos e Práticas. Página 147 ii. Todas as variáveis do tipo char inicializadas: End. Var Célula End. Var Célula End. Var Célula 1 40C000 C1 $61 25 40C018 $00 298 40C129 $00 2 40C001 C2 $5B 26 40C019 $00 299 40C12A $00 3 40C002 C3 $A7 263 40C106 $00 300 40C12B $00 4 40C003 C4 $A8 264 40C107 $00 301 40C12C $00 5 40C004 C5 $03 265 40C108 $00 302 40C12D $00 6 40C005 C6 $41 266 40C109 $00 303 40C12E $00 7 40C006 S1 S2 $00 267 40C10A $00 304 40C12F $00 8 40C007 $00 268 40C10B $00 305 40C130 $00 9 40C008 $00 269 40C10C $00 306 40C131 $00 10 40C009 $00 270 40C10D $00 307 40C132 $00 11 40C00A $00 271 40C10E $00 308 40C133 $00 12 40C00B $00 272 40C10F $00 309 40C134 $00 13 40C00C $00 273 40C110 $00 310 40C135 $00 14 40C00D $00 274 40C111 $00 311 40C136 $00 15 40C00E $00 275 40C112 $00 312 40C137 $00 16 40C00F $00 276 40C113 $00 313 40C138 $00 17 40C010 $00 277 40C114 $00 314 40C139 $00 18 40C011 $00 278 40C115 $00 315 40C13A $00 19 40C012 $00 279 40C116 $00 316 40C13B $00 20 40C013 $00 280 40C117 $00 317 40C13C $00 21 40C014 $00 294 40C125 $00 318 40C13D $00 22 40C015 $00 295 40C126 $00 319 40C13E $00 23 40C016 $00 296 40C127 $00 320 40C13F $00 24 40C017 $00 297 40C128 $00 321 40C140 $00 S3 Este segundo quadro permite que seja visualizado o efeito das atribuições realizadas às variáveis do tipo char. Instrução C1 := 'a'; C2 := '['; C3 := #167; C4 := #$A8; C5 := ^C; Comentário A célula apresenta o valor hexadecimal $61 (97) que corresponde ao código ASCII da letra 'a'. A célula apresenta o valor hexadecimal $5B (91) que corresponde ao código ASCII do caractere '['. A célula apresenta o valor hexadecimal $A7 (167) que corresponde ao código ASCII do caractere 'º'. A célula apresenta o valor hexadecimal $A8 (168) que corresponde ao código ASCII do caractere '¿'. A célula apresenta o valor hexadecimal $08 (8) que corresponde ao código Introdução à Programação: Conceitos e Práticas. Página 148 C6 := 'A'; ASCII do caractere '♥'. Equivale a Ctrl+Letra C. A célula apresenta o valor hexadecimal $41 (65) que corresponde ao código ASCII da letra 'A'. Uma constante pode ser escrita de várias formas, sendo que as atribuições ilustradas apresentam as mais usuais. A forma direta é envolver o caractere desejado entre apóstrofe. A segunda maneira é indicar o código precedido pelo caractere Os caracteres com código inferior a podem ser designados pelo caractere (equivalente a tecla ) seguido por uma letra do alfabeto iii. Após a execução de todas as instruções, porém antes de encerrar o programa: End. Var Célula End. Var Célula End. Var Célula 1 40C000 C1 $61 25 40C018 $00 298 40C129 $00 2 40C001 C2 $5B 26 40C019 $00 299 40C12A $00 3 40C002 C3 $A7 263 40C106 $00 300 40C12B $00 4 40C003 C4 $A8 264 40C107 $00 301 40C12C $00 5 40C004 C5 $03 265 40C108 $00 302 40C12D $00 6 40C005 C6 $41 266 40C109 $00 303 40C12E $00 7 40C006 S1 $00 267 40C10A $00 304 40C12F $00 8 40C007 $00 268 40C10B $00 305 40C130 $00 9 40C008 $00 269 40C10C $00 306 40C131 $00 10 40C009 $00 270 40C10D $00 307 40C132 $00 11 40C00A $00 271 40C10E $00 308 40C133 $00 12 40C00B $00 272 40C10F $00 309 40C134 $00 13 40C00C $00 273 40C110 $00 310 40C135 $00 14 40C00D $00 274 40C111 $00 311 40C136 $00 15 40C00E $00 275 40C112 $00 312 40C137 $00 16 40C00F $00 276 40C113 $00 313 40C138 $00 17 40C010 $00 277 40C114 $00 314 40C139 $00 18 40C011 $00 278 40C115 $00 315 40C13A $00 19 40C012 $00 279 40C116 $00 316 40C13B $00 20 40C013 $00 280 40C117 $00 317 40C13C $00 21 40C014 $00 294 40C125 $00 318 40C13D $00 22 40C015 $00 295 40C126 $00 319 40C13E $00 23 40C016 $00 296 40C127 $00 320 40C13F $00 24 40C017 $00 297 40C128 $00 321 40C140 $00 S2 S3 Agora temos um quadro completo incluindo a configuração de memória para as variáveis do tipo . Os detalhes da atribuição , são Introdução à Programação: Conceitos e Práticas. Página 149 apresentados no mapa a seguir. O primeiro byte está na posição [ ] e o primeiro caractere na posição [ ]. O primeiro byte está reservado para a linguagem e é utilizado para armazenar o comprimento da , que é dado pela quantidade de caracteres atribuídos à variável. Assim, o texto possui dezesseis caracteres, o que fez surgir na posição [ ] o valor que equivale a em decimal. As demais posições contém o valor numérico correspondente ao código do caractere atribuído. 40C01B S1 $10 s1[0] 40C01C $4C s1[1] L 40C01D $69 s1[2] i 40C01E $6E s1[3] n 40C01F $67 s1[4] g 40C020 $75 s1[5] u 40C021 $61 s1[6] a 40C022 $67 s1[7] g 40C023 $65 s1[8] e 40C024 $6D s1[9] m 40C025 $20 s1[10] 40C026 $50 s1[11] P 40C027 $61 s1[12] a 40C028 $73 s1[13] s 40C029 $63 s1[14] c 40C02A $61 s1[15] a 40C02B $6C s1[16] l 40C02C $00 s1[17] Fica para o leitor analisar os efeitos na memória e na tela das atribuições em seus respectivos . e e S2 := 'Ola Mundo'#33; S3 := 'Linha 1'#10'Linha 2'#10'Linha 3'; 19.3. Operadores O operador disponível para textos é o de concatenação representado pelo símbolo . São necessários dois operandos do tipo texto, a esquerda e a direita, produzindo como resultado um texto formado pela junção dos caracteres destes operandos. O seguinte exemplo ilustra esta operação: Introdução à Programação: Conceitos e Práticas. Página 150 Var Sa, Sb, Sc : String; Begin Sa := 'Ciencia'; Sb := 'Computacao'; Sc := Sa + ' da ' + Sb; Writeln (Sc); End. A variável irá conter . É comum pressupor que a linguagem adiciona um espaço em branco entre os textos concatenados, o que não é fato em Pascal, ficando ao programador a incumbência de codificar este caractere separador, caso seja necessário. 19.4. Funções A linguagem Pascal disponibiliza algumas funções e procedimentos que podem ser utilizadas na manipulação de textos. Segue uma descrição resumida de cada uma delas: 1) Retorna o comprimento (quantidade de caracteres) de uma comprimento máximo é e uma vazia tem comprimento zero. . O Exemplo: Program ExLENGTH; Var Sa : String; N : Integer; Begin Writeln ('Entre com uma frase'); Readln (Sa); N := Length(Sa); Writeln ('O comprimento deste texto é: ', N); Writeln ('O ultimo caractere é: ', Sa[N]); end. A interface de entrada e saída para o programa é: Entre com uma frase Ola mundo belo O comprimento deste texto é: 14 O ultimo caractere é: o Introdução à Programação: Conceitos e Práticas. Página 151 2) Retorna o equivalente maiúsculo de um somente ao tipo char, enquanto que no argumento . . No padrão se aplica é aceito também um Exemplo: Program ExUPCASE; Var Sa : String; Ch : Char; Begin Writeln ('Entre com uma frase toda em minuscula'); Readln (Sa); Ch := UpCase(Sa[1]); Writeln ('O equivalente maiusculo do primeiro char é: ', Ch); end. A interface de entrada e saída para o programa é: Entre com uma frase toda em minuscula ola mundo belo O equivalente maiusculo do primeiro char é: O 3) Retorna o caractere correspondente a posição na tabela . Exemplo: Program ExCHR; Var I : Integer; Ch : Char; Begin Writeln ('Entre com um número entre 32 e 128'); Readln (I); Ch := Chr(I); Writeln ('O caractere ASCII numero ', I, ' é o ''', Ch,''''); end. A interface de entrada e saída para o programa é: Entre com um número entre 32 e 128 76 O caractere ASCII numero 76 é o 'L' Introdução à Programação: Conceitos e Práticas. Página 152 4) Retorna o valor inteiro utilizado para representar o ordinal. São considerados ordinais os tipos , , e . No caso do tipo é retornada a posição na tabela . Exemplo: Program ExORD; Var B : Integer; Ch : Char; Begin B := Ord('a')- Ord('A'); Writeln ('A distancia entre ''a'' e ''A'' = ', B); Ch := Chr (Ord('D') + B); Writeln ('O minúsculo da letra ''D'' = ''', Ch, ''''); Writeln ('False = ', Ord(False)); end. A interface de entrada e saída para o programa é: A distancia entre 'a' e 'A' = 32 O minúsculo da letra 'D' = 'd' False = 0 5) Retorna a posição inicial de uma string em outra. Caso contida em , então zero é retornado. Substr S o 1 l 2 a 3 4 m u n d o m 5 u n d o b e l o 6 7 8 9 10 11 12 13 14 não esteja Pos = 5 Exemplo: Program ExPOS; Introdução à Programação: Conceitos e Práticas. Página 153 Var B : Integer; S : String; Ch : Char; Begin Readln (S); Ch := UpCase(S[1]); B := Pos (Ch, 'AEIOU'); Writeln (B); // // // // // Obter uma string Ch := maiusculo do primeiro caractere. B := posicao de Ch em 'AEIOU' Imprimirá zero se Ch não for vogal ou a posição de Ch na string dada. end. A interface de entrada e saída para o programa é: Ola Mundo Belo 4 6) Retorna uma parte de uma caracteres de a partir da posição . O resultado é uma cópia dos . index = 5 S o 1 l 2 a 3 4 m 5 u n d o b e l o 6 7 8 9 10 11 12 13 14 Count = 4 m u n d Exemplo: Program ExCOPY; Var Sx, S : String; Begin S := 'ola mundo belo'; Sx := Copy (S, 5, 4); Writeln (Sx); Introdução à Programação: Conceitos e Práticas. // imprimirá 'mund' Página 154 end. A interface de entrada e saída para o programa é: mund 7) A procedure converte um número para o formato . O primeiro parâmetro é um valor inteiro ou real e o segundo parâmetro deve ser a variável que receberá a . Uma especificação de formato pode ser adicionada ao número. Exemplo: Program ExSTR; Function IntToStr (I : Longint) : String; Var S : String; begin Str (I, S); IntToStr := S; end; Function RealToStr (R : Real) : String; Var S : String; begin Str (R:9:6, S); RealToStr := S; end; Var L : String; begin L :='[' + IntToStr(-475) + ']'; Writeln (L); L :='[' + RealToStr(PI) + ']'; Writeln (L); end. A interface de entrada e saída para o programa é: [-475] [ 3.141593] 8) Introdução à Programação: Conceitos e Práticas. Página 155 A procedure converte uma para número. O primeiro parâmetro é a cujo conteúdo se deseja converter para o seu equivalente numérico. O segundo parâmetro é o endereço de memória onde será armazenado o valor convertido. O terceiro parâmetro é o endereço de memória onde será depositado um número inteiro que indica o da conversão. Caso a conversão foi bem sucedida, este último parâmetro conterá zero, caso contrário conterá a posição do primeiro caractere que violou a conversão. Exemplo: Program ExVAL; Var S, Sx : String; X, P1, P2, C : Integer; Begin S := '>>>>>> [368] <<<<<<'; P1 := Pos ('[', S); P2 := Pos (']', S); Sx := Copy(S, P1+1, P2-P1-1); Val (Sx, X, C); Writeln (X); End. Neste exemplo, uma possui em qualquer posição um número escrito entre colchetes. Utilizando são obtidas as posições ( e ) dos caracteres [ e ] . Em seguida é utilizada a função para obter uma ( ) contendo somente o número em formato texto ( ). Por fim, a procedure converte este texto para número, armazena em , e imprime na tela. A interface de entrada e saída para o programa é: 368 19.5. Exemplos i. Elaborar um programa que isola a primeira palavra de uma . Considerar que o texto dado contenha várias palavras separadas por um espaço em branco. Incluir também a lógica que retorna uma cópia da sem a primeira palavra. A solução proposta utiliza uma função denominada para obter a primeira palavra de uma , e a função para retornar o texto sem a primeira palavra. A lógica da função é dada por: Function Car (S : String) : String; Introdução à Programação: Conceitos e Práticas. Página 156 Var P : Integer; Begin P := Pos (' ', S); Car := Copy (S, 1, P-1); End; A linha de código espaço em branco. A função armazenado em . computa a posição em da primeira ocorrência do é utilizada par obter esta informação. O resultado é Com o valor de P já é possível obter a primeira palavra, bastando para isto copiar um pedaço de , iniciando na primeira posição e quantidade de caracteres igual a , tal como codificado na linha . A função tem lógica semelhante. Segue implementação completa. Program ExCAR; Function Car (S : String) : String; Var P : Integer; Begin P := Pos (' ', S); Car := Copy (S, 1, P-1); End; Function Cdr (S : String) : String; Var P : Integer; Begin P := Pos (' ', S); Cdr := Copy (S, P+1, 255); End; var L : String; Begin L := 'Ola Mundo Belo'; Writeln (Car (L)); Writeln (Cdr (L)); End. A interface de entrada e saída para o programa é: Ola Mundo Belo ii. Partindo das funções já codificadas, pede-se: elaborar uma solução que forneça separadamente a segunda palavra e a terceira palavra de uma . O texto informado é composto por pelo menos três palavras separadas por um espaço em branco. Introdução à Programação: Conceitos e Práticas. Página 157 Program ExCAR2; Function Car (S : String) : String; Var P : Integer; Begin P := Pos (' ', S); Car := Copy (S, 1, P-1); End; Function Cdr (S : String) : String; Var P : Integer; Begin P := Pos (' ', S); Cdr := Copy (S, P+1, 255); End; Function Car2 (S : String) : String; Begin Car2 := car(cdr(S)); End; Function Car3 (S : String) : String; Begin Car3 := car(cdr(cdr(S))); End; var L : String; Begin L := 'esta frase tem mais de tres palavras'; Writeln (Car2 (L)); Writeln (Car3 (L)); End. O código do exemplo anterior foi completado com as funções e que retornam respectivamente a segunda palavra e a terceira palavra de uma . Observar que a lógica utiliza uma combinação das funções e para obter o processamento desejado. Os detalhes sobre a posição do espaço em branco e da cópia do texto desejado ficam apenas nas funções e . A segunda palavra de uma string pode ser obtida, primeiramente tomando a original sem sua primeira palavra e desta string isolando a primeira palavra . A mesma estratégia pode ser empregada para a função , que retorna a terceira palavra. A interface de entrada e saída para o programa é: frase tem Introdução à Programação: Conceitos e Práticas. Página 158 20. Expressões Lógicas Expressão lógica é um recurso fundamental nas linguagens de programação, pois é elemento presente na construção de qualquer algoritmo significativo. O tipo de dado e os operadores lógicos formam a base para estas expressões. Os operadores lógicos trabalham sobre os valores e , que representam condições ou , produzindo resultados deste mesmo conjunto. 20.1. Tipo de Dado Boolean Em , o tipo de dado admite os valores { exemplo ilustra o tipo . é a base para as expressões lógicas. Uma variável } e ocupa de memória. O seguinte Program ExBoolean; Var B : Boolean; Begin B := False; Writeln (Ord (B)); B := True; Writeln (Ord (B)); End. Observe no exemplo a atribuição dos valores e à variável . O tipo é um ordinal, sendo que o é zero e o é . É importante observar que as constantes são e sem o uso de apóstrofes. De forma semelhante ao descrito anteriormente, uma expressão lógica combina variáveis, operadores lógicos, constantes e funções a fim de produzir um resultado que também é um valor lógico. 20.2. Operadores Lógicos O tipo de dado verdade: admite as seguintes operações lógicas e respectivas tabelas Operadores i. O resultado é quando todos os operandos forem . Introdução à Programação: Conceitos e Práticas. Tabelas-Verdade AND F F F T F T F T AND A B X F F F Página 159 F T T T F T F F T A F F T T OR B F T F T X F T T T A F F T T OR B F T F T X F T T F ii. O resultado é quando todos os operandos forem . OR F T F F T T T T OR F T F F T T F NOT F T T F iii. É um exclusivo, ou seja difere do quando os dois operandos forem , produzindo neste caso um resultado . iv. Retorna a negação do valor do operando. NOT A X F T F O seguinte exemplo ilustra o uso de operadores lógicos: Var B : Boolean; Begin B := (True Or False) And (True Xor True); Writeln (B); // imprimirá False B := Not B; Writeln (B); // imprimirá True End. A expressão Introdução à Programação: Conceitos e Práticas. é avaliada da seguinte forma: Página 160 O operador também é chamado de e o operador de . Esta denominação permite compreender melhor a prioridade na execução das operações. O operador é executado antes que o operador quando estiverem no mesmo nível na expressão. O parêntesis deve ser utilizado quando se deseja priorizar outra ordem. Está claro que codificar expressões lógicas operando somente sobre constantes e não permitirá construir programas práticos. Assim, normalmente utilizamos os para produzir os valores e que necessitamos em uma expressão lógica. 20.3. Operadores Relacionais Os operadores relacionais funcionam como se fossem perguntas feitas sobre seus operandos a fim de produzir respostas do tipo ou . A sintaxe destes operadores demanda a presença de dois operandos, sendo um à esquerda e outro à direita do operador. Os operandos podem ser expressões de qualquer tipo, porém compatíveis entre si. Assim, se um operando é de natureza textual ( ou ), o outro também deverá ser do tipo textual. Da mesma forma, se um operando produz um valor numérico ( ou ), o outro também deverá ser do tipo numérico. Os operadores relacionais do são: e , cuja descrição é: Operador Relacional i. Exemplos Var FA, FB, FC : Boolean; S : String; A : Integer; X : Real; Ch : Char; Avalia se o valor do primeiro argumento é ao valor do segundo argumento. Aplica-se tanto a valores numéricos quanto textuais. Begin ii. Avalia se o valor do primeiro argumento é que o valor do segundo argumento. No caso de argumentos o entendimento é o que normalmente utilizamos em matemática. Quando os argumentos forem , o termo se aplica a posição do elemento na tabela . Um char é que outro quando ocupa uma posição superior na tabela . Se os argumentos forem , o termo equivale a perguntar se o primeiro argumento vem após o segundo argumento no caso de ordem alfabética, também orientada pela tabela . iii. l) Introdução à Programação: Conceitos e Práticas. // FA será True se a string S conter // 'Joao' e a variável A for maior // que 30 e X diferente de zero. FA := (S = 'Joao') And (A > 30) And (X <> 0); // FB será True se o char em Ch for o // caractere 'a' ou 'e' ou 'i' FB := (Ch = 'a') Or (Ch = 'e') Or (Ch = 'i'); // FC será True se o char em Ch // estiver entre o caractere '0' // e '9' na tabela ASCII. FC := (Ch >= '0') And (Ch <= '9'); Página 161 Explicação semelhante ao . iv. Explicação semelhante ao . v. Explicação semelhante ao . vi. Explicação semelhante ao // FA será True se o valor de X // for maior que raiz quadrada de 2 // e menor que raiz quadrada de 3 FA := (X > Sqrt(2)) And (X < Sqrt(3)); // FB será True se o valor de X // for menor ou igual a // OU maior ou igual a FB := (X <= PI/2) OR (X >= PI); // // // // // FC será True se a string S contiver uma sequencia de caracteres que resulte alfabeticamente a frente (maior) da string 'Pedro' FC := (S > 'Pedro'); // // // // FA será True se o primeiro caractere da string S estiver entre o caracter 'a' e 'z' na tabela ASCII. FA := (S[1] >= 'a') AND (S[1] <= 'z'); End. 20.4. Exemplos i. Elaborar um programa que informa se um determinado caractere é uma vogal. A solução proposta utiliza uma função chamada que avalia se o caractere dado é ou não uma vogal. O programa principal serve para testar alguns casos com o objetivo de validar a lógica. A função pode ser codificada de várias maneiras, utilizando apenas os recursos já apresentados. Program ExLOGICA; Function IsVogal0 (Ch : Char) : Boolean; Begin Ch := UpCase (Ch); IsVogal0 := Pos(Ch, 'AEIOU') <> 0; End; Function IsVogal (Ch : Char) : Boolean; Begin Ch := UpCase (Ch); IsVogal := (Ch = 'A') OR (Ch = 'E') OR (Ch = 'I') OR (Ch = 'O') OR (Ch = 'U'); End; Introdução à Programação: Conceitos e Práticas. Página 162 Begin Writeln Writeln Writeln Writeln End. (IsVogal('a')); (IsVogal('O')); (IsVogal('0')); (IsVogal('T')); A primeira versão da função utiliza a função para determinar se um caractere é vogal ou não. Neste caso a pergunta que se faz é se a posição do caractere na string é diferente de zero. O caso afirmativo indica que é um dos caracteres indicados, ou seja, é uma vogal. Observar que antes disso o conteúdo da variável é substituído pelo seu equivalente maiúsculo, com o uso da função . A segunda solução utiliza uma expressão lógica completa composta por cinco perguntas, sendo cada uma delas avaliando se o caractere é uma das vogais. Um principiante em programação normalmente pergunta se a expressão não poderia ser escrita na forma: A resposta é não do tipo , pois os operadores lógicos necessitam de operandos booleanos e como utilizado. A interface de entrada e saída para o programa é: TRUE TRUE FALSE FALSE ii. Elaborar um programa que informa se um determinado caractere é uma consoante. A solução proposta utiliza duas funções de apoio, sendo uma a e outra a que avalia se um caractere é ou não uma letra do alfabeto. Assim, um caractere será uma consoante se for ao mesmo tempo uma letra e não for vogal. O programa principal serve para testar alguns casos com o objetivo de validar a lógica. Program ExLOGICA; Function IsLetra (Ch : Char) : Boolean; Begin Ch := UpCase (Ch); IsLetra := (Ch >= 'A') AND (Ch <= 'Z'); End; Function IsVogal (Ch : Char) : Boolean; Begin Introdução à Programação: Conceitos e Práticas. Página 163 Ch := UpCase (Ch); IsVogal := (Ch = 'A') OR (Ch = 'E') OR (Ch = 'I') OR (Ch = 'O') OR (Ch = 'U'); End; Function IsConsoante (Ch : Char) : Boolean; Begin Ch := UpCase (Ch); IsConsoante := IsLetra(Ch) And (Not IsVogal(Ch)); End; Begin Writeln Writeln Writeln Writeln End. A função (IsConsoante('a')); (IsConsoante('B')); (IsConsoante('x')); (IsConsoante('3')); testa se um caractere está entre o eo combina adequadamente as funções na tabela e . A função através da expressão: A função retornar retornará se retornar e . A interface de entrada e saída para o programa é: FALSE TRUE TRUE FALSE Introdução à Programação: Conceitos e Práticas. Página 164 21. Instruções de Controle Até o momento foram apresentados recursos de programação que permitem escrever algoritmos compostos por linhas de código cuja execução ocorre em sequência, uma linha após a outra. Ao concluir o processamento de uma linha a próxima será executada, e assim sucessivamente. A seguinte figura ilustra o fluxo de processamento de um módulo contendo apenas instruções executadas em . O processamento inicia no até alcançar o final do módulo no e flui executando cada uma das linhas de código, . Estas linhas de código, com os recursos vistos, podem ser do tipo comandos de atribuição ou chamadas de procedures. O lado direito dos comandos de atribuição pode incluir expressões com funções. Assim, quando uma linha de código invoca uma ou , tem-se um fluxo semelhante ao da figura que segue: Ainda nesta situação, tem-se o seguinte fluxo único para a execução do programa. Os problemas práticos demandam programas robustos o suficiente, que não produzam erros de execução que causam a parada do sistema. Isto significa dizer que as lógicas devem ser completas no sentido de incluir o tratamento adequado de possíveis erros e quando possível prevenir que instruções sejam executadas na iminência de falhas. Como exemplo, citamos a lógica já implementada de cálculo das duas raízes de uma equação do segundo grau. A lógica codifica apenas as expressões que levam ao cálculo das raízes. Porém, há erros potenciais que podem se manifestar quando as Introdução à Programação: Conceitos e Práticas. Página 165 expressões forem avaliadas, tais como a divisão por zero e a raiz de um delta negativo. Assim, o programador deve pensar na lógica que desvie o fluxo de processamento para um caminho seguro que evita a avaliação das expressões nos casos que levam a erro de execução. As linguagens de programação disponibilizam instruções que permitem ao programador fazer a entre caminhos alternativos de processamento. Outra categoria de problemas práticos requer a codificação de cálculos que repetem um mesmo padrão da lógica por inúmeras vezes. Um exemplo deste comportamento é o cálculo da função através de uma série, onde as parcelas possuem uma forma padrão. Para estes problemas o algoritmo normalmente submete a forma padrão a um processamento repetitivo de cálculo. Assim, é fundamental que a linguagem permita que uma ou mais instruções sejam processadas várias vezes sem que o programador tenha que duplicar códigos semelhantes. A estrutura que permite este recurso é denominada de . Em síntese, uma linguagem de programação de alto nível deve prover mecanismos de controle do fluxo de execução que se enquadrem nas seguintes categorias: ; ;e . Assim, é possível codificar qualquer problema computável combinando estas três construções de controle. 21.1. Seleção – IF-THEN-ELSE O disponibiliza uma instrução de seleção que possibilita ao usuário definir dois caminhos alternativos que serão escolhidos no momento da execução do programa dependendo do valor de uma expressão lógica. Como a expressão lógica admite dois valores possíveis, então um caminho de processamento é associado ao valor eo outro caminho é associado ao valor . 21.1.1. Sintaxe e Semântica A sintaxe da instrução é: Desenhando um diagrama para melhor visualizar a estrutura da , tem-se: Introdução à Programação: Conceitos e Práticas. Página 166 Se qualquer um dos caminhos tiver mais de uma instrução associada, então as instruções deverão estar definidas como blocos através do uso do ⋯ . A execução do inicia com a avaliação da expressão lógica e em seguida com o teste do valor resultante. Caso o valor da expressão seja então serão processadas as instruções associadas ao caminho . Caso contrário, serão executadas as instruções associadas ao caminho . A cláusula é opcional, o que proporciona uma variação da instrução: O diagrama para a é dado por: A execução do inicia com a avaliação da expressão lógica e em seguida com o teste do valor resultante. Caso o valor da expressão seja então serão processadas as instruções associadas ao caminho . Caso contrário, o processamento segue para a próxima instrução após a instrução . Introdução à Programação: Conceitos e Práticas. Página 167 21.1.2. Exemplos Os exemplos abaixo relacionados apresentam várias formas de utilização da instrução de controle a partir de diagramas com as lógicas. Diagrama Programa Program exIF_THEN; Var X, Y : Real; A, B : Integer; Begin If X > Y Then A := 1 Else A := 2; i. // Se a expressão for // executada a instrução // será executada a instrução If X > Begin A := B := End Else então será caso contrário . 10 Then 1; 2; A := B + 1; ii. // Se a expressão for então // serão executadas as duas instruções // associadas a cláusula , caso contrário // será executada a única instrução associada ao // . Observe o ⋯ envolvendo as // duas instruções associadas ao . iii. Introdução à Programação: Conceitos e Práticas. If X <= 10 Then A := 1 Else Begin A := 2; B := 1; End; // Observe a ausência do antes do Página 168 If X >= 10 Then Begin A := -1; B := -2; End Else Begin A := 3; B := 4; End; iv. End. anteriores que o presença de . Observe nos exemplos nunca vem precedido deste símbolo. Outra situação de destaque é a , como nos seguintes casos: If X > Y Then If X <> 0 Then A := 1 Else A := 2; If X > Y Then If X <> 0 Then A := 1 Else A := 2; Observe que as soluções acima são idênticas, pois possuem exatamente a mesma sequência de palavras ( ). O deslocamento ( ) do texto mais para esquerda ou para a direita não afeta em nada a forma como a linguagem interpreta a sentença. Assim, uma das duas construções não representa o diagrama correspondente. Está claro no desenho que são lógicas distintas. Este caso ficou conhecido no Algol como . A linguagem resolve esta ambiguidade afirmando que na dúvida a que pertence o , o fica vinculado ao mais interno. Havendo a necessidade em vincular o ao mais externo, um bloco ⋯ deverá ser utilizado, como indicado no código abaixo: If X > Y Then Begin If X <> 0 Then A := 1 End Else A := 2; Introdução à Programação: Conceitos e Práticas. If X > Y Then If X <> 0 Then A := 1 Else A := 2; Página 169 v. Calcular as raízes e de uma equação do segundo grau a partir dos coeficientes , e . A solução deverá produzir um valor que indica o dos cálculos de acordo com a seguinte convenção: 0 sem erro: raízes calculadas 1 se 2 se A solução adotada anteriormente para o cálculo das raízes poderá em fim ser completada com o tratamento adequado das condições que poderiam levar a um erro na execução da lógica. Assim, devemos construir uma nova lógica que desvia o fluxo de execução do módulo para que não ocorram nem nem . Observe no diagrama abaixo proposto, que a cláusula é utilizada para testar inicialmente o valor do coeficiente e desviar para o cálculo das raízes somente no caso de passar pelo teste de . Superado este primeiro teste, o é calculado e um novo teste é feito direcionando o fluxo para o cálculo das raízes somente em caso de . Nos casos de falha nos testes a cláusula else é executada atribuindo ao parâmetro a condição do erro. Figura 61: Diagrama lógico para o cálculo das raízes de uma equação do 2º grau. Program ExRaizes; procedure raizes (a, b, c : real; var r1, r2 : real; var code : integer); var d : real; begin Introdução à Programação: Conceitos e Práticas. Página 170 if a <> 0 then begin d := b*b - 4*a*c; if d >= 0 then begin code := 0; r1 := (-b + sqrt(d))/(2*a); r2 := (-b - sqrt(d))/(2*a); end else code := 2; end else code := 1; end; procedure show (code : integer; x1, x2 : real); begin If code = 0 Then begin Writeln ('Raiz 1 = ', x1:8:4); Writeln ('Raiz 2 = ', x2:8:4); end Else If Code = 1 Then writeln ('erro: a = 0') Else If Code = 2 Then writeln ('erro: delta < 0'); end; var a1, b1, c1, x1, x2 : real; code : integer; begin Writeln ('Entre com os coeficientes A, B e C:'); Readln (a1, b1, c1); raizes (a1, b1, c1, x1, x2, code); show (code, x1, x2); end. A solução proposta inclui o módulo do processamento da seguinte forma: Se Se Se que permite apresentar na tela os resultados mostrar as raízes calculadas mostrar a mensagem mostrar a mensagem Introdução à Programação: Conceitos e Práticas. Página 171 A seguinte tela mostra os resultados de quatro processamentos independentes do programa completo: Entre com os coeficientes A, B e C: 1 4 4 Raiz 1 = -2.0000 Raiz 2 = -2.0000 Entre com os coeficientes A, B e C: 1 6 3 Raiz 1 = -0.5505 Raiz 2 = -5.4495 Entre com os coeficientes A, B e C: 4 6 3 erro: delta < 0 Entre com os coeficientes A, B e C: 0 3 5 erro: a = 0 vi. Uma família de problemas muito comum em situações reais é a relacionada com a manipulação de datas. Assim, este exemplo pede uma solução para o cálculo da quantidade de dias de um determinado mês e ano. A solução consiste em retornar a quantidade de dias conforme o valor do parâmetro mês. Para os meses e a quantidade de dias é ; para os meses e a quantidade de dias é ; e para o mês a quantidade de dias é para anos normais e para ano bissexto. Assim, a solução proposta inclui uma função para indicar se um ano é bissexto ou não. Em função das inúmeras alterações que o calendário foi submetido ao longo dos tempos, acabou sendo consolidado o padrão solar. O ano solar, ou seja, o tempo que a leva para completar um ciclo em torno do te a duração de ou dias. Para sincronizar o calendário e o tempo solar foi introduzido, pelos imperadores e , o ano bissexto para compensar a fração de dia não considerada no total de dias do ano. Esta mudança fez com que os anos que fossem múltiplos de 4 passassem a ter dias, e os demais dias. Mesmo com esta alteração, a diferença entre a duração média do novo calendário com dias e o valor real de conduzia a defasagem significativa no longo prazo. Deste modo, o , em , suprimiu dias do calendário e determinou que o dia fosse seguido pelo dia . As regras para foram ajustadas e ficou estabelecido que16: de de de em anos é ano bissexto. em anos não é ano bissexto. em anos é ano bissexto. 16 http://pt.wikipedia.org/wiki/Ano_bissexto Leap Years: the rule. U.S. Naval Observatory (14 September 2007). Introdução à Programação: Conceitos e Práticas. Página 172 prevalecem as últimas regras sobre as primeiras. De forma simplificada os anos múltiplos de são bissextos. Os anos múltiplos de são excluídos, e os múltiplos de incluídos novamente. Segue a implementação proposta: Program exDATA; Function IsLeap (A : Integer) : Boolean; Begin IsLeap := (A Mod 4 = 0) And ((A Mod 100 <> 0) Or (A Mod 400 = 0)); End; Function NumberOfDays (M, A : Integer) : Integer; Begin If (M = 1) OR (M = 3) OR (M = 5) OR (M = 7) OR (M = 8) OR (M = 10) OR (M = 12) Then NumberOfDays := 31 Else If (M = 4) OR (M = 6) OR (M = 9) OR (M = 11) Then NumberOfDays := 30 Else If (M = 2) Then If IsLeap (A) Then NumberOfDays := 29 Else NumberOfDays := 28 Else NumberOfDays := 0; End; Begin Writeln Writeln Writeln Writeln Writeln Writeln End. (NumberOfDays (NumberOfDays (NumberOfDays (NumberOfDays (NumberOfDays (NumberOfDays ( 2, ( 1, ( 2, (12, ( 2, (15, 2000)); 1996)); 1996)); 1600)); 2013)); 2000)); A saída para o programa é: 29 31 29 31 28 0 O seguinte pode ser substituído por uma expressão: Introdução à Programação: Conceitos e Práticas. Página 173 If IsLeap (A) Then NumberOfDays := 29 Else NumberOfDays := 28 A função retorna seguinte expressão: ou , que se convertido para ou pode dar origem a NumberOfDays := 28 + Ord(IsLeap (A)); A expressão faz a conversão do valor para inteiro ( ou ). Assim, a função ficaria conforme segue: retornado por Function NumberOfDays (M, A : Integer) : Integer; Begin If (M = 1) OR (M = 3) OR (M = 5) OR (M = 7) OR (M = 8) OR (M = 10) OR (M = 12) Then NumberOfDays := 31 Else If (M = 4) OR (M = 6) OR (M = 9) OR (M = 11) Then NumberOfDays := 30 Else If (M = 2) Then NumberOfDays := 28 + Ord(IsLeap (A)) Else NumberOfDays := 0; End; vii. Implementar uma solução que incrementa em um dia uma data válida (dia, mês e ano). A solução consiste em incrementar o dia em , e testar se o resultado não extrapolou a quantidade de dias permitida para o mês. Em caso positivo, o dia passa para e o mês é incrementado de , testando agora se o mês não excedeu aos permitidos para o ano. Em caso positivo, o mês passa para e o ano é incrementado de . Segue esta solução: Program exDATA; Function IsLeap (A : Integer) : Boolean; { igual a implementação anterior} Function NumberOfDays (M, A : Integer) : Integer; { igual a implementação anterior} Procedure IncDate (Var D, M, A : Integer); Begin Inc (D); If D > NumberOfDays (M, A) Then Begin D := 1; Introdução à Programação: Conceitos e Práticas. Página 174 Inc (M); If M > 12 Then Begin M := 1; Inc (A); End; End; End; Var Dia, Mes, Ano : Integer; Begin Writeln Readln IncDate Writeln End. ('Entre com uma data: dd mm aaaa'); (Dia, Mes, Ano); (Dia, Mes, Ano); ('O dia seguinte é: ', Dia, Mes:3, Ano:5); A solução adotada utiliza como opção, uma vez que a melhor modelagem para este problema é fornecer três variáveis para a rotina e esperar que a lógica da modifique estas variáveis com os novos valores para a data adiantada em um dia. Em função disso, a adotada é a , permitindo que toda alteração nos parâmetros possa ser captada pelos argumentos. Os módulos e tiveram seus códigos omitidos para melhor visualização da solução. A seguinte tela ilustra as entradas e saídas para alguns testes: Entre com uma data: dd mm aaaa 1 1 2000 O dia seguinte é: 2 1 2000 Entre com uma data: dd mm aaaa 10 10 2010 O dia seguinte é: 11 10 2010 Entre com uma data: dd mm aaaa 31 1 2012 O dia seguinte é: 1 2 2012 Entre com uma data: dd mm aaaa 31 12 2009 O dia seguinte é: 1 1 2010 Introdução à Programação: Conceitos e Práticas. Página 175 21.2. Seleção – CASE A construção de controle do tipo proporciona dois caminhos alternativos para o fluxo de processamento. Uma expressão do tipo é utilizada como ponto de decisão para a escolha do trajeto a ser tomado. Um dos caminhos está associado ao valor e o caminho alternativo está associado ao valor . Em determinados problemas é comum que o código resultante se enquadre em um aninhamento (estruturas embutidas) de , como o visto na função . Especificamente para situações semelhantes, a linguagem disponibiliza a construção que permite a escolha de um entre múltiplos caminhos possíveis. O ponto de decisão deixa de ser uma expressão booleana, que admite apenas dois valores, e passa a ser uma expressão ordinal, que no caso dos inteiros e char, admitem múltiplos valores. 21.2.1. Sintaxe e Semântica A sintaxe da construção de controle é dada por: A versão original do não apresenta a cláusula . Algumas versões de compiladores aceitam a palavra como variação do . Se várias instruções estiverem associadas à condição, então deve ser utilizado o bloco ⋯ para envolver as instruções. O seguinte diagrama ilustra o significado da construção : Introdução à Programação: Conceitos e Práticas. Página 176 Figura 62: Instrução CASE Em grande parte dos problemas a expressão ordinal utilizada para direcionar o é do tipo inteiro ou char. Além destes, a expressão pode ser do tipo enumerado, cujo conceito será visto mais adiante. Não é permitido o uso de expressão do tipo ou , pois estes tipos não são ordinais. O diagrama anterior indica que o inicia com uma expressão ordinal, cujo resultado é testado quanto a pertinência a um conjunto de valores, que em caso positivo as instruções associadas serão executadas. Em caso negativo, é feito o teste seguinte em relação a outro conjunto de valores, que em caso positivo as instruções associadas serão executadas, e assim sucessivamente. Opcionalmente, pode ser incluída uma cláusula que será executada no caso de falha dos testes anteriores. Os exemplos que seguem destacam as formas de uso desta construção. Introdução à Programação: Conceitos e Práticas. Página 177 21.2.2. i. Exemplos Refazer o programa que informa se um determinado caractere é uma consoante. As funções e código listado abaixo: serão reescrita utilizando a construção , conforme o Program ExLOGICA; Function IsLetra (Ch : Char) : Boolean; Begin Case UpCase(Ch) Of 'A'..'Z' : IsLetra := True; Else IsLetra := False; End; End; Function IsVogal (Ch : Char) : Boolean; Begin Case UpCase(Ch) Of 'A', 'E', 'I', 'O', 'U' : IsVogal := True; Else IsVogal := False; End; End; Function IsConsoante (Ch : Char) : Boolean; Begin Ch := UpCase (Ch); IsConsoante := IsLetra(Ch) And (Not IsVogal(Ch)); End; Begin Writeln Writeln Writeln Writeln End. O tabela (IsConsoante('a')); (IsConsoante('B')); (IsConsoante('x')); (IsConsoante('3')); utilizado na função : testa se um caractere está entre o e o na Case UpCase(Ch) Of 'A'..'Z' : IsLetra := True; Else IsLetra := False; End; Introdução à Programação: Conceitos e Práticas. Página 178 A expressão avaliada é dada por . O primeiro e único teste é pela pertinência deste valor no conjunto de valores dado pelos elementos formados pelos caracteres de a . O uso de dois pontos seguidos equivale ao conjunto formado por todos elementos neste intervalo. Se este teste falhar, será executada a instrução associada à cláusula . O utilizado na função formado pelas vogais. avalia se o caractere dado pertence ao conjunto Case UpCase(Ch) Of 'A', 'E', 'I', 'O', 'U' : IsVogal := True; Else IsVogal := False; End; A expressão avaliada é dada novamente por . O primeiro e único teste é pela pertinência deste valor no conjunto formado pelas vogais { }. A função não foi alterada, e retornará for e o resultado de for . se o resultado de A saída para o programa é: FALSE TRUE TRUE FALSE ii. Refazer o programa para o cálculo da quantidade de dias de um determinado mês e ano. Segue a solução proposta: Program exDATA; Function IsLeap (A : Integer) : Boolean; Begin IsLeap := (A Mod 4 = 0) And ((A Mod 100 <> 0) Or (A Mod 400 = 0)); End; Function NumberOfDays (M, Begin Case M Of 1, 3, 5, 7, 8, 10, 12 4, 6, 9, 11 2 Else A : Integer) : Integer; : NumberOfDays := 31; : NumberOfDays := 30; : NumberOfDays := 28 + Ord(IsLeap(A)) Introdução à Programação: Conceitos e Práticas. Página 179 NumberOfDays := 0; End; End; Procedure IncDate (Var D, M, A : Integer); Begin Inc (D); If D > NumberOfDays (M, A) Then Begin D := 1; Inc (M); If M > 12 Then Begin M := 1; Inc (A); End; End; End; Var Dia, Mes, Ano : Integer; Begin Writeln Readln IncDate Writeln End. ('Entre com uma data: dd mm aaaa'); (Dia, Mes, Ano); (Dia, Mes, Ano); ('O dia seguinte é: ', Dia, Mes:3, Ano:5); Observe que a função , reescrita com o melhora na legibilidade em relação a versão anterior com iii. , apresenta uma sensível . Fornecer uma solução para a conversão de um número decimal de a para o dígito hexadecimal em forma de caractere. Incluir também a conversão de um caractere contendo um dígito hexadecimal ( a ) em seu equivalente inteiro decimal. Segue a solução proposta: Program ExHEXA; Function HexDigToDec (Ch : Char) : Integer; Begin Ch := UpCase (Ch); Case Ch Of '0'..'9' : HexDigToDec := Ord(Ch) - Ord('0'); 'A'..'F' : HexDigToDec := Ord(Ch) - Ord('A') + 10; Else HexDigToDec := -1; End; Introdução à Programação: Conceitos e Práticas. Página 180 End; Function DecToHexDig (X : Integer) : Char; Begin Case X Of 0..9 : DecToHexDig := Chr(Ord('0') + X); 10..15 : DecToHexDig := Chr(Ord('A') + X - 10); Else DecToHexDig := 'X'; End; End; Begin Writeln Writeln Writeln Writeln Writeln Writeln Writeln Writeln Writeln End. (HexDigToDec (HexDigToDec (HexDigToDec (HexDigToDec ('A')); ('0')); ('f')); ('B')); (DecToHexDig (DecToHexDig (DecToHexDig (DecToHexDig (DecToHexDig (0 )); (10)); (9 )); (15)); (20)); A função recebe um caractere qualquer e o processa de acordo com a convenção para os dígitos hexadecimais, ou seja: Se for um dos caracteres de , onde , que subtraído de desejado. Se for um dos caracteres de a então o resultado é o valor da expressão , onde é o número com a posição de na tabela , que subtraído de , fornecerá um valor de a , que somado com , retornará o resultado de a , conforme desejado. Qualquer outro caractere resultará em a então o resultado é o valor da expressão é o número com a posição de na tabela , fornecerá o resultado de 0 a 9, conforme . A função codifica o comportamento inverso da função anterior. Fica a cargo de o leitor estudar e entender a lógica utilizada. Introdução à Programação: Conceitos e Práticas. Página 181 A saída para o programa é: 10 0 15 11 0 A 9 F X As conversões pedidas podem ter lógicas alternativas utilizando outros recursos já apresentados. Segue outra versão possível: Program ExHEXA; Function HexDigToDec (Ch : Char) : Integer; Begin HexDigToDec := Pos (UpCase(Ch), '0123456789ABCDEF') - 1; End; Function DecToHexDig (X : Integer) : Char; Var S : String; Begin S := '0123456789ABCDEF'; Case X Of 0..15 : DecToHexDig := S[X+1]; Else DecToHexDig := 'X'; End; End; Begin {mesmo programa principal anterior} End. A solução para utiliza a função para obter a posição do caractere na , cujo resultado será um número de a , dependendo do caractere. Para obter o equivalente decimal basta subtrair deste valor. Caso o caractere não seja nenhum dos relacionados na , então retornará que subtraído , resultará em para esta condição. A função retorna um dos caracteres da de apoio quando o valor de estiver entre 0 e 15. O caractere desejado é o que se encontrar na posição , uma vez que quando é , o caractere correspondente se encontra na posição . Introdução à Programação: Conceitos e Práticas. Página 182 Quando se deseja apenas um caractere de uma , , mas sim o . Por exemplo, se for necessário atribuir a variável o caractere que está na posição de , a forma [ ] é preferível em relação a . 21.3. Repetição – FOR⋯DO A estrutura de repetição completa os recursos de uma linguagem tipo e amplia as possibilidades de controle sobre o fluxo de execução de um programa. A estrutura de repetição permite que uma ou mais instruções sejam executadas várias vezes de acordo com a necessidade do problema. Assim, ao modelar um problema e desenhar o algoritmo, deve ser pensada uma lógica que represente a situação de uma forma geral. Por exemplo, se é necessário obter as notas de alunos para calcular a média da turma, não é razoável construir uma lógica com . Deve haver um mecanismo que determine a repetição de vezes a instrução desejada. As linguagens da família elas: disponibilizam várias estruturas de repetição, sendo Repetição controlada por contador; Repetição controlada por expressão lógica; o Pré-teste; o Pós-teste A repetição controlada por contador também é conhecida como estrutura tipo apresentada neste tópico. 21.3.1. ,eé Sintaxe e Semântica A sintaxe da instrução de repetição tipo FOR é: Onde, é uma variável do tipo ordinal denominada da repetição ( ) ou apenas ; é uma expressão ordinal compatível com o tipo de , cujo resultado indica o valor inicial de ; é uma expressão ordinal compatível com o tipo de , cujo resultado indica o valor final de ; Introdução à Programação: Conceitos e Práticas. Página 183 A seguinte figura ilustra os componentes da instrução , bem como destaca a dinâmica da estrutura com os vários ciclos de execução da instrução controlada, onde cada ciclo tem o valor do contador incrementado de . Figura 63: Repetição do tipo FOR. O seguinte diagrama lógico permite visualizar o fluxo do processamento: Figura 64: Estrutura de Controle do tipo FOR Neste diagrama podemos verificar o funcionamento da instrução . A variável de controle é inicializada com o valor , em seguida é realizado um teste para certificar que . Sendo verdadeiro, então as instruções controladas são executadas e o valor de é incrementado de . O fluxo é novamente direcionado para a pergunta sobre o valor de em relação a . A repetição se encerra quando . Introdução à Programação: Conceitos e Práticas. Página 184 21.3.2. i. Exemplos Implementar a seguinte somatória para N termos: ⋯ A série possui termos, onde pode ser qualquer valor maior que um. Os termos da série possuem a seguinte forma geral: Assim, é necessário agora formular uma solução para gerar cada um dos termos, e somá-los a fim de atender a especificação do problema. Segue solução implementada: Program ExSERIE; Function SomaSerie (N : Integer) : Real; Var I : Integer; S : Real; Begin S := 0; For I := 1 To N Do S := S + 1/I; SomaSerie := S; End; Begin Writeln (SomaSerie(2) :8:6); Writeln (SomaSerie(5) :8:6); Writeln (SomaSerie(100):8:6); End. A função utiliza duas variáveis, sendo a variável de controle do ea variável para acumular os termos gerados em cada ciclo de execução. A função recebe como parâmetro a quantidade de termos que se pretende considerar para a série. A seguinte tabela ilustra os cinco ciclos de execução da chamada . I Instrução S 1 S := S + 1/I; S := 0 + 1/1 1 2 S := S + 1/I; S := 1 + 1/2 1.5 3 S := S + 1/I; S := 1.5 + 1/3 1.833333 4 S := S + 1/I; S := 1.8333 + 1/4 2.083333 5 S := S + 1/I; S := 2.0833 + 1/5 2.283333 Introdução à Programação: Conceitos e Práticas. Página 185 A coluna contém os valores da variável de controle em cada ciclo. A coluna apenas reforça que a cada ciclo a mesma instrução é executada. A coluna destaca como a variável vai sendo atualizada a cada ciclo pela atribuição do valor da expressão a direita e acumulando o valor de cada termo. Ao final do processamento do , a variável conterá o valor . A saída para o programa é: 1.500000 2.283333 5.187378 ii. Implementar a seguinte somatória para termos: ⋯ Esta série difere da anterior pela alternância dos sinais dos termos. Uma solução possível é utilizar a instrução com a variável de controle assumindo os valores { }, e para cada um destes valores escrever um mecanismo para produzir o termo e o respectivo sinal, bem como acumular estas parcelas. Assim, é necessário agora formular uma solução para gerar cada um dos termos, e soma-los a fim de atender a especificação do problema. Segue a implementação desta lógica: Program ExSERIE; Function SomaSerie (N : Integer) : Real; Var I : Integer; S : Real; Begin S := 0; For I := 1 To N Do If I Mod 2 = 0 Then S := S - 1/I Else S := S + 1/I; SomaSerie := S; End; Begin Writeln (SomaSerie(2) :8:6); Writeln (SomaSerie(5) :8:6); Writeln (SomaSerie(100):8:6); End. Introdução à Programação: Conceitos e Práticas. Página 186 A lógica adotada aproveita o valor de para deduzir o sinal da parcela. Assim, quando for par o termo deve ser diminuído (negativo) de e quando for impar deve ser somado (positivo). O teste se é par é feito avaliando o resto da divisão de por . Uma alternativa a esta lógica se baseia no uso de uma variável adicional para fornecer o sinal correto a cada ciclo. Segue esta versão: Function SomaSerie (N : Integer) : Real; Var I : Integer; S : Real; T : Integer; Begin S := 0; T := 1; For I := 1 To N Do Begin S := S + T/I; T := -T; End; SomaSerie := S; End; A variável é inicializa com e a cada ciclo a instrução é executada com o objetivo de alternar o valor de entre e . As parcelas passam a apresentar o seguinte termo geral , que constrói exatamente os termos com o sinal. A saída para o programa é: 0.500000 0.783333 0.688172 iii. Implementar a seguinte somatória para a quantidade de termos indicada: ⋯ Esta série poderia ser reescrita para ficar explícita a ordem de cada termo, como segue: ⋯ O termo geral desta série tem a seguinte forma: Introdução à Programação: Conceitos e Práticas. Página 187 Uma solução possível é utilizar a instrução com a variável de controle assumindo os valores { }, e para cada um destes valores escrever um mecanismo para produzir o termo geral, bem como acumular estas parcelas. A função implementada anteriormente pode ser utilizada para compor a expressão que constrói cada um dos termos. Program ExSERIE; function power (base, expoente : real) : real; begin power := exp (expoente*ln(base)); end; Function SomaSerie (X : Real; N : Integer) : Real; Var I : Integer; S : Real; Begin S := 0; For I := 0 To N Do S := S + Power (X, I); SomaSerie := S; End; Begin Writeln (SomaSerie(2, 2) :8:6); Writeln (SomaSerie(2, 5) :8:6); Writeln (SomaSerie(1, 100):8:6); End. Neste exemplo, a variável de controle assume valores de a , tendo associação direta com o expoente do termo geral. A lógica fica reduzida a acumular em o valor de . A desvantagem desta solução é que a cada ciclo é calculado integralmente o valor de , o que torna esta lógica ineficiente. Por exemplo, quando , o termo é calculado de forma completa, com as nove multiplicações. Porém, no ciclo anterior foi calculado o valor de , caso fosse aproveitado o esforço computacional feito, bastaria agregar mais uma multiplicação no ciclo atual para chegar em . Assim, a seguinte lógica melhora a eficiência para esta série: Function SomaSerie (X : Real; N : Integer) : Real; Var I : Integer; P : Real; S : Real; Begin S := 0; P := 1; For I := 0 To N Do Introdução à Programação: Conceitos e Práticas. Página 188 Begin S := S + P; P := P*X; End; SomaSerie := S; End; Esta solução utiliza dois acumuladores: e . A variável representa cada parcela, que é calculada acumulando o produto de ao longo dos ciclos. Esta lógica está codificada na instrução . O cuidado que deve ser tomado ao acumular um produto, é o de inicializar a variável adequadamente, que neste caso é inicializado com . A variável acumula a soma das parcelas ( ). Uma nova variante para a implementação desta série é dada pela seguinte transformação, para alguns termos: ( ( ( ))) Assim, a seguinte lógica melhora o desempenho para esta série: Function SomaSerie (X : Real; N : Integer) : Real; Var I : Integer; S : Real; Begin S := 0; For I := 0 To N Do S := S*X + 1; SomaSerie := S; End; Esta solução é a mais eficiente, pois apresenta por ciclo de execução do . , e A saída para o programa é: 7.000000 63.000000 101.000000 iv. Implementar uma solução para o cálculo do fatorial de um inteiro: ⋯ Introdução à Programação: Conceitos e Práticas. Página 189 O fatorial de um é o produto de todos os inteiros entre e . A proposta é utilizar a instrução com a variável de controle produzindo os valores { }, e para cada um destes valores utilizar a técnica de acumular o produto de em uma variável. O seguinte código implementa o cálculo do fatorial. Program ExFATORIAL; Function Fat (N : Integer) : LongInt; Var F : LongInt; I : Integer; Begin F := 1; For I := 1 To N Do F := F*I; Fat := F; End; Var K : Integer; Begin For K := 0 To 30 Do Writeln (K:3, ' ', Fat(K)); End. Porém há um problema crítico, que reside no fato que o cálculo do fatorial produz um rápido crescimento dos valores, uma vez que há um acúmulo de multiplicação. A função retorna um resultado do tipo , que tem de comprimento e admite uma faixa de a . Este tamanho em bits para o resultado limita ao valor , a partir da qual há um estouro de capacidade. A solução é utilizar um tipo com faixa maior. Assim, a função foi testada para os tipos e . As seguintes saídas foram obtidas para cada um destes tipos: LongInt 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 1 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600 1932053504 1278945280 2004310016 2004189184 -288522240 -898433024 109641728 Int64 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 1 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600 6227020800 87178291200 1307674368000 20922789888000 355687428096000 6402373705728000 121645100408832000 Introdução à Programação: Conceitos e Práticas. Real 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 1 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600 6227020800 87178291200 1307674368000 20922789888000 355687428096000 6402373705728000 121645100408832000 Página 190 20 -2102132736 21 -1195114496 22 -522715136 ... 30 1409286144 20 2432902008176640000 21 -4249290049419214848 22 -1250660718674968576 ... 30 -8764578968847253504 20 21 22 ... 30 31 32 33 34 35 ... 97 98 99 100 2432902008176640000 51090942171709400000 1124000727777610000000 265252859812191000000000000000000 8222838654177920000000000000000000 263130836933694000000000000000000000 8.68331761881189E+036 2.95232799039604E+038 1.03331479663861E+040 9.61927596824821E+151 9.42689044888324E+153 9.33262154439441E+155 9.33262154439441E+157 A linha sublinhada indica o valor de a partir da qual há um estouro de capacidade. Assim, a opção é pela versão abaixo com o uso do tipo para o retorno da função: Function Fat (N : Integer) : Real; Var F : Real; I : Integer; Begin F := 1; For I := 1 To N Do F := F*I; Fat := F; End; O programador deve observar no manual do compilador quais são os tipos de dados mais adequados entre os vários da família . A listagem para o tipo também chama atenção para o fato de que a partir de certo valor perde-se a precisão, pois a quantidade de dígitos significativos é bastante limitada, mesmo que a faixa de representação admita números de alta magnitude. Para o tipo escolhido neste exemplo a capacidade é de dígitos significativos, e fica evidente na listagem o complemento feito com zeros a partir de igual a . A recomendação é sempre evitar algoritmos que acumulem extensos produtos que podem levar ao estouro de capacidade. A indicação é buscar mecanismos que aproveitam os cálculos de ciclos anteriores, e observar se há possibilidade de priorizar operações na expressão que contribua para manter os resultados intermediários dentro de limites seguros. Isto pode ser obtido em algumas situações priorizando divisões antes das multiplicações. Por exemplo, na seguinte expressão: Na situação em que os valores de , , e estejam próximos ao limite do tipo definido, é praticamente certo que os produtos levarão ao estouro de capacidade e consequentemente a um erro de lógica ou de execução. Esta mesma expressão pode ser reescrita para avaliar primeiro as divisões: Introdução à Programação: Conceitos e Práticas. Página 191 v. Refazer o programa que calcula o seno de um ângulo através da série: ⋯ A lógica do já codificada foi limitada aos primeiros seis termos e se resumiu a transcrição da série em uma expressão aritmética com a soma explícita de todas as seis parcelas. Agora, com o recurso da repetição podemos calcular o seno com melhor precisão. A primeira versão será a mais básica e intuitiva, pois utilizará e para gerar os termos. Program ExSENO; function power (base, expoente : Extended) : Extended; begin If base <> 0 Then power := exp (expoente*ln(base)) Else power := 0; end; Function Reduzir (X : Extended) : Extended; Begin Reduzir := Frac(X/(2*PI))*(2*PI); End; Function GrausToRad (G : Extended) : Extended; Begin GrausToRad := G*PI/180; End; Function Fat (N : Integer) : Extended; Var F : Extended; I : Integer; Begin F := 1; For I := 1 To N Do F := F*I; Fat := F; End; Function MySin (X : Extended) : Extended; Var S : Extended; T, I : Integer; Begin X := Reduzir (X); Introdução à Programação: Conceitos e Práticas. Página 192 T := 1; S := 0; For I := 1 To 15 Do Begin S := S + T*Power (X, 2*I-1)/Fat(2*I-1); T := -T; End; MySin := S; End; Var Graus, Rad, Y1 : Extended; Y2 : Extended; I : Integer; Begin For I := 0 To 24 Do Begin Graus := I*15; Rad := GrausToRad (Graus); Y1 := Sin (Rad); Y2 := MySin (Rad); Writeln (I:4, ' ', Graus:4:0, Y1:21:20, Y2:21:20, Y1-Y2:21:20); End; End. A função inclui uma repetição para o cálculo de tem a seguinte forma: termos, onde o termo geral A menos da obtenção do sinal, a expressão que codifica cada termo tem exatamente este formato. O sinal foi gerado a partir da variável que alterna entre e a cada ciclo. Isto evita utilizar apenas para obter e . O testa a função em comparação à função interna do , e apresenta os valores dos senos e a diferença entre estes valores. O de vezes ( a ) serve para gerar os ângulos de a de em graus, através da expressão . A saída obtida foi: I 0 1 2 3 4 5 6 7 X 0 15 30 45 60 75 90 105 SIN(X) 0.00000000000000000 0.25881904510252076 0.50000000000000000 0.70710678118654752 0.86602540378443865 0.96592582628906829 1.00000000000000000 0.96592582628906829 MYSIN(X) ERRO 0.00000000000000000 0.00000000000000000 0.25881904510252076 0.00000000000000000 0.50000000000000000 0.00000000000000000 0.70710678118654752 0.00000000000000000 0.86602540378443865 0.00000000000000000 0.96592582628906829 0.00000000000000000 1.00000000000000000 0.00000000000000000 0.96592582628906829 -0.00000000000000000 Introdução à Programação: Conceitos e Práticas. Página 193 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 120 135 150 165 180 195 210 225 240 255 270 285 300 315 330 345 360 0.86602540378443865 0.70710678118654752 0.50000000000000000 0.25881904510252076 -0.00000000000000000 -0.25881904510252076 -0.50000000000000000 -0.70710678118654752 -0.86602540378443865 -0.96592582628906829 -1.00000000000000000 -0.96592582628906829 -0.86602540378443865 -0.70710678118654752 -0.50000000000000000 -0.25881904510252076 0.00000000000000000 0.86602540378443865 0.70710678118654752 0.50000000000000000 0.25881904510252076 0.00000000000000000 -0.25881904510252076 -0.49999999999999997 -0.70710678118654721 -0.86602540378443635 -0.96592582628905322 -0.99999999999991161 -0.96592582628859701 -0.86602540378213313 -0.70710678117611210 -0.49999999995598198 -0.25881904492839897 0.00000000000000000 0.00000000000000000 0.00000000000000000 -0.00000000000000000 -0.00000000000000000 -0.00000000000000000 -0.00000000000000001 -0.00000000000000003 -0.00000000000000031 -0.00000000000000230 -0.00000000000001506 -0.00000000000008839 -0.00000000000047127 -0.00000000000230552 -0.00000000001043543 -0.00000000004401802 -0.00000000017412179 0.00000000000000000 A diferença em relação a função da biblioteca aumenta a medida que o ângulo se aproxima de . A lógica implementada é ineficiente, pois não aproveita os cálculos feitos no ciclo anterior, o que aumenta consideravelmente a quantidade de multiplicações e divisões para obter o resultado final. Este mesmo resultado pode ser obtido usando uma onde um termo atual pode ser definido em função do termo calculado anteriormente. Observa-se nas equações abaixo, que dois termos consecutivos possuem uma relação entre eles cujo estilo se conserva ao longo da série. 𝑥 𝑥 𝑥 𝑥 𝑥 𝑥 𝑥 𝑥 𝑥 ⋯ 𝑥 O termo atual é o termo anterior multiplicado por e dividido pelo produto de dois números, que também podem ser generalizados em função da posição do termo na série. Assim, temos a seguinte relação de recorrência: Onde: Assim, a seguinte solução é preferida em relação a anterior que utiliza e : Program ExSENO; Function Reduzir (X : Extended) : Extended; Introdução à Programação: Conceitos e Práticas. Página 194 {igual a anterior} Function GrausToRad (G : Extended) : Extended; {igual a anterior} Function MySin (X : Extended) : Extended; Var P, S : Extended; I : Integer; Begin X := Reduzir (X); S := 0; P := X; For I := 1 To 15 Do Begin S := S + P; P := -P*X*X/((2*I)*(2*I+1)); End; MySin := S; End; Var Graus, Rad, Y1 : Extended; Y2 : Extended; I : Integer; Begin {igual ao programa principal anterior} End. A saída obtida foi: I 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 X 0 15 30 45 60 75 90 105 120 135 150 165 180 195 210 225 240 255 270 285 300 SIN(X) 0.00000000000000000 0.25881904510252076 0.50000000000000000 0.70710678118654752 0.86602540378443865 0.96592582628906829 1.00000000000000000 0.96592582628906829 0.86602540378443865 0.70710678118654752 0.50000000000000000 0.25881904510252076 -0.00000000000000000 -0.25881904510252076 -0.50000000000000000 -0.70710678118654752 -0.86602540378443865 -0.96592582628906829 -1.00000000000000000 -0.96592582628906829 -0.86602540378443865 MYSIN(X) 0.00000000000000000 0.25881904510252076 0.50000000000000000 0.70710678118654752 0.86602540378443865 0.96592582628906829 1.00000000000000000 0.96592582628906829 0.86602540378443865 0.70710678118654752 0.50000000000000000 0.25881904510252076 0.00000000000000000 -0.25881904510252076 -0.49999999999999996 -0.70710678118654721 -0.86602540378443634 -0.96592582628905323 -0.99999999999991161 -0.96592582628859700 -0.86602540378213311 Introdução à Programação: Conceitos e Práticas. ERRO 0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 -0.00000000000000000 -0.00000000000000000 -0.00000000000000000 -0.00000000000000004 -0.00000000000000031 -0.00000000000000230 -0.00000000000001506 -0.00000000000008839 -0.00000000000047128 -0.00000000000230554 Página 195 21 22 23 24 315 -0.70710678118654752 -0.70710678117611212 -0.00000000001043541 330 -0.50000000000000000 -0.49999999995598203 -0.00000000004401797 345 -0.25881904510252076 -0.25881904492839899 -0.00000000017412177 360 0.00000000000000000 0.00000000000000000 0.00000000000000000 Além da sensível redução na quantidade de cálculos esta solução apresentou um erro levemente menor em relação a função interna quando comparado ao erro apresentado pela lógica anterior. Mais adiante, no tópico seguinte, será apresentada uma solução que reduz ainda mais o erro. vi. Elaborar um programa que leia vários números fornecidos via teclado e apresente ao final a somatória destes valores. Considere este esboço de interface: A lógica deste problema é bastante simples e consiste em um loop onde deve ser lido um dado e acumulado em uma variável. Ao encerrar a repetição o resultado é impresso. Segue a solução proposta: Program ExLERDADOS; Procedure LerDados (N : Integer); Var I : Integer; X, S : Real; Begin S := 0; Writeln ('Entre com os dados'); For I := 1 To N Do Begin Readln (X); S := S + X; End; Writeln ('SOMA = ', S:7:2); End; Begin LerDados (10); End. Introdução à Programação: Conceitos e Práticas. Página 196 A seguinte figura ilustra uma saída real: Entre com os dados 10 23 54 98 102 75 98 17 34.5 56.25 SOMA = 567.75 vii. Elaborar um programa semelhante ao anterior, porém o usuário pode interromper a entrada de dados fornecendo o valor . A lógica do problema anterior é adaptada para encerrar o loop antes do seu final, testando pelo valor de . O recurso da linguagem que permite encerrar o e desviar o processamento para a próxima instrução após o fim do é a instrução . Normalmente o vem combinado com um teste de condição, que em caso positivo desvia o fluxo para fora do . O seguinte diagrama ilustra visualmente a instrução inserida em um . Figura 65: Instrução BREAK. Segue a solução proposta: Program ExLERDADOS; Introdução à Programação: Conceitos e Práticas. Página 197 Procedure LerDados (N : Integer); Var I : Integer; X, S : Real; Begin S := 0; Writeln ('Entre com os dados'); For I := 1 To N Do Begin Readln (X); If X = -1 Then Break; S := S + X; End; Writeln ('SOMA = ', S:7:2); End; Begin LerDados (10); End. A seguinte figura ilustra uma saída real: Entre com os dados 10 20 30 40 10.5 20.5 30.5 -1 SOMA = 161.50 viii. Elaborar um programa semelhante ao anterior, porém fornecendo ao final a soma e a média aritmética dos valores fornecidos. O cálculo da média é feito dividindo a soma dos dados pela quantidade de dados fornecidos. A primeira impressão poderia levar a uma solução utilizando o valor do , variável de controle do loop, acreditando que ela reterá a quantidade de termos. Porém esta não é uma boa prática de programação, pois não há garantias para o uso da variável de controle fora do . Ao encerrar a repetição, esta variável não contém um valor confiável. Assim, a solução demanda que dentro do loop resolvamos esta questão. Segue a solução proposta: Program ExLERDADOS; Procedure LerDados (N : Integer); Var I, T : Integer; Introdução à Programação: Conceitos e Práticas. Página 198 X, S, M : Real; Begin S := 0; T:= N; Writeln ('Entre com os dados'); For I := 1 To N Do Begin Readln (X); If X = -1 Then Begin T := I-1; Break; End; S := S + X; End; M := S/T; Writeln ('SOMA = ', S:7:2); Writeln ('MEDIA = ', M:7:2); End; Begin LerDados (10); End. Observe que antes de sair do com o , vem a instrução armazena em o número de termos dado por . Se o término do naturalmente, sem o , a variável permanecerá com o valor inicial que foram lidos dados. A seguinte figura ilustra uma saída real: que ocorrer , indicando Entre com os dados 10 20 30 10.5 20.5 30.5 -1 SOMA = 121.50 MEDIA = 20.25 ix. Elaborar um programa semelhante ao anterior, porém fornecendo ao final a soma, a média aritmética e o maior valor dentre os valores fornecidos. Deve ser agregada ao programa anterior a lógica para obter o maior entre os números fornecidos. A estratégia consiste em inicializar uma variável com um valor pequeno, e dentro do loop atualizar esta variável toda vez que um valor fornecido ( ) for superior ao atual maior. Segue a lógica proposta: Program ExLERDADOS; Introdução à Programação: Conceitos e Práticas. Página 199 Procedure LerDados (N : Integer); Var I, T : Integer; Maior, X, S, M : Real; Begin S := 0; T:= N; Writeln ('Entre com os dados'); Maior := 0; For I := 1 To N Do Begin Readln (X); If X = -1 Then Begin T := I-1; Break; End; If X > Maior Then Maior := X; S := S + X; End; M := S/T; Writeln ('SOMA = ', S:7:2); Writeln ('MEDIA = ', M:7:2); Writeln ('MAIOR = ', Maior:7:2); End; Begin LerDados (10); End. No código acima foram destacadas as linhas que tratam a lógica do maior: Inicializa a variável que conterá o maior valor: Maior := 0; Para cada valor fornecido, verificar se o mesmo é maior que o maior atual, e em caso positivo faça a substituição: If X > Maior Then Maior := X; Introdução à Programação: Conceitos e Práticas. Página 200 A seguinte figura ilustra uma saída real: Entre 20 30 10 30.5 20.5 10.5 -1 SOMA MEDIA MAIOR com os dados = = = 121.50 20.25 30.50 Esta lógica tem um problema quando os valores fornecidos forem todos negativos. Neste cenário, o programa informará que o maior número é zero. Uma melhoria na estratégia seria inicializar a variável com o primeiro número informado, pois assim fica assegurada a consistência da lógica. Program ExLERDADOS; Procedure LerDados (N : Integer); Var I, T : Integer; Maior, X, S, M : Real; Begin S := 0; T := N; Writeln ('Entre com os dados'); Readln (X); Maior := X; For I := 1 To N Do Begin If X = -1 Then Begin T := I-1; Break; End; If X > Maior Then Maior := X; S := S + X; If I < N Then Readln (X); End; M := S/T; Writeln ('SOMA = ', S:7:2); Writeln ('MEDIA = ', M:7:2); Writeln ('MAIOR = ', Maior:7:2); End; Introdução à Programação: Conceitos e Práticas. Página 201 Begin LerDados (10); End. A alteração introduzida para melhorar a lógica do consiste em efetuar uma leitura de dados antes de iniciar a repetição, e deslocar a leitura interna à repetição para o final do . Com isto fica resolvido o problema da inicialização da variável . Foi adicionado um teste para evitar a leitura, já que uma leitura válida foi realizada antes de iniciar a repetição. 21.4. Repetição – WHILE⋯DO A estrutura de controle do tipo repetição controlada por expressão lógica é essencial em uma linguagem de programação do paradigma imperativo ou procedural, da qual e fazem parte. O objetivo desta construção de controle é prover um mecanismo onde uma ou mais instruções são executadas zero ou mais vezes dependendo do valor de uma expressão lógica. O ciclo é repetido enquanto o valor da expressão lógica for . 21.4.1. Sintaxe e Semântica A sintaxe da instrução de repetição tipo Onde é: é a condição que determina o controle da repetição. A seguinte figura ilustra os componentes da instrução , bem como destaca que a dinâmica da estrutura inclui vários ciclos de execução da instrução controlada, onde o fim da repetição ocorre quanto a expressão lógica assume o valor . O seguinte diagrama lógico permite visualizar o fluxo do processamento: Introdução à Programação: Conceitos e Práticas. Página 202 O valor da expressão lógica do é testado. Caso seja o fluxo do processamento segue para as instruções controladas, retornando ao final para um novo teste do valor da expressão lógica. O loop é encerrado quando o valor da expressão lógica for . Introdução à Programação: Conceitos e Práticas. Página 203 21.4.2. i. Exemplos Refazer a lógica da leitura dos dados e cálculo da soma, da média aritmética e do maior valor dentre os valores fornecidos, porém utilizando a repetição controlada por expressão lógica. A única forma de encerrar a entrada de dados é através do dado . A utilização da instrução irá simplificar a última implementação feita com o , pois deverá dispensar o . A permanência no loop será definida pela condição . Segue a lógica proposta: Program ExLERDADOS; Procedure LerDados; Var T : Integer; Maior, X, S, M : Real; Begin S := 0; T := 0; Writeln ('Entre com os dados'); Readln (X); Maior := X; While X <> -1 Do Begin Inc (T); If X > Maior Then Maior := X; S := S + X; Readln (X); End; M := S/T; Writeln ('SOMA = ', S:7:2); Writeln ('MEDIA = ', M:7:2); Writeln ('MAIOR = ', Maior:7:2); End; Begin LerDados; End. A procedure foi alterada, pois o parâmetro que indica a quantidade de dados a serem lidos não é mais necessária. O final da repetição se dá unicamente pela entrada do valor . A lógica tem as seguintes características: São inicializadas as variáveis e . Não há mais contador, assim a variável armazenará a quantidade de dados válidos digitados. É útil para o cálculo da média; É feita uma leitura antes da repetição, que servirá de inicialização para a variável ; Introdução à Programação: Conceitos e Práticas. Página 204 ii. Dentro do a variável é incrementada de a cada entrada válida. A saída de dados na tela é semelhante aos programas anteriores. Elaborar um programa que converta um número inteiro decimal em uma contendo o equivalente . A seguinte figura ilustra o processo de conversão da base decimal para a binária. São feitas divisões sucessivas do número que se deseja converter pela base destino. Os restos destas divisões fornecem os dígitos binários, e o processo se encerra quando o quociente alcançar zero: Figura 66: Conversão Decimal - Binário Segue a lógica proposta: Program ExBASE; Function DecToBin (X : Integer) : String; Var S : String; Begin S := ''; While X <> 0 Do Begin If X Mod 2 = 0 Then S := '0' + S Else S := '1' + S; X := X Div 2; End; DecToBin := S; End; Begin Writeln (DecToBin(15)); Writeln (DecToBin(67)); Introdução à Programação: Conceitos e Práticas. Página 205 Writeln (DecToBin(1030)); Writeln (DecToBin(255)); End. A função codifica a lógica de conversão. A que conterá o binário convertido é inicializado com vazio ( )17. A repetição se encerra quando for igual a zero, e este é atualizado ao final do com o novo quociente. Antes, porém, o resto da divisão de por é utilizado para decidir se deve ser adicionado o caractere ou a esquerda de a fim de montar o resultado corretamente. A saída para o programa é: 1111 1000011 10000000110 11111111 iii. Elaborar um programa que converta um número inteiro decimal em uma contendo o equivalente . A lógica é semelhante ao do problema anterior bastando substituir o divisor pela base desejada. Segue a lógica proposta: Program ExBASE; Function DecToHexDig (X : Integer) : Char; Begin Case X Of 0..9 : DecToHexDig := Chr(Ord('0') + X); 10..15 : DecToHexDig := Chr(Ord('A') + X - 10); Else DecToHexDig := 'X'; End; End; Function DecToHex (X : Integer) : String; Var S : String; Begin S := ''; While X <> 0 Do Begin S := DecToHexDig (X Mod 16) + S; X := X Div 16; End; DecToHex := S; 17 Observar que são dois caracteres ' (apóstrofe) seguidos sem espaço intermediário. Introdução à Programação: Conceitos e Práticas. Página 206 End; Begin Writeln Writeln Writeln Writeln End. (DecToHex(15)); (DecToHex(67)); (DecToHex(1030)); (DecToHex(255)); A principal modificação em relação à conversão para binário, é no trecho que obtém o caractere da base desejada. Na conversão para base , resolveu-se o problema com um simples , em função do binário ter dois símbolos. A conversão para hexadecimal utiliza a função já codificada anteriormente que fornece um digito hexadecimal a partir do valor decimal de a . A saída para o programa é: F 43 406 FF iv. Elaborar um programa que complete uma string com brancos a esquerda a fim de ajustar o tamanho do texto para uma quantidade estabelecida de caracteres. Faça o mesmo para complemento a direita. Segue a lógica proposta: Program ExPAD; Function PadLeft (S : String; N : Integer) : String; Begin While Length(S) < N Do S := ' ' + S; PadLeft := S; End; Function PadRight (S : String; N : Integer) : String; Begin While Length(S) < N Do S := S + ' '; PadRight := S; End; Begin Writeln ('[', PadLeft ('Ola Mundo', 30),']'); Writeln ('[', PadRight ('Ola Mundo', 30),']'); Introdução à Programação: Conceitos e Práticas. Página 207 End. A lógica principal para a função consiste em concatenar o caractere espaço em branco a esquerda da até que ela fique com o comprimento definido. A função recebe a inicial e o comprimento desejado. A repetição é encerrada quando o comprimento do novo alcançar , e em cada ciclo o comprimento de é incrementado pela concatenação de um caractere a esquerda. A mesma lógica é utilizada para a função . O programa principal imprime a envolvida com os símbolos [ e ] para melhor visualização dos efeitos das funções. A saída para o programa é: [ [Ola Mundo v. Ola Mundo] ] Elaborar um programa que calcule a raiz quadrada ( √ ) de um número pelo método de . O método de calcula a raiz quadrada de um através de um método iterativo (repetitivo) onde cada ciclo calcula um novo valor a partir do valor do ciclo anterior, convergindo em direção ao resultado desejado. A fórmula de recorrência é dada por: ( ) A repetição deve ser encerrada quando a diferença entre o valor atual e o anterior for inferior a um determinado . Normalmente o erro estabelecido é o menor número possível de ser representado na arquitetura em que o programa está rodando. Este erro pode ser obtido por uma função que executa divisões sucessivas a partir de até alcançar o , sendo que o valor anterior ao é o menor número representável naquela arquitetura. A solução proposta é dada por: Program ExSQRT; Function GetEPS : Extended; Var EPS, X : Extended; Begin X := 1; While X <> 0 Do Begin EPS := X; X := X/2; End; GetEPS := EPS; Introdução à Programação: Conceitos e Práticas. Página 208 End; Function Raizq (X : Extended) : Extended; Var EPS, R, Rant : Extended; begin If X >= 0 Then Begin RAnt := 0; R := 1; EPS := GetEPS; While ABS (R - RAnt) >= EPS Do Begin RAnt := R; R := (Rant + X / Rant) / 2; End; End Else R := 0; Raizq := R; End; Eegin Writeln (Raizq(2)); Writeln (Sqrt (2)); End. Na função , antes de caminhar para o cálculo da raiz, o valor de é testado para evitar o processamento de número negativo. A lógica principal da função é semelhante a fórmula apresentada: Os valores das variáveis que representam o valor da raiz no ciclo atual ( ) e o valor do ciclo anterior ( ) são inicializados com valores distintos para que o erro ( ) seja diferente de . RAnt := 0; R := 1; A variável que estabelece qual a precisão requerida para o cálculo é inicializada com o ) da máquina, que é o menor número real antes de chegar ao zero. EPS := GetEPS; O principal determina que seja calculada uma nova raiz a cada ciclo a diferença entre o valor do ciclo atual e o do ciclo anterior seja superior à precisão estabelecida. A é muito semelhante à expressão codificada. While ABS (R - RAnt) >= EPS Do Begin RAnt := R; Introdução à Programação: Conceitos e Práticas. Página 209 ( R := (Rant + X / Rant) / 2; ) End; A saída para o programa é: 1.4142135623730950E+0000 1.4142135623730950E+0000 Os valores calculados pela função implementada e pela função interna do idênticos. vi. Elaborar um programa que calcule a raiz . são ( √ ) de um número pelo método de O método de calcula a raiz de um através de um método iterativo (repetitivo), similar ao método da raiz quadrada. A fórmula de recorrência é dada por: ( ) Segue a solução proposta: Program ExRAIZN; Function GetEPS : Extended; Var EPS, X : Extended; Begin X := 1; While X <> 0 Do Begin EPS := X; X := X/2; End; GetEPS := EPS; End; Function PowerInt (X : Extended; T : Integer) : Extended; Var P : Extended; I : Integer; Begin P := 1; For I := 1 To T Do P := P*X; PowerInt := P; End; Introdução à Programação: Conceitos e Práticas. Página 210 Function RaizN (X : Extended; N : Integer) : Extended; var EPS, R, Rant : Extended; begin If X >= 0 Then Begin RAnt := 0; R := 1; EPS := GetEPS; while ABS (R - RAnt) >= EPS do begin RAnt := R; R := ((N-1)*Rant + X / PowerInt(Rant, N-1)) / N; End; End Else R := 0; RaizN := R; end; Var Y : Extended; begin Y := RaizN (25, 3); Writeln (Y); Writeln (PowerInt(Y, 3)); end. A utiliza um cálculo do tipo na parte . Como o expoente é um número inteiro positivo, preferiu-se implementar uma versão da função exclusivamente para este caso ( ), evitando o uso das funções e . A saída para o programa é: 2.9240177382128661E+0000 2.5000000000000000E+0001 No programa principal, o teste foi feito calculando os valores de e de √ valores apresentados indicam que o cálculo foi realizado com boa precisão. . Os A função pode ser implementada de outra forma, tal como a que segue, (I) baseada no exemplo do , publicado pelo prof. Niklaus Wirth. Function PowerInt (X : Extended; T : Integer) : Extended; Var W, Z : Extended; I : Integer; Begin W := X; Z := 1; i := T; While I <> 0 Do Introdução à Programação: Conceitos e Práticas. Página 211 Begin If Odd (i) Then Z := Z*W; I := I Div 2; W := W*W; End; PowerInt := Z; End; vii. Refazer o programa que calcula o seno de um ângulo a fim de que não tenha diferença com a função interna do : Para zerar o erro em relação a função interna do , faremos uma transformação no cálculo do seno. A série será mais rapidamente convergente nos casos que , e de preferência com o ângulo mais perto de zero. Assim, faremos algumas transformações para reduzir o ângulo a um menor valor equivalente. Segue a transformação proposta: ( ) ( )√ A equação indica que se calcularmos o expressão, ou seja: ( ) ( )( ) ( )( ) podemos chegar ao (√ pela última ) Onde: ( ) Segue a solução proposta, cujos detalhes ficam a cargo do leitor analisar: Program ExSENO; Function Reduzir (X : Extended) : Extended; Begin Reduzir := Frac(X/(2*PI))*(2*PI); End; Function GrausToRad (G : Extended) : Extended; Introdução à Programação: Conceitos e Práticas. Página 212 Begin GrausToRad := G*PI/180; End; Function MySinx (X : Extended) : Extended; Var P, S, SAnt : Extended; I : Integer; Begin S := 0; SAnt := X; P := X; I := 2; While S <> SAnt Do Begin SAnt := S; S := S + P; P := -P*X*X/(I*(I+1)); I := I + 2; End; MySinx := S; End; Function MySin (X : Extended) : Extended; Var S : Extended; Begin X := Reduzir(X)/4; S := MySinx(X); S := 4*S*Sqrt(1-S*S)*(1-2*S*S); MySin := S; End; Var Graus, Rad, Y1 : Extended; Y2 : Extended; I : Integer; Begin For I := 0 To 24 Do Begin Graus := I*15; Rad := GrausToRad (Graus); Y1 := Sin (Rad); Y2 := MySin (Rad); Writeln (I:4, ' ', Graus:4:0, Y1:21:20, Y2:21:20, Y1-Y2:21:20); End; End. A função que calcula o seno de um ângulo implementa uma lógica indireta, onde primeiro o ângulo é reduzido ao primeiro quadrante ( ). Na sequência o seno desejado é obtido levando este valor de para a expressão que relaciona e , conforme indica o código abaixo: Introdução à Programação: Conceitos e Práticas. Página 213 A lógica do módulo principal, que calcula o valor da série, foi alterada para utilizar o , sendo o critério de parada a situação em que dois cálculos sucessivos não acrescentam precisão alguma ao resultado. Observar na saída obtida que o erro foi zerado. I 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 viii. X 0 15 30 45 60 75 90 105 120 135 150 165 180 195 210 225 240 255 270 285 300 315 330 345 360 SIN(X) 0.00000000000000000 0.25881904510252076 0.50000000000000000 0.70710678118654752 0.86602540378443865 0.96592582628906829 1.00000000000000000 0.96592582628906829 0.86602540378443865 0.70710678118654752 0.50000000000000000 0.25881904510252076 -0.00000000000000000 -0.25881904510252076 -0.50000000000000000 -0.70710678118654752 -0.86602540378443865 -0.96592582628906829 -1.00000000000000000 -0.96592582628906829 -0.86602540378443865 -0.70710678118654752 -0.50000000000000000 -0.25881904510252076 0.00000000000000000 MYSIN(X) 0.00000000000000000 0.25881904510252076 0.50000000000000000 0.70710678118654752 0.86602540378443865 0.96592582628906829 1.00000000000000000 0.96592582628906829 0.86602540378443865 0.70710678118654752 0.50000000000000000 0.25881904510252076 0.00000000000000000 -0.25881904510252076 -0.50000000000000000 -0.70710678118654752 -0.86602540378443865 -0.96592582628906829 -1.00000000000000000 -0.96592582628906829 -0.86602540378443865 -0.70710678118654753 -0.50000000000000000 -0.25881904510252076 0.00000000000000000 ERRO 0.00000000000000000 0.00000000000000000 -0.00000000000000000 0.00000000000000000 -0.00000000000000000 -0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 -0.00000000000000000 0.00000000000000000 0.00000000000000000 -0.00000000000000000 -0.00000000000000000 0.00000000000000000 -0.00000000000000000 -0.00000000000000000 0.00000000000000000 0.00000000000000000 -0.00000000000000000 0.00000000000000000 0.00000000000000000 0.00000000000000000 -0.00000000000000000 0.00000000000000000 Elaborar um programa que formate uma . O filtro para a formatação é a eliminação dos caracteres espaço em branco duplicados em sequência. Qualquer sequência de mais de um espaço em branco deverá ser substituída por apenas um caractere. A solução proposta utiliza a função para localizar a primeira ocorrência de dois espaços em branco. Uma vez encontrada é devolvida uma string com apenas um destes dois caracteres. O processo deve se repetir até não haver mais espaços em branco em sequência. Segue o código proposto: Program ExBRANCO; Function Erase (S : String; Ini, N : Integer) : String; Begin Introdução à Programação: Conceitos e Práticas. Página 214 Erase := Copy (S, 1, Ini-1) + Copy (S, Ini+N, 255); End; Function Repete (Ch : Char; N : Integer) : String; Var S : String; Begin S := ''; While N > 0 Do Begin S := S + Ch; Dec (N); End; Repete := S; End; Function DelSpace (S : String) : String; Var P : Integer; Begin P := Pos (' ', S); While P <> 0 Do Begin S := Erase (S, P, 1); P := Pos (' ', S); End; DelSpace := S; End; Var S, Sx : String; Begin S := 'OLA MUNDO BELO '; Sx := Repete ('X', Length(S)); Writeln ('[', Sx,']'); Writeln ('[', S, ']'); Writeln ('[', DelSpace (S),']'); End. A saída para o programa é: [XXXXXXXXXXXXXXXXXXXXX] [OLA MUNDO BELO ] [OLA MUNDO BELO ] Nesta saída, o primeiro texto impresso é uma série de caracteres repetidos apenas para dar uma referência para a contagem de caracteres nas linhas que seguem. A com caracteres repetidos foi gerada a partir da função . Introdução à Programação: Conceitos e Práticas. Página 215 A lógica da função inicializada com vazio , perfazendo ao final uma utiliza uma estrutura com . Uma é e em cada repetição é concatenado em S um caractere com caracteres iguais a . A função faz a filtragem da , onde a lógica é localizar uma sequência de dois caracteres branco e atualizar para que receba a mesma porém eliminado um dos caracteres. A função , ilustrada abaixo, codifica a lógica que dada uma , uma posição e uma quantidade , é gerada uma sem os caracteres a partir da posição . A lógica utilizada concatena os textos a esquerda e a direita de um dos espaços em branco. Ini S x x .. x 1 2 3 .. z z ... z z z N Copy (S, 1, Ini-1) Erase Copy (S, Ini+N, 255) x x .. x z z ... z z z 21.5. Repetição – REPEAT⋯UNTIL O objetivo da construção de controle do tipo é prover um mecanismo onde uma ou mais instruções são executadas uma ou mais vezes dependendo do valor de uma expressão lógica. O ciclo é repetido até que o valor da expressão lógica seja . 21.5.1. Sintaxe e Semântica A sintaxe da instrução de repetição tipo Onde é: é a condição que determina o controle da repetição. A seguinte figura ilustra os componentes da instrução , bem como destaca que a dinâmica da estrutura inclui vários ciclos de execução da instrução controlada, onde o fim da repetição ocorre quanto a expressão lógica assume o valor . Introdução à Programação: Conceitos e Práticas. Página 216 As instruções controladas são executas uma vez. Em seguida o valor da expressão lógica do é testado. Caso seja o fluxo do processamento é desviado para o início das instruções controladas. O loop é encerrado quando o valor da expressão lógica for . 21.5.2. i. Exemplos Refazer a lógica da leitura dos dados e cálculo da soma e a média, sendo que o usuário digitará o texto para indicar que deseja finalizar a entrada de dados. Neste exemplo, a entrada de dados não pode ser armazenada em uma variável numérica, pois o usuário digitará o texto para informar que não deseja prosseguir com a entrada de dados. Assim, a linha a ser digitada pelo usuário deverá ser armazenada em uma variável do tipo . Desta forma, o programa aceitará qualquer texto como entrada sem provocar erro de execução. Porém, para efetuar o acúmulo dos valores fornecidos, será necessário converter o texto de entrada para uma variável numérica. O acúmulo deste valor só é feito após certificar que o conteúdo do texto fornecido é compatível com um dado numérico. Segue a lógica proposta: Program ExRUNTIL; Function IsNumber (L : String) : Boolean; Var X : Real; Code : Integer; Begin Val (L, X, Code); IsNumber := Code = 0; End; Introdução à Programação: Conceitos e Práticas. Página 217 Function Str2Real (L : String) : Real; Var X : Real; Code : Integer; Begin Val (L, X, Code); Str2Real := X; End; Procedure LerDados; Var T : Integer; M, S : Real; Linha : String; Begin S := 0; T := 0; Writeln ('Entre com os dados e digite ''fim'' para encerrar'); Repeat Readln (Linha); If IsNumber (Linha) Then Begin S := S + Str2Real (Linha); Inc (T); End; Until Linha = 'fim'; If T <> 0 Then Begin M := S/T; Writeln ('SOMA = ', S:7:2); Writeln ('MEDIA = ', M:7:2); End Else Writeln ('Não há dados disponíveis'); End; Begin LerDados; End. As funções e auxiliam na construção da lógica principal. A função testa se o conteúdo de uma satisfaz as regras de formação de um número real. A função recebe uma string como entrada e retorna como resultado o equivalente numérico. Assim, a lógica principal se apresenta de forma bastante legível e de fácil compreensão. Introdução à Programação: Conceitos e Práticas. Página 218 Uma saída exemplo para o programa é: Entre com os dados e digite 'fim' para encerrar 10.5 20.5 10.5 fim SOMA = 41.50 MEDIA = 13.83 ii. Escrever um programa que faz a leitura de várias linhas que representam uma redação qualquer, e informa a quantidade de vogais contidas em todo o texto. O usuário digitará para indicar que deseja finalizar a entrada de dados. Segue a lógica proposta: Program ExREDACAO; Function IsVogal (Ch : Char) : Boolean; Begin Case UpCase(Ch) Of 'A', 'E', 'I', 'O', 'U' : IsVogal := True; Else IsVogal := False; End; End; Function ContaVogal (L : String) : Integer; Var I, C : Integer; Begin C := 0; For I := 1 To Length (L) Do C := C + Ord (IsVogal(L[I])); ContaVogal := C; End; Procedure LerDados; Var V : Integer; Linha : String; Begin V := 0; Writeln ('Entre com os textos e digite ''fim'' para encerrar'); Repeat Readln (Linha); If Linha <> 'fim'Then V := V + ContaVogal (Linha); Until Linha = 'fim'; Writeln ('VOGAIS = ', V); End; Introdução à Programação: Conceitos e Práticas. Página 219 Begin LerDados; End. As funções e auxiliam na construção da lógica principal. A função testa se um é um dos caracteres ASCII que representam vogais. A função recebe uma string como entrada e retorna como resultado a quantidade de caracteres que são vogais. A função é implementada incluindo uma lógica de contagem. A variável de contagem é inicializada com zero . A estrutura principal é uma repetição por uma quantidade de vezes que é dada pelo número de caracteres da fornecida . A instrução repetida se resume em incrementar a variável de contagem de ou . O valor ou é obtido convertendo o resultado do teste de cada caractere da string [] para número utilizando a função . Assim, a lógica principal se apresenta de forma bastante legível e de fácil compreensão. Uma saída exemplo para o programa é: Entre com os textos e digite 'fim' para encerrar OLA mundo BELO fim VOGAIS = 6 Introdução à Programação: Conceitos e Práticas. Página 220 22. Array Os tipos de dados vistos até o momento são de natureza escalar, ou seja, permitem definir variáveis com capacidade para representar apenas um elemento. Uma variável do tipo tem capacidade para armazenar apenas um número em determinado instante. O conteúdo pode ser alterado, porém sempre será apenas um elemento. O mesmo acontece com os demais tipos escalares: , , e . O tipo antecipou o conceito de , e é um tipo de dado que permite representar um agregado de elementos escalares, no caso uma sequência de . Uma série de problemas práticos conduz a situações onde é fundamental manter simultaneamente na memória uma coleção de elementos de uma determinada natureza. Alguns exemplos seriam: nomes de alunos de uma turma, disciplinas de um curso, notas de uma turma de uma disciplina em um bimestre, notas de uma turma de uma disciplina em todos os bimestres, relação de professores de um curso, temperaturas diárias em um ano, cotação da bolsa em cinco anos, vendas diárias de uma loja, vendas diárias de todas as filiais, medições de grandezas, salários de empregados de uma empresa, e etc. Os exemplos citados poderiam ser submetidos a lógicas para obter: média de cada aluno em uma disciplina ordenados de várias formas, livro de chamada, relação de professores por curso, disciplina que mais reprova, regressão linear de grandezas medidas, semana com a menor temperatura média, folha de pagamento, e etc. Assim, é importante que uma linguagem de programação permita modelar e processar entidades que representam uma coleção de dados. Em contraste ao tipo escalar, o tipo de dado voltado para representar simultaneamente vários elementos é denominado tipo vetorial. A linguagem Pascal disponibiliza o tipo para definir variáveis capazes de armazenar uma coleção de elementos. Com este tipo de dado é possível modelar problemas que precisam dos conceitos de e . Introdução à Programação: Conceitos e Práticas. Página 221 22.1. Vetor Um vetor é uma estrutura composta por uma quantidade conhecida de elementos, sendo todos do mesmo tipo. O vetor também é chamado de agregado homogêneo. A seguinte figura compara uma variável com uma variável do : X: V: Tabela 2: Comparação Escalar x Vetor A variável é do tipo escalar (p. ex. ) e a variável é do tipo vetor ou . Apenas uma célula de memória está disponível para uso através de , enquanto que através de várias células estão disponíveis para o programa. A variável foi declarada como , e declarada como A linguagem é essencialmente escalar, sendo que as expressões refletem esta característica e operam somente sobre dados escalares18. Assim, as células de um vetor devem ser refenciadas individualmente, o que demanda que a variável seja complementada com uma informação que identifique especificamente uma célula. Este complemento é o , que indica a sua posição no vetor. Por exemplo, a primeira célula poderia ser [ ], a segunda [ ] e assim sucessivamente. Agora temos a definição completa do endereço de memória referente a uma célula vinculada ao vetor. Esta célula individualizada preenche todos requisitos de uma variável escalar, e também é denominada . 18 O tipo string não é um tipo primitivo do Pascal. Os dialetos Pascal incluem facilidades para manipular strings, que no detalhe é um array de char. Introdução à Programação: Conceitos e Práticas. Página 222 22.2. Sintaxe e Semântica O vetor é um com apenas uma dimensão, também denominado unidimensional. A sintaxe simplificada para a definição do tipo vetor é: [ ] A natureza do é necessariamente ordinal, ou seja, o índice de 19 20 um deve ser do tipo , , ou . Variáveis podem ser criadas a partir desta definição. O seguinte exemplo ilustra as várias possibilidades de declaração de uma variável do tipo . À direita são apresentadas algumas atribuições às células de cada , permitindo também compreender o processo de indexação. Declaração das variáveis Var Va Vb Vc Vx Vt Vq Vr Vs Vn : : : : : : : : : Array[1..100] Array[0..499] Array[-10..10] Array[1..1000] Array[0..10000] Array['a'..'z'] Array['0'..'9'] Array[False..True] Array[Boolean] Of Of Of Of Of Of Of Of Of Integer; LongInt; Word; Real; Char; Real; Real; Integer; Integer; Programa Principal Begin Va[3] Vb[35] Vc[-5] Vx[300] Vt[0] Vq['d'] Vr['4'] Vs[False] Vn[True] End. := := := := := := := := := -1; 80; $ffff; 1.28; 'x'; 3.26; -1.32e10; -43; 43; A seguinte tabela ilustra a quantidade de memória que cada vetor ocupa, bem como destaca o número de elementos e o tamanho de cada elemento que compõe o . Declaração Va Vb Vc Vx Vt Vq 19 20 : : : : : : Array[1..100] Array[0..499] Array[-10..10] Array[1..1000] Array[0..10000] Array['a'..'z'] #bytes do #elementos Elemento Of Integer; 2 100 Of LongInt; 4 500 Of Word; 2 21 Of Real; 8 1000 Of Char; 1 10001 Of Real; 8 26 #bytes do array 200 2000 42 8000 10001 208 Inclui as variações dos inteiros. O tipo enumerado será apresentado mais adiante. Introdução à Programação: Conceitos e Práticas. Página 223 Vr : Array['0'..'9'] Of Real; Vs : Array[False..True] Of Integer; Vn : Array[Boolean] Of Integer; 8 2 2 10 2 2 80 4 4 A forma utilizada para especificar o tamanho de um é estabelecendo uma faixa para o índice. O primeiro elemento da faixa é o índice da primeira célula, e o último elemento da faixa é o índice para a última célula. As células serão de tamanho suficiente para acomodar um elemento do tipo base. Nos exemplos acima, temos que o foi definido com elementos do tipo , sendo que a primeira célula é índice e a última é índice .O foi definido com elementos do tipo , sendo que o primeiro é índice e o último é índice . O índice do é do tipo , sendo que a primeira célula é identificada pelo e a última pelo , perfazendo ao todo elementos. Os índices do tipo devem ser interpretados como similares a índices inteiros equivalentes a posição do caractere na tabela . A seguinte figura ilustra estes conceitos para a variável : O índice de um deve ser uma expressão compatível com o tipo utilizado na definição da dimensão. Seguem alguns exemplos de índices com expressões cujo resultado é de tipo compatível com a definição do índice. Var Va : Vq : Vs : Vt : I : Begin Array[1..100] Array['a'..'z'] Array[False..True] Array[0..10000] Integer; Of Of Of Of Integer; Real; Integer; Char; Introdução à Programação: Conceitos e Práticas. Página 224 Va[I-1] := -125; Vq[Chr(Ord('a') + I Mod 27)] := -2.16e-32; Vs[I Mod 2 = 0] := 27; Vt[3*Va[I]+120] := Chr (Vs[False] mod 45); End. A figura a seguir faz a conexão entre a definição da variável do tipo e a linha de código com o comando de atribuição. Observe que no caso da variável , o índice é definido como e na expressão de atribuição o acesso a célula é feito com uma expressão [ ] Ainda em , o tipo de cada elemento é definido como e o valor atribuído também é . O mesmo ocorre com a variável , onde o índice é definido como e na expressão de atribuição o acesso a célula é feito com uma expressão do tipo [ ] para o índice. Ainda em , o tipo de cada elemento é definido como real e o valor atribuído na expressão também é . A compatibilidade é mantida para e . Figura 67: Correspondência entre Definição do Vetor e a Atribuição. O tipo é de fato um vetor ou de char, que em função da sua importância em grande parte dos problemas, fez com que a maioria das versões de trouxessem facilidades embutidas para processamento. A principal facilidade é a atribuição, onde uma instrução do tipo faz com que os caracteres sejam armazenados em sequência a partir da célula [ ]. 22.3. Exemplos i. Reescrever o programa que lê vários números fornecidos via teclado e apresente ao final algumas estatísticas, tais como: somatória destes valores, média, maior valor, e etc. A lógica deste problema será alterada em relação às várias versões já implementadas. A parte de entrada de dados será separada da parte de processamento dos dados. Assim, um módulo fará a leitura dos dados e os armazenará em um vetor e outros módulos Introdução à Programação: Conceitos e Práticas. Página 225 farão os cálculos que forem necessários. O programa principal fará a junção destes módulos para atender ao requisito do problema. Segue a solução proposta: Program ExLERVETOR; Type TVReal = Array[1..100] Of Real; Procedure LerDados (Var V : TVReal; Var N : Integer); Var X : Real; Begin Writeln ('Entre com os dados'); N := 0; Readln (X); While X <> -1 Do Begin Inc (N); V[N] := X; Readln (X); End; End; Function SomaVet (V : TVReal; N : Integer) : Real; Var I : Integer; S : Real; Begin S := 0; For I := 1 To N Do S := S + V[I]; SomaVet := S; End; Function MaiorVet (V : TVReal; N : Integer) : Real; Var I : Integer; M : Real; Begin M := V[1]; For I := 2 To N Do If V[I] > M Then M := V[I]; MaiorVet := M; End; Var Vx : TVReal; Nx : Integer; Soma, Media, Maior : Real; Begin Introdução à Programação: Conceitos e Práticas. Página 226 LerDados (Vx, Nx); Soma := SomaVet (Vx, Nx); Media := Soma/Nx; Maior := MaiorVet (Vx, Nx); Writeln ('SOMA = ', Soma :7:2); Writeln ('MEDIA = ', Media:7:2); Writeln ('MAIOR = ', Maior:7:2); End. O problema foi modularizado e deu origem a três rotinas: a procedure e as funções e . O programa principal organiza as chamadas a fim de atender a especificação do problema. Observe que no programa principal, inicialmente é invocada a procedure , que interage com o usuário fazendo a aquisição de dados e armazenando na memória. Em seguida, três linhas de código fazem os cálculos da , e do . Seguem os comentários de cada módulo: A procedure está definida através do seguinte cabeçalho: Procedure LerDados (Var V : TVReal; Var N : Integer); Esta definição informa que são necessários dois parâmetros passados pelo método por referência. Isto indica que o trabalho feito pela rotina será devolvido ao programa chamador através dos parâmetros e . É importante destacar que será prática comum na manipulação de arrays que além da variável com os elementos, também acompanhará a variável que indica a quantidade de elementos efetivamente em uso. Isto ocorre normalmente pelo fato da definição da variável array sempre reservar um tamanho máximo fixo, e de fato as lógicas trabalham com qualquer quantidade de elementos até este limite máximo. Esta quantidade deve ser indicada para as rotinas que processarão o vetor. Voltando à rotina , temos que o primeiro parâmetro é do tipo segundo é do tipo . Caso não houvesse restrição por parte do cabeçalho seria o seguinte: e o , o Procedure LerDados (Var V : Array[1..100] Of Real; Var N : Integer); Porém, o requer que o tipo de um parâmetro seja expresso por apenas uma palavra, e não por uma sentença. Assim, deve ser utilizado um recurso da linguagem, que permite fazer associação de tipos. É possível criar um novo tipo a partir de outros já existentes. Deste modo, o programador contorna esta situação utilizando a cláusula , como em: Type TVReal = Array[1..100] Of Real; Com esta definição colocada no topo do programa, podemos então criar variáveis ou parâmetros do tipo . O uso de também é aceito por vários compiladores, porém será estimulada a prática de definição de um novo tipo. Introdução à Programação: Conceitos e Práticas. Página 227 Apresentado e esclarecido este conceito, então a especificação do módulo é efetuar a leitura de alguns números via teclado e armazená-los no parâmetro . Além disso, a quantidade de dados lidos será armazenada no parâmetro . Assim, ao invocar esta procedure, os argumentos deverão ser variáveis ( ) e ao retornar ao chamador, estes argumentos terão os mesmos conteúdos atribuídos aos parâmetros. A lógica interna de consiste em: Writeln ('Entre com os dados'); N := 0; Readln (X); While X <> -1 Do Begin Inc (N); V[N] := X; Readln (X); End; Escrever mensagem na tela Zerar o contador de elementos Ler dado e armazenar em X Loop enquanto valor digitado ≠ -1 Para um X válido Incrementar o contador de elementos Armazenar elemento X na célula V[N] Ler dado e armazenar em X Voltar para início do loop. O seguinte diagrama lógico ilustra graficamente o fluxo do processamento: Vamos supor que o programa está em execução e durante a sessão de execução o usuário entrou com os seguintes números: e . Nestas condições apresentamos na sequência o comportamento da memória nos vários ciclos da repetição: Introdução à Programação: Conceitos e Práticas. Página 228 Primeiro Ciclo N: 0/1 V: V[1] := X X: 25 inc(N) 25 ? ? ? ? V[1] V[2] V[3] V[4] V[5] N: X: 32 inc(N) X <> -1 True False V[96] V[97] V[98] V[99] V[100] ? ? ? ? ? fim V[1] V[2] V[3] V[4] V[5] V[96] V[97] V[98] V[99] V[100] fim Terceiro Ciclo 2/3 V: V[3] := X X: 25 32 ? ? ? False ? ? ? ? ? N: V: ... True 1/2 V[2] := X ... X <> -1 Segundo Ciclo 18.2 inc(N) V[1] V[2] V[3] V[4] V[5] N: 3 X: -1 V: X <> -1 True False 25 32 18.2 ? ? V[1] V[2] V[3] V[4] V[5] ... True 25 32 18.2 ? ? ... X <> -1 Término do Loop False ? ? ? ? ? V[96] V[97] V[98] V[99] V[100] fim ? ? ? ? ? V[96] V[97] V[98] V[99] V[100] fim a) Incialmente é zero, o usuário fornece o número , a condição do loop é testada, o processamento entra no bloco , incrementa que passa a conter , armazena o valor de na célula [ ] e ainda no interior do faz uma nova leitura de dado. Introdução à Programação: Conceitos e Práticas. Página 229 b) O usuário fornece o número , a condição do loop é testada, o processamento entra no bloco , incrementa que passa a conter , armazena o valor de na célula [ ] e ainda no interior do faz uma nova leitura de dado. c) O usuário fornece o número , a condição do loop é testada, o processamento entra no bloco , incrementa que passa a conter , armazena o valor de na célula [ ] e ainda no interior do loop faz uma nova leitura de dado. d) O usuário fornece o número repetição. , a condição do loop é testada e indica o fim da Após o final do loop a variável conterá , indicando a quantidade de elementos armazenados, e as três primeiras células do vetor conterão os dados fornecidos. O fluxo de execução retorna para a unidade chamadora, que neste caso é o programa principal, e avança para a próxima linha de código. Begin LerDados (Vx, Nx); Soma := SomaVet (Vx, Nx); Media := Soma/Nx; Maior := MaiorVet (Vx, Nx); Writeln ('SOMA = ', Soma :7:2); Writeln ('MEDIA = ', Media:7:2); Writeln ('MAIOR = ', Maior:7:2); End. Chamar LerDados com Vx e Nx Obter a soma com SomaVet com Vx e Nx Obter a média Obter o maior com MaiorVet com Vx e Nx Mostrar a soma Mostrar a média Mostrar o maior A atribuição inicia com a avaliação da expressão que contém apenas a chamada a função SomaVet com os argumentos e . A lógica interna de consiste em: Function SomaVet (V : TVReal; N : Integer) : Real; Var I : Integer; S : Real; Begin S := 0; For I := 1 To N Do S := S + V[I]; O primeiro parâmetro é um vetor e o segundo parâmetro é um inteiro que indica a quantidade de elementos do vetor. Variáveis para o For e para acumular a soma. SomaVet := S; End; Resultado da função em S Zerar a soma Repetir N vezes Acumula em S o valor de V[I] Encerrada a chamada de o controle volta para a unidade chamadora que é o programa principal, que completa o processamento da linha, atribuindo o resultado de à variável . Introdução à Programação: Conceitos e Práticas. Página 230 Na sequência é executada a linha que avalia a expressão indicada, calculando a divisão da soma pelo número de termo, e armazena o resultado em . A próxima linha é os argumentos e consiste em: que invoca a função copiando para os parâmetros desta função. A lógica interna de Function MaiorVet (V : TVReal; N : Integer) : Real; Var I : Integer; M : Real; Begin M := V[1]; For I := 2 To N Do If V[I] > M Then M := V[I]; MaiorVet := M; End; O primeiro parâmetro é um vetor e o segundo parâmetro é um inteiro que indica a quantidade de elementos do vetor. Variáveis para o For e para armazenar o maior Iniciar Maior com o primeiro elemento de V Repetir para I de 2 até N Se o elemento V[I] > do que o maior atual Então faça V[I] ser o maior Resultado da função em maior Encerrada a chamada de o controle volta para a unidade chamadora que é o programa principal, que completa o processamento da linha, atribuindo o resultado de à variável . As próximas três linhas: Writeln ('SOMA = ', Soma :7:2); Writeln ('MEDIA = ', Media:7:2); Writeln ('MAIOR = ', Maior:7:2); escrevem na tela os conteúdos das três variáveis , e . A seguinte interface de entrada e saída ilustra a interação do programa com o usuário: Entre 25 32 18.2 -1 SOMA MEDIA MAIOR com os dados = = = Introdução à Programação: Conceitos e Práticas. 75.20 25.07 32.00 Página 231 ii. Escrever um programa que calcule o valor de e de um valor de : a partir dos coeficientes do polinômio ⋯ A sugestão para este problema é estruturar a lógica nos módulos para: a) Entrada de dados que obtém via teclado os valores de de e os armazena na memória; b) Cálculo do valor de a partir de c) Apresentar o vetor e os resultados na tela. ⋯ e Segue a solução proposta: Program ExPOLI; Type TVReal = Array[1..100] Of Real; Procedure LerDados (Var V : TVReal; Var N : Integer); Var X : Real; Begin Writeln ('Entre com os dados'); N := 0; Readln (X); While X <> -1 Do Begin Inc (N); V[N] := X; Readln (X); End; End; Procedure MostrarVet (V : TVReal; N : Integer); Var I : Integer; Begin For I := 1 To N Do Writeln ('V[', I:2,'] = ', V[I]:8:3); End; Function Polinomio (V : TVReal; N : Integer; X : Real) : Real; Var I : Integer; S : Real; Begin S := 0; For I := N DownTo 1 Do S := S*X + V[I]; Polinomio := S; Introdução à Programação: Conceitos e Práticas. Página 232 End; Var Vx Nx X, Y : TVReal; : Integer; : Real; Begin LerDados (Vx, Nx); Write ('Entre com X = '); Readln (X); Y := Polinomio (Vx, Nx, X); MostrarVet (Vx, Nx); Writeln ('P(', X:5:2, ') = ', Y:7:2); End. A procedure é a mesma do exercício anterior. A função que calcula o valor de foi implementada utilizando a seguinte forma, denominada fatoração de : ( ( ( ⋯ ))) A lógica é semelhante ao código aplicado para solucionar os exercícios sobre séries. Compare as duas lógicas e identifique a pequena alteração feita. A procedure apresenta na tela os coeficientes do polinômio permitindo ao usuário inspecionar se foram considerados os dados certos. O programa principal ordena as chamadas na sequência lógica estabelecida para este problema, e proporciona a seguinte interface: Entre com os dados 1 2 3 -1 Entre com X = 3 V[ 1] = 1.000 V[ 2] = 2.000 V[ 3] = 3.000 P( 3.00) = 34.00 iii. Escrever um programa que localiza a posição de um elemento em um vetor. Problemas de busca são corriqueiros em grande parte dos problemas. Isto faz com que se estude muito sobre métodos eficientes para este propósito. Neste exercício faremos implementações básicas, ficando fora do escopo métodos mais elaborados. Segue uma solução para a busca por um elemento em um vetor não ordenado: Program ExBUSCA; Introdução à Programação: Conceitos e Práticas. Página 233 Type TVReal = Array[1..100] Of Real; Procedure LerDados (Var V : TVReal; Var N : Integer); {igual aos exemplos anteriores} End; { Function Buscar (V : TVReal; N : Integer; X : Real) : Integer; Var P, I : Integer; Begin P := 0; For I := 1 To N Do If V[I] = X Then Begin P := I; Break; End; Buscar := P; End; Function Buscar (V : TVReal; N : Integer; X : Real) : Integer; Var I : Integer; Begin I := 1; While (I <= N) And (V[I] <> X) Do Inc(I); If I > N Then Buscar := 0 Else Buscar := I; End; } Function Buscar (V : TVReal; N : Integer; X : Real) : Integer; Var I : Integer; Begin Inc (N); V[N] := X; I := 1; While (V[I] <> X) Do Inc(I); If I = N Then Buscar := 0 Else Buscar := I; End; Introdução à Programação: Conceitos e Práticas. Página 234 Var Vx X : TVReal; : Real; Nx P : Integer; : Integer; Begin LerDados (Vx, Nx); X := 28; P := Buscar (Vx, Nx, X); Writeln ('Posicao de ',X:5:1,' = ', P); End. Foram incluídas três versões distintas de uma busca linear, onde os elementos do vetor são percorridos a fim de encontrar a primeira ocorrência do elemento . Caso este elemento não seja encontrado a função retorna zero. Duas das funções estão como comentário. A primeira versão utiliza a seguinte lógica: P := 0; For I := 1 To N Do If V[I] = X Then Begin P := I; Break; End; Inicialmente a posição é definida como zero, e parte-se para percorrer todo o vetor em busca do elemento , que uma vez encontrado á posição é salva, e a repetição é encerrada. A segunda versão utiliza a seguinte lógica: I := 1; While (I <= N) And (V[I] <> X) Do Inc(I); If I > N Then Buscar := 0 Else Buscar := I; A estratégia é a mesma da implementação anterior, porém o com o é substituído pelo . A expressão lógica que controla a repetição indica que o loop deve ser encerrado assim que encontrar um [ ] ou quando já percorreu todo o vetor sem nada encontrar . Ao terminar o , o valor de é testado para saber se o seu valor não extrapolou o tamanho do vetor, que em caso positivo significa elemento não encontrado. A terceira versão utiliza a seguinte lógica: Inc (N); V[N] := X; Introdução à Programação: Conceitos e Práticas. Página 235 I := 1; While (V[I] <> X) Do Inc(I); If I = N Then Buscar := 0 Else Buscar := I; Esta função visa simplificar a expressão que controla o , fazendo com que fique apenas um teste. Para que isto seja feito com segurança, observe que as duas primeiras linhas cuidam de inserir o elemento no final do vetor, e incrementar o comprimento do vetor em . Assim, há a certeza de que pelo menos um elemento exista no vetor. Caso a busca indique que o elemento encontrado está na posição do novo , então significa que no vetor original o não foi encontrado. Destaca-se também que a instrução controlada pelo é simplemente o incremento de , cujo significado é simplesmente avançar a busca para o próximo elemento. Segue uma interface de entrada e saída para um caso exemplo: Entre com os dados 18 32 28 17 -1 Posicao de 28.0 = 3 iv. Escrever um programa que ordene um vetor de números em ordem decrescente: Segue a proposta de uma lógica bem simples: Program ExORDENA; Type TVReal = Array[1..100] Of Real; Procedure LerDados (Var V : TVReal; Var N : Integer); Begin {igual ao exemplo anterior} End; Procedure MostrarVet (V : TVReal; N : Integer); Begin {igual ao exemplo anterior} End; Procedure Trocar (Var A, B : Real); var T : Real; Introdução à Programação: Conceitos e Práticas. Página 236 Begin T := A; A := B; B := T; End; Function GetPosMaior (V : TVReal; Ini, N : Integer) : Integer; Var P, I : Integer; Begin P := Ini; For I := Ini+1 To N Do If V[I] > V[P] Then P := I; GetPosMaior := P; End; Procedure MaiorFirst (Var V : TVReal; Ini, N : Integer); Var P, I : Integer; Begin P := GetPosMaior (V, Ini, N); Trocar (V[Ini], V[P]); End; Procedure Ordenar (Var V : TVReal; N : Integer); Var I : Integer; Begin For I := 1 To N-1 Do MaiorFirst (V, I, N); End; Var Vx Nx : TVReal; : Integer; Begin LerDados (Vx, Nx); Ordenar (Vx, Nx); MostrarVet (Vx, Nx); End. O programa principal indica a sequência da lógica mais abstrata, que é: 1. Ler os dados do vetor para a memória; 2. executar a ordenação; e 3. apresentar o resultado na tela. Os módulos e são os mesmos do exemplo anterior. A procedure encaminha o vetor e seu tamanho para o processo de ordenação. A procedure recebe estes argumentos nos parâmetros e . A passagem de Introdução à Programação: Conceitos e Práticas. Página 237 parâmetro do vetor é por referência, implicando que toda alteração no parâmetro reflete no argumento . Procedure Ordenar (Var V : TVReal; N : Integer); Var I : Integer; Begin For I := 1 To N-1 Do MaiorFirst (V, I, N); End; A lógica da procedure 1. 2. 3. 4. 5. é: Trazer o maior elemento entre Trazer o maior elemento entre Trazer o maior elemento entre ... Trazer o maior elemento entre e e e para a posição ; para a posição ; para a posição ; e para a posição . Ao final desta sequência o vetor estará em ordem decrescente. A forma geral desta sequência é: Trazer o maior elemento entre e para a posição , com . Esta lógica foi transformada em: variando de a For I := 1 To N-1 Do MaiorFirst (V, I, N); A rotina posições abaixo: e faz o trabalho de identificar a posição do maior elemento entre as e trazê-lo para a posição do vetor , como indicado no código Procedure MaiorFirst (Var V : TVReal; Ini, N : Integer); Var P, I : Integer; Begin P := GetPosMaior (V, Ini, N); Trocar (V[Ini], V[P]); End; A abstração está novamente presente nesta rotina, pois trazer para a posição do vetor o maior elemento entre e , foi codificado em duas linhas, sendo uma que chama que retorna a posição do maior elemento de entre as posições e . A segunda linha executa o módulo que os conteúdos das células e . A função tem o seguinte código: Function GetPosMaior (V : TVReal; Ini, N : Integer) : Integer; Var P, I : Integer; Begin P := Ini; Introdução à Programação: Conceitos e Práticas. Página 238 For I := Ini+1 To N Do If V[I] > V[P] Then P := I; GetPosMaior := P; End; Neste módulo a lógica utilizada assume que o primeiro elemento do intervalo dado é a posição do maior elemento, e em seguida o vetor é percorrido do segundo elemento do intervalo até o seu final, perguntando se o elemento é maior que o maior atual. Em caso positivo, a posição do novo elemento assume como a posição do maior. Ao final, este valor é retornado como resultado da função. A procedure recebe dois parâmetros por referência, ou seja, dois endereços de memória, e executa a troca destes conteúdos. O conteúdo da primeira variável é copiado para a segunda e vice-versa. Uma variável auxiliar é utilizada para não haver perda de conteúdo nas movimentações. O programa principal ordena as chamadas na sequência lógica estabelecida para este problema, e proporciona a seguinte interface: Entre 6 8 3 9 2 7 -1 V[ 1] V[ 2] V[ 3] V[ 4] V[ 5] V[ 6] com os dados = = = = = = 9.000 8.000 7.000 6.000 3.000 2.000 A lógica utilizada é chamada de método de ordenação por seleção direta. Outros métodos estão disponíveis e são objetos de disciplina específica. A título de exemplo, segue o módulo que ordena pelo método denominado : Procedure BubbleSort (Var V : TVReal; N : Integer); Var I, J : Integer; Begin For I := 2 To N-1 Do For J := N DownTo I Do If V[J-1] < V[J] Then Trocar (V[J-1], V[J]); End; Para o vetor inicializado com os dados do caso exemplo, ilustramos na sequência o passo a passo de cada um dos cinco ciclos de execução da lógica do módulo , até alcançar a configuração final: Introdução à Programação: Conceitos e Práticas. Página 239 I: N: 1 6 6 8 3 9 2 7 V[1] V[2] V[3] V[4] V[5] V[6] P: I: 2 N: 6 9 8 3 6 2 7 4 N: 3 6 V[1] V[2] V[3] V[4] V[5] V[6] P: 6 I: 4 N: 6 9 8 7 6 2 3 5 6 V[1] V[2] V[3] V[4] V[5] V[6] V: P: 6 V[1] V[2] V[3] V[4] V[5] V[6] P: 4 9 8 7 6 3 2 V[1] V[2] V[3] V[4] V[5] V[6] ... ... v. 2 ... ... I: N: 9 8 7 6 2 3 P: ... ... I: 9 8 3 6 2 7 V[1] V[2] V[3] V[4] V[5] V[6] Escrever um programa que forneça as seguintes operações sobre um vetor de números: a. Copiar para os elementos de que se encontram entre as posições e ; b. Retornar a posição da primeira ocorrência dos elementos do vetor em um vetor ; c. Eliminar os elementos do vetor que estejam entre as posições e , inclusive; A de parte dos elementos de um vetor para um segundo vetor deve considerar os movimentos de memória ilustrados na seguinte figura: Introdução à Programação: Conceitos e Práticas. Página 240 p q Va: 1 2 3 4 Vb: 2 3 4 5 5 6 7 8 Figura 68: Cópia de Sub-Vetor. A quantidade de elementos copiada é dada por , e os índices das células do vetor de origem são diferentes dos índices das células do vetor de destino. Se a repetição for de a , então os índices envolvidos serão [ ] [ ]. A busca pela de uma sequência de elementos dentro de outra sequência de elementos pode ser estruturada pela comparação dos primeiros elementos de e . Não havendo coincidência, então se compara os elementos a partir do segundo elemento de com os elementos de . Esta comparação será feita vezes, ou até que uma igualdade seja verificada e a posição armazenada. A seguinte figura ilustra esta lógica: V: 1 2 3 4 5 6 7 8 1° Ciclo: Falha 1° Ciclo Vt: 2 3 4 5 V: 1 2 3 4 5 6 7 8 2° Ciclo: Sucesso 2° Ciclo Vt: 2 3 4 5 Figura 69: Posição de Sub-Vetor. No exemplo deste diagrama, a quantidade máxima de repetições que seria feita a comparação entre parte de com seria vezes, pois irá escorregando até que o último elemento de e de fiquem emparelhados. Logo, serão feitas comparações de vetores. Para fazer a comparação dos elementos de cada vetor, a lógica inicia o resultado com a posição do primeiro elemento do sub-vetor de a ser comparado, e testa elemento a elemento em busca de alguma diferença. Caso sejam encontrados elementos distintos então a repetição interna é interrompida, e uma próxima comparação de sub-vetor será feita em novo ciclo. Caso não haja diferença, então é constatado que Introdução à Programação: Conceitos e Práticas. Página 241 foi encontrado um sub-vetor idêntico ao vetor pesquisado, e o ciclo principal é encerrado. A terceira lógica trata de os elementos entre as posições e de um vetor . A seguinte figura ilustra os movimentos necessários. Os elementos que estão a partir da posição deverão ser copiados para as posições a partir de . p q V: 1 2 3 4 V: 1 6 7 8 5 6 7 8 N=8 N=4 Figura 70: Eliminação de um Sub-Vetor. Segue a implementação destas lógicas: Program ExVETOR; Type TVReal = Array[1..100] Of Real; Procedure LerDados (Var V : TVReal; Var N : Integer); Begin {igual ao exemplo anterior} End; Procedure MostrarVet (V : TVReal; N : Integer); Begin {igual ao exemplo anterior} End; Procedure EliminarVet (Var V : TVReal; Var N : Integer; p, q : integer); Var I, D : Integer; Begin D := Q-P+1; For I := Q+1 To N Do V[I-D] := V[I]; N := N-D; End; Procedure CopiarToVet (Va : TVReal; Na : Integer; p, q : Integer; Introdução à Programação: Conceitos e Práticas. Página 242 Var Vb : TVReal; Var Nb : Integer); Var I : Integer; Begin For I := p To q Do Vb[I-p+1] := Va[I]; Nb := q-p+1; End; Function PosVet (Vt : TVReal; Nt : Integer; V : TVReal; N : Integer) : Integer; Var P, I, J : Integer; Begin P := 0; For I := 1 To N - Nt + 1 Do Begin P := I; For J := 1 To Nt Do If V[I+J-1] <> Vt[J] Then Begin P := 0; Break; End; If P <> 0 Then Break; End; PosVet := P; End; Var Vx Vy V : TVReal; : TVReal; : TVReal; Nx Ny N : Integer; : Integer; : Integer; Begin LerDados (V, N); CopiarToVet (V, N, 2, 5, Vx, Nx); MostrarVet (Vx, Nx); Writeln ('POS = ', PosVet (Vx, Nx, V, N)); EliminarVet (V, N, 2, 5); MostrarVet (V, N); End. A rotina altera o próprio vetor passado como argumento, o que demanda a passagem de parâmetro por referência. A rotina copia um vetor origem para um vetor destino, o que determina que a passagem do vetor destino deva ser necessariamente por referência, pois as alterações no parâmetro deverá ser refletida no argumento. Introdução à Programação: Conceitos e Práticas. Página 243 O programa principal ordena as chamadas na sequência lógica estabelecida para este problema, e proporciona a seguinte interface: Entre 1 2 3 4 5 6 7 8 -1 V[ 1] V[ 2] V[ 3] V[ 4] POS = V[ 1] V[ 2] V[ 3] V[ 4] vi. com os dados Vetor V = = = = 2 = = = = 2.000 3.000 4.000 5.000 1.000 6.000 7.000 8.000 Vetor Vx Posição de Vx em V Novo Vetor V Escrever um programa que conta a quantidade de vezes que cada letra do alfabeto se repete em uma . A solução deverá receber uma e produzir como resultado um vetor com as quantidades de vezes que cada letra aparece repetida nesta . Segue a proposta de uma solução: Program ExCONTALETRA; Type TVInt = Array['A'..'Z'] Of Integer; Procedure ContarLetra (S : String; Var V : TVInt); Var I : Integer; C : Char; Begin For C := 'A' To 'Z' Do V[C] := 0; For I := 1 To Length(S) Do Begin C := UpCase(S[I]); If (C >= 'A') AND (C <= 'Z') Then Inc (V[C]); End; End; Procedure MostrarVet (V : TVInt); Var C : Char; Introdução à Programação: Conceitos e Práticas. Página 244 Begin For C := 'A' To 'Z' Do If V[C] <> 0 Then Writeln ('V[''', C,'''] = ', V[C]:4); End; Var S : String; Vc : TVInt; Begin Readln (S); ContarLetra (S, Vc); MostrarVet (Vc); End. A rotina implementa a lógica principal. Observar que ao contrário do que foi dito anteriormente, este caso não demanda a presença de uma variável com a quantidade de elementos em uso no vetor, uma vez que o vetor utilizado foi definido com o mesmo tamanho que a quantidade de letras do alfabeto. A seguinte definição de tipo é a base para o vetor com as contagens: Type TVInt = Array['A'..'Z'] Of Integer; O índice do vetor é do tipo char, sendo uma célula associada a cada letra de a . Recordamos que o Pascal permite que o índice de seja qualquer faixa do tipo ordinal. A seguinte lógica foi implementada na rotina : For C := 'A' To 'Z' Do V[C] := 0; For I := 1 To Length(S) Do Begin C := UpCase(S[I]); If (C >= 'A') AND (C <= 'Z') Then Inc (V[C]); End; O primeiro serve para zerar cada uma das células que irão conter as quantidades de cada letra na . O segundo , percorre cada caractere da string , perguntando se o seu equivalente maiúsculo é uma letra de a . Em caso positivo, a célula correspondente àquela letra é incrementada de . Introdução à Programação: Conceitos e Práticas. Página 245 Segue a seguinte interface de entrada e saída para um caso: ola mundo belo, bom dia V['A'] = 2 V['B'] = 2 V['D'] = 2 V['E'] = 1 V['I'] = 1 V['L'] = 2 V['M'] = 2 V['N'] = 1 V['O'] = 4 V['U'] = 1 22.4. Matriz O tipo utilizado para definir vetores também pode ser utilizado para definir matrizes. Muitos problemas do mundo real podem ser modelados sob a forma de matrizes tanto pela conveniência da representação quanto pela necessidade de fazer uso da álgebra matricial. As linguagens imperativas clássicas não trazem operadores embutidos para processar matrizes, ficando ao programador a incumbência de codificar as rotinas com os recursos escalares da linguagem. Algumas linguagens do paradigma funcional, tal como , disponibilizam uma grande quantidade de operadores matriciais. A seguinte figura compara tipos , e : 1 X: V: X V[I] [1] [2] [3] [4] [5] 2 ... J M: ... ? 1 2 3 4 ... I ... ? [I] M[I,J] [?] Figura 71: Comparação Escalar, Vetor e Matriz Introdução à Programação: Conceitos e Práticas. Página 246 A variável pode ter seu conteúdo acessado diretamente através do nome da variável, enquanto que um necessita do nome mais um índice, e a matriz requer além do nome mais dois índices, um para linha e outro para coluna. Este último caso, para matrizes de duas dimensões. De forma geral, uma matriz de dimensão demanda índices para identificar uma célula. 22.5. Sintaxe e Semântica A sintaxe simplificada para a definição de um array com [ dimensões é: { }] A diferença em relação a definição de um vetor está na inclusão de uma lista de . A notação { } significa zero ou mais repetições do conteúdo entre chaves. Assim, caso não tenha nenhuma repetição desta parte, o array fica reduzido a um vetor. Ficam valendo as observações feitas no tópico sobre vetores, onde a natureza do é necessariamente escalar, ou seja, o índice 21 22 de um deve ser do tipo , , ou . Variáveis podem ser criadas a partir desta definição. O seguinte exemplo ilustra as várias possibilidades de declaração de uma variável do tipo . À direita são apresentadas algumas atribuições às células de cada , permitindo também compreender o processo de indexação. Var Ma Mb Mc Mx Mt Mq Mr Ms Mn Vs : : : : : : : : : : Array[1..100, 1..30] Array[0..499, 1..50] Array[-10..10, -5..5] Array[1..1000,'a'..'z'] Array[0..10000, 1..100] Array['a'..'z', 1..50] Array['0'..'9', 1..80] Array[False..True, 'a'..'z'] Array[Boolean, 0..99] Array[1..100] Begin Ma[3 , Ma[3][10] Mb[35 , Mc[-5 , Mx[300 , 21 22 10 ] 20 ] 3 ] 'd'] := := := := := Of Of Of Of Of Of Of Of Of Of Integer; LongInt; Word; Real; Char; Real; Real; Integer; Integer; String; -1; 0; 80; $ffff; 1.28; Inclui as variações dos inteiros. O tipo enumerado será apresentado mais adiante. Introdução à Programação: Conceitos e Práticas. Página 247 Mt[0 , Mq['d' , Mr['4' , Ms[False, Mn[True , Vs[1] Vs[1,3] Vs[1][3] End 50 ] 30 ] 40 ] 'v'] 60 ] := := := := := := := := 'x'; 3.26; -1.32e10; -43; 43; 'ola mundo belo'; 'A'; 'E'; Os exemplos acima são exclusivamente de matrizes bidimensionais, porém sintaxe semelhante se aplica a arrays multidimensionais. Observar a definição da variável como vetor de , o que lhe confere também a característica de matriz, uma vez que cada célula de faz referência a uma , que é um vetor de char. Alguns compiladores estenderam a sintaxe do Pascal padrão permitindo a indexação tanto na forma [ ] ou [ ][ ], sendo esta última notação o estilo utilizado pela linguagem . Para não gerar problemas de migração de código, é recomendado manter o primeiro estilo. A exceção se dá no caso de vetor de , onde a segunda forma é a mais adequada. Todas as considerações feitas para vetor em relação às expressões válidas para o índice, também se aplicam para multidimensionais, o que vale dizer, que o índice de uma dimensão pode ser qualquer expressão compatível com o tipo adotado na definição da variável. Fica para o leitor analisar as variáveis declaradas e extrair a quantidade de memória alocada a cada uma delas. Observar que as dimensões dos foram definidas através de constantes numéricas. Em determinadas situações podem surgir lógicas que com frequência fazem uso do tamanho máximo de uma determinada dimensão. Assim, ao longo do código surgirão referências a este valor. Por exemplo, um determinado programa define a dimensão de uma matriz para linhas. Adicionalmente, em vários locais do programa existem expressões aritméticas e lógicas fixando este valor nos seus cálculos. Porém, quando por alguma necessidade a quantidade de linhas for redefinida para , o programador deverá percorrer todo o código em busca deste número para as devidas readequações. Nestas circunstâncias, é recomendado o uso da cláusula , onde constantes podem estar associadas a . A sintaxe da cláusula é: { } Os exemplos que seguem ilustram o uso deste recurso: Introdução à Programação: Conceitos e Práticas. Página 248 CONST ENTER ESCAPE SPACE MAXCOL MAXLIN MAXLEN MSG = = = = = = = #13; #27; #32; 100; 200; MAXCOL * MAXLIN; 'OLA MUNDO'; Begin {programa principal} end. O símbolo utilizado para relacionar o identificador com a constante é e não . 22.6. Exemplos i. Escrever um programa que preencha uma matriz bidimensional com dados lidos via teclado e apresente estes dados na tela. Uma matriz bidimensional é um array definido com dois índices. Assim, para percorrer cada uma das células da matriz, quer seja para armazenar um dado ou obter o dado armazenado, será demandado do programa, no caso de uma lógica simples, a utilização de duas repetições aninhadas. A repetição mais interna processa mais rapidamente que a repetição que a engloba, uma vez que o controle só retorna para a repetição externa quando a interna é encerrada. Um segundo ciclo da repetição mais externa certamente determinará um novo ciclo completo para a repetição interna. A seguinte figura ilustra este estilo de construção: Segue a proposta de uma solução: Program ExMATRIZ; Introdução à Programação: Conceitos e Práticas. Página 249 Const MAXL = 100; MAXC = 100; Type TMReal = Array[1..MAXL, 1..MAXC] Of Real; Procedure LerDados (Var M : TMReal; Var L, C : Integer); Function GetX (Lin, Col : Integer) : Real; Var X : Real; Begin Write ('M[',Lin,', ', Col,'] = '); Readln (x); GEtX := X; End; Var X : Real; Begin Writeln ('Entre com os dados:'); Writeln ('{-1} encerra LINHA'); Writeln ('{-2} encerra MATRIZ'); L := 0; C := 0; X := GetX (L+1, C+1); While X <> -2 Do Begin Inc (L); C := 0; If X = -1 Then X := GetX (L, C+1); While (X <> -1) AND (X <> -2) Do Begin Inc (C); M[L, C] := X; X := GetX (L, C+1); End; End; End; Procedure MostrarMat (M : TMReal; L, C : Integer); Var I, J : Integer; Begin For I := 1 To L Do Begin For J := 1 To C Do Write (M[I,J]:8:3); Writeln; End; End; Introdução à Programação: Conceitos e Práticas. Página 250 Var Mx : TMReal; Lx, Cx : Integer; Begin LerDados (Mx, Lx, Cx); MostrarMat (Mx, Lx, Cx); End. A convenção adotada para as leituras dos dados é: O valor O valor indica final de entrada de uma linha da matriz; indica final de entrada da matriz; Assim, na rotina foram implementados dois loops, sendo que o loop externo é encerrado quando uma entrada igual a é identificada, e o loop intermo é encerrado quando uma entrada ou é detectada. As variáveis e controlam em que posição da matriz o dado fornecido será armazenado. A variável é zerada antes de iniciar o loop externo e incrementada a cada ciclo de execução deste loop. A variável é zerada dentro do loop externo antes de iniciar o loop interno, e incrementado a cada ciclo deste loop interno. Assim, quando , o valor de será { }, quando , o valor de assumirá novamente cada valor deste conjunto. Ao encerrar a entrada de dados, as variáveis e conterão a dimensão real da matriz, parte efetivamente preenchida com dados válidos. A entrada de dados foi encapsulada na função , que recebe como parâmetros as posições e da matriz onde deverá ser armazenado o dado fornecido, permitindo que seja apresentada na tela uma mensagem explicativa. Cabe destacar que a função foi implementada dentro do escopo da procedure . Assim, ela é acessível somente no corpo desta procedure. Com este exemplo, pretende-se mostrar ao programador principiante que é possível, em , limitar a visibilidade de variáveis e rotinas ao escopo de outra rotina. A procedure escreve na tela os valores armazenados na matriz. São necessárias novamente duas repetições, porém neste caso como as dimensões da matriz já são conhecidas, então foi possível codificar a solução utilizando a instrução . Na rotina as dimensões só seriam conhecidas quando fosse encerrada a digitação dos dados. O tipo foi definido no seguinte contexto: Const MAXL = 100; MAXC = 100; Type TMReal = Array[1..MAXL, 1..MAXC] Of Real; As dimensões associadas ao tipo são definidas pelas constantes e , e não mais em função de constantes fixas. Isto proporciona uma maior facilidade no caso de redimensionamento de variáveis deste tipo. As modificações estarão concentradas em um único ponto do código. Introdução à Programação: Conceitos e Práticas. Página 251 Segue a seguinte interface de entrada e saída para um caso: Entre com os dados: {-1} encerra LINHA {-2} encerra MATRIZ M[1, 1] = 11 M[1, 2] = 12 M[1, 3] = 13 M[1, 4] = 14 M[1, 5] = -1 M[2, 1] = 21 M[2, 2] = 22 M[2, 3] = 23 M[2, 4] = 24 M[2, 5] = -1 M[3, 1] = 31 M[3, 2] = 32 M[3, 3] = 33 M[3, 4] = 34 M[3, 5] = -2 11.000 12.000 13.000 21.000 22.000 23.000 31.000 32.000 33.000 ii. 14.000 24.000 34.000 Escrever um programa que preencha uma matriz conforme o padrão que segue e apresente estes dados na tela. a) b) | | ⋯ ⋯ ⋯ ⋯ | | c) ⋯ ⋯ | | | | ⋯ ⋯ d) | | ⋯ ⋯ ⋯ ⋯ | | | | ⋯ ⋯ ⋯ ⋯ | | O formato geral de uma matriz é: Introdução à Programação: Conceitos e Práticas. Página 252 | | ⋯ ⋯ ⋯ ⋯ ⋯ | | Os casos apresentados possuem lógicas que conectam os índices de uma célula com o seu conteúdo. Por exemplo, no onde se pretende preencher uma matriz de acordo com o padrão dado pela matriz identidade, a lógica que orienta a forma de preenchimento é: Esta lógica deve ser aplicada a cada célula da matriz. Assim, a lógica para percorrer cada elemento da célula requer duas repetição do tipo , sendo a mais externa produzindo os índices para as linhas { } e a interna os índices para as colunas { }. As combinações de e geradas serão: { }. No , fica evidente que cada célula é preenchida com o seu índice de linha. O , a diagonal secundária é preenchida com e as demais células com zero. No caso de matriz quadrada ( ), uma célula pertencerá a diagonal secundária, quando a soma dos seus índices coincidir com o valor de . O trata de identificar quando uma célula está acima da diagonal principal. Uma rápida análise nos índices permite certificar que esta condição ocorre quando . Segue a proposta de uma solução: Program ExFILL; Const MAXL = 100; MAXC = 100; NEWLINE = #10; Type TMInt = Array[1..MAXL, 1..MAXC] Of Integer; Procedure Fill_A (Var M : TMInt; L, C : Integer); Var I, J : Integer; Begin For I := 1 To L Do For J := 1 To C Do M[I,J] := Ord (I=J); End; Introdução à Programação: Conceitos e Práticas. Página 253 Procedure Fill_B (Var M : TMInt; L, C : Integer); Var I, J : Integer; Begin For I := 1 To L Do For J := 1 To C Do M[I,J] := I; End; Procedure Fill_C (Var M : TMInt; L, C : Integer); Var I, J : Integer; Begin For I := 1 To L Do For J := 1 To C Do If I + J = L + 1 Then M[I,J] := 1 Else M[I,J] := 0; End; Procedure Fill_D (Var M : TMInt; L, C : Integer); Var I, J : Integer; Begin For I := 1 To L Do For J := 1 To C Do If J >= I Then M[I,J] := 1 Else M[I,J] := 0; End; Procedure MostrarMat (Msg : String; M : TMInt; L, C : Integer); Var I, J : Integer; Begin Writeln (Msg); For I := 1 To L Do Begin For J := 1 To C Do Write (M[I,J]:3); Writeln; End; End; Var Mx : TMInt; Lx, Cx : Integer; Begin Lx := 5; Cx := 5; Fill_A (Mx, Lx, Cx); MostrarMat (NEWLINE+'Exemplo a) ', Mx, Lx, Cx); Introdução à Programação: Conceitos e Práticas. Página 254 Fill_B (Mx, Lx, Cx); MostrarMat (NEWLINE+'Exemplo b) ', Mx, Lx, Cx); Fill_C (Mx, Lx, Cx); MostrarMat (NEWLINE+'Exemplo c) ', Mx, Lx, Cx); Fill_D (Mx, Lx, Cx); MostrarMat (NEWLINE+'Exemplo d) ', Mx, Lx, Cx); End. No preenchimento da matriz identidade optou-se pela forma a esquerda para a produção do ou do : For I := 1 To L Do For J := 1 To C Do M[I,J] := Ord (I = J); For I := 1 For J := If I = Then Else Foi utilizada a expressão lógica convertida para para produzir o valor desejado. No caso de resultado será , o que é exatamente o desejado. Na lógica da procedure To L Do 1 To C Do J M[I,J] := 1 M[I,J] := 0; ou ,o através da função será , e para optou-se pela forma convencional, com o uso do . Segue a seguinte interface de entrada e saída para o programa: Exemplo 1 0 0 1 0 0 0 0 0 0 a) 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 Exemplo 1 1 2 2 3 3 4 4 5 5 b) 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 Exemplo 0 0 0 0 0 0 0 1 1 0 c) 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 Exemplo 1 1 0 1 0 0 0 0 0 0 d) 1 1 1 0 0 1 1 1 1 0 1 1 1 1 1 Introdução à Programação: Conceitos e Práticas. Página 255 iii. Escrever um programa que forneça as seguintes informações a partir dos dados de uma matriz: a. As somas dos elementos de cada linha; b. As somas dos elementos de cada coluna; c. O valor a posição do maior elemento. A soma dos elementos de cada linha deverá produzir um resultado por linha, que resulta em um vetor com o comprimento igual a quantidade de colunas da matriz. ⋯ ⋯ ⋯ | | | | ⋯ ⋯ Esta matriz deverá ser processada para produzir a seguinte soma: ⋯ ⋯ ⋯ | | ⋯ ⋯ | | | | [ ] [ ] | [ ] | [ ] [] Lógica semelhante se aplica a soma orientada por coluna. Segue a proposta de uma solução: Program ExFILL; Const MAXL MAXC MAX NEWLINE = = = = 100; 100; 100; #10; Type TMInt = Array[1..MAXL, 1..MAXC] Of Integer; TVInt = Array[1..MAX] Of Integer; Procedure SomarLinha (Var M : TMInt; L, C : Integer; Var V : TVInt; Var N : Integer); Var I, J : Integer; Begin N := L; For I := 1 To L Do Introdução à Programação: Conceitos e Práticas. Página 256 Begin V[I] := 0; For J := 1 To C Do V[I] := V[I] + M[I,J]; End; End; Procedure SomarColuna (Var M : TMInt; L, C : Integer; Var V : TVInt; Var N : Integer); Var I, J : Integer; Begin N := L; For J := 1 To C Do Begin V[J] := 0; For I := 1 To L Do V[J] := V[J] + M[I,J]; End; End; Procedure GetMaior (Var M : TMInt; L, C : Integer; Var Lin, Col : Integer); Var I, J : Integer; Begin Lin := 1; Col := 1; For I := 1 To L Do For J := 1 To C Do If M[I,J] > M[Lin, Col] Then Begin Lin := I; Col := J; End; End; Procedure MostrarMat (Msg : String; M : TMInt; L, C : Integer); {igual aos exemplos anteriores} End; Procedure MostrarVet (Msg : String; V : TVInt; N : Integer); Var I : Integer; Begin Writeln (Msg); For I := 1 To N Do Writeln ('V[',I,'] = ', V[I]:3); End; Procedure Fill_D (Var M : TMInt; L, C : Integer); {igual ao exemplo anterior} Introdução à Programação: Conceitos e Práticas. Página 257 End; Var Mx : TMInt; Lx, Cx : Integer; Vx : TVInt; Nx : Integer; Lin, Col : Integer; Begin Lx := 5; Cx := 5; Fill_D (Mx, Lx, Cx); Mx[2, Cx] := 8; MostrarMat (NEWLINE+'Matriz ', Mx, Lx, Cx); SomarLinha (Mx, Lx, Cx, Vx, Nx); MostrarVet (NEWLINE+'Soma Linha ', Vx, Nx); SomarColuna (Mx, Lx, Cx, Vx, Nx); MostrarVet (NEWLINE+'Soma Coluna ', Vx, Nx); GetMaior (Mx, Lx, Cx, Lin, Col); Writeln (NEWLINE+'Posicao do Maior = (', Lin, ', ',Col, ')'); End. A lógica da utiliza duas repetições do tipo , sendo a mais externa associada às e a mais interna às . Desta forma, para cada valor de , será produzido um valor de para cada . Dizemos assim, que o índice da percorre mais rapidamente. N := L; For I := 1 To L Do Begin V[I] := 0; For J := 1 To C Do V[I] := V[I] + M[I,J]; End; De forma semelhante, a também utiliza duas repetições do tipo , sendo a mais externa associada às e a mais interna às . Desta forma, para cada valor de , será produzido um valor de para cada . Dizemos assim, que o índice da percorre mais rapidamente. N := L; For J := 1 To C Do Begin V[J] := 0; For I := 1 To L Do V[J] := V[J] + M[I,J]; End; Se considerarmos associado a e associado a , vemos que para percorrer as colunas mais rapidamente usamos o aninhamento ⋯ , e para o caso de percorrer as linhas mais rapidamente usamos o aninhamento ⋯ . Introdução à Programação: Conceitos e Práticas. Página 258 A lógica para obter a posição do maior elemento da matriz se assemelha à aplicada a um vetor, e consiste em percorrer cada elemento da matriz ⋯ testando se valor da célula é maior que o elemento da célula com o atual maior. Em caso positivo os valores de passam a conter os índices desta célula . Segue a seguinte interface de entrada e saída para o programa: Matriz 1 1 0 1 0 0 0 0 0 0 1 1 1 0 0 1 1 1 1 0 Soma V[1] V[2] V[3] V[4] V[5] Linha = 5 = 11 = 3 = 2 = 1 Soma V[1] V[2] V[3] V[4] V[5] Coluna = 1 = 2 = 3 = 4 = 12 1 8 1 1 1 Posicao do Maior = (2, 5) A matriz utilizada como teste foi preenchida pela lógica do caso acrescida da alteração de uma célula para o valor . do exemplo anterior, Fill_D (Mx, Lx, Cx); Mx[2, Cx] := 8; iv. Escrever um programa que efetua o produto matricial. Considerando o seguinte produto de duas matrizes: O produto será possível quando o número de colunas de seja igual ao número de linhas de ( ). A matriz resultante será de dimensão ( ). Segue um exemplo simplificado para o produto: Introdução à Programação: Conceitos e Práticas. Página 259 | | | | | | Um elemento da matriz é obtido pela soma dos produtos dos elementos da linha de pelos elementos da coluna de . Neste exemplo, o elemento | | é obtido da seguinte forma: | | | | Ou seja: Observe que todos os elementos de na expressão possuem a mesma linha de , e todos os elementos de possuem a mesma coluna de . Segue a expressão adaptada para esta expressão: Generalizando esta expressão para qualquer dimensão, e sabendo que , tem-se: ⋯ Sob a forma de somatória, um elemento de é obtido por: ∑ Logo, para todos os elementos de , tem-se: ∑ ⋯ ⋯ Segue a proposta de uma solução: Program ExMULT; Const MAXL = 100; MAXC = 100; NEWLINE = #10; Introdução à Programação: Conceitos e Práticas. Página 260 Type TMInt = Array[1..MAXL, 1..MAXC] Of Integer; Function StrToInt (S : String) : Integer; Var X, Code : Integer; Begin Val (S, X, Code); StrToInt := X; End; Procedure LerDados (Msg : String; Var M : TMInt ; Var L, C : Integer); Function GetX (Lin, Col : Integer) : String; Var SX : String; Begin Write ('M[',Lin,', ', Col,'] = '); Readln (Sx); GetX := Sx; End; Var Sx : String; Begin Writeln (Msg); Writeln ('{next} encerra LINHA'); Writeln ('{end} encerra MATRIZ'); L := 0; C := 0; SX := GetX (L+1, C+1); While Sx <> 'end' Do Begin Inc (L); C := 0; If SX = 'next' Then SX := GetX (L, C+1); While (SX <> 'next') AND (SX <> 'end') Do Begin Inc (C); M[L, C] := StrToInt(SX); SX := GetX (L, C+1); End; End; End; Procedure MostrarMat (Msg : String; M : TMInt; L, C : Integer); {igual aos exemplos anteriores} End; Procedure ProdMat (Ma : TMInt; La, Ca : Integer; Mb : TMInt; Lb, Cb : Integer; Var Mc : TMint; Var Lc, Cc : Integer); Var I, J, K, S : Integer; Begin Introdução à Programação: Conceitos e Práticas. Página 261 Lc := 0; If Ca = Lb Then Begin Lc := La; Cc := Cb; For I := 1 To Lc Do For J := 1 To Cc Do Begin S := 0; For K := 1 To Ca Do S := S + Ma[I,K]*Mb[K,J]; Mc[I,J] := S; End; End; End; Var Mx, My, Mz : TMInt; Lx, Cx, Ly, Cy, Lz, Cz : Integer; Begin LerDados ('Entre com a Matriz X', Mx, Lx, Cx); MostrarMat (NEWLINE+'Matriz X', Mx, Lx, Cx); LerDados ('Entre com a Matriz Y', My, Ly, Cy); MostrarMat (NEWLINE+'Matriz Y', Mx, Lx, Cx); ProdMat (Mx, Lx, Cx, My, Ly, Cy, Mz, Lz, Cz); If Lz <> 0 Then MostrarMat (NEWLINE+'Matriz Z', Mz, Lz, Cz) Else Writeln ('Produto impossível'); End. A foi adaptada para permitir que o e possam fazer parte dos elementos da matriz. Assim, o dado de entrada é tratado como , sendo que o final de linha é o texto e o final de matriz é o texto . Não sendo um destes valores, a é convertida para inteiro pela função e o resultado armazenado na matriz. A conversão é feita utilizando a : Function StrToInt (S : String) : Integer; Var X, Code : Integer; Begin Val (S, X, Code); StrToInt := X; End; A lógica do módulo que realiza o produto matricial define inicialmente o produto como impossível através da atribuição . Na sequência testa pela viabilidade da operação através da expressão . Em caso positivo, a lógica do produto é iniciada. Introdução à Programação: Conceitos e Práticas. Página 262 O produto matricial demanda três elemento da matriz resultante , e o acumulada do produto de [ ] sendo o eo para referenciar cada para obter este elemento realizando a soma [ ] Procedure ProdMat (Ma : TMInt; La, Ca : Integer; Mb : TMInt; Lb, Cb : Integer; Var Mc : TMint; Var Lc, Cc : Integer); Var I, J, K, S : Integer; Begin Lc := 0; If Ca = Lb Then Begin Lc := La; Cc := Cb; For I := 1 To Lc Do For J := 1 To Cc Do Begin S := 0; For K := 1 To Ca Do S := S + Ma[I,K]*Mb[K,J]; Mc[I,J] := S; End; End; End; A soma acumulada poderia ser feita diretamente em [ ] mas optou-se por utilizar a variável para melhorar a legigilidade. A eficiência também é melhorada, uma vez que se utiliza uma em vez de uma dentro do loop mais interno. A seguinte interface ilustra uma entrada e saída do programa, sendo que segunda coluna é a sequência da primeira: Entre com a Matriz X {-1} encerra LINHA {-2} encerra MATRIZ M[1, 1] = 1 M[1, 2] = 2 M[1, 3] = 1 M[1, 4] = next M[2, 1] = 2 M[2, 2] = 3 M[2, 3] = 3 M[2, 4] = next M[3, 1] = 1 M[3, 2] = 0 M[3, 3] = 2 M[3, 4] = end Matriz X 1 2 1 2 3 3 1 0 2 Introdução à Programação: Conceitos e Práticas. Entre com a Matriz Y {-1} encerra LINHA {-2} encerra MATRIZ M[1, 1] = 0 M[1, 2] = 5 M[1, 3] = next M[2, 1] = 6 M[2, 2] = 3 M[2, 3] = next M[3, 1] = -3 M[3, 2] = -4 M[3, 3] = end Matriz Y 0 5 6 3 -3 -4 Matriz Z 9 7 9 7 -6 -3 Página 263 v. Escrever um programa que resolva um sistema de equações lineares do tipo. Considerando que o sistema tenha a seguinte representação matricial: , onde: ⋯ ⋯ [ ] [ ] [ ] ⋯ A matriz e o vetor são fornecidos, sendo a solução desejada. O seguinte sistema de equações será utilizado como exemplo para detalhar os passos de um método simples de solução. Pelo método de , as linhas são combinadas através de operações matemáticas que eliminem as parcelas abaixo da diagonal principal, produzindo uma matriz triangular. O consiste em efetuar transformações nas três linhas abaixo da linha de tal forma a eliminar a parcela de nas linhas e . A visualização no formato de expressões pode ser melhorada se passarmos para o formato matricial, justapondo a matriz com o vetor produzindo a [ ]: ⋯ ⋯ ⋯ [ ] Para o caso exemplo, tem-se: [ Fixando o elemento como referência, a Introdução à Programação: Conceitos e Práticas. ] , produz: Página 264 [ A lógica utilizada no Um ] foi: consiste em eliminar os termos abaixo do elemento [ A lógica utilizada no O ] foi: consiste em eliminar os termos abaixo do elemento | A lógica utilizada no . . | foi: Podemos combinar os passos com a lógica do escalonamento, obtendo: Introdução à Programação: Conceitos e Práticas. Página 265 Para completar a lógica, basta detalhar as operações com as linhas, de tal modo a ser estendida a cada elemento. Neste caso, surgiria mais um para codificar a operação . O detalhe pode ser inspecionado no código mais adiante. A próxima etapa, com a matriz já escalonada, é obter os valores de efetuando as devidas substituições de baixo para cima ( ). A linha fornece o valor de , que levado a linha fornece o valor de , e assim sucessivamente: ( ) ( ) Assim, o vetor solução é , e a forma geral da solução é: ∑ ⋯ O seguinte programa implementa as lógicas apresentadas acima: Program ExGAUSS; Const MAXL MAXC MAX NEWLINE = = = = 100; 100; 100; #10; Type TMReal = Array[1..MAXL, 1..MAXC] Of Real; TVReal = Array[1..MAX] Of Real; Introdução à Programação: Conceitos e Práticas. Página 266 Function StrToReal (S : String) : Real; Var Code : Integer; X : Real; Begin Val (S, X, Code); StrToReal := X; End; Procedure LerDados (Msg : String; Var M : TMReal; Var L, C : Integer); Function GetX (Lin, Col : Integer) : String; Var SX : String; Begin Write ('M[',Lin,', ', Col,'] = '); Readln (Sx); GEtX := Sx; End; Var Sx : String; Begin Writeln (Msg); Writeln ('{next} encerra LINHA'); Writeln ('{end} encerra MATRIZ'); L := 0; C := 0; SX := GetX (L+1, C+1); While Sx <> 'end' Do Begin Inc (L); C := 0; If SX = 'next' Then SX := GetX (L, C+1); While (SX <> 'next') AND (SX <> 'end') Do Begin Inc (C); M[L, C] := StrToReal(SX); SX := GetX (L, C+1); End; End; End; Procedure MostrarMat (Msg : String; M : TMReal; L, C : Integer); Var I, J : Integer; Begin Writeln (Msg); For I := 1 To L Do Begin For J := 1 To C Do Write (M[I,J]:9:4); Writeln; End; End; Introdução à Programação: Conceitos e Práticas. Página 267 Procedure MostrarVet (Msg : String; V : TVReal; N : Integer); Var I : Integer; Begin Writeln (Msg); For I := 1 To N Do Writeln ('V[',I,'] =', V[I]:10:5); End; Procedure Gauss (Var A : TMReal; L, C : Integer); Var I, J, K : Integer; T : Real; Begin For I := 1 To L-1 Do For J := I+1 To L Do Begin T := -A[I,I]/A[J,I]; For K := 1 To C Do A[J,K] := T*A[J,K] + A[I,K]; End; End; Procedure BackSubst (A : TMReal; L, C : Integer; Var V : TVReal; Var N : Integer); Var I, J, K : Integer; S : Real; Begin N := L; For I := N DownTo 1 Do Begin S := A[I,C]; For J := I+1 To N Do S := S - A[I,J]*V[J]; V[I] := S/A[I,I]; End; End; Var Mx : TMReal; Lx, Cx, Nx: Integer; Vx : TVReal; Begin LerDados ('Entre com a Matriz X', Mx, Lx, Cx); MostrarMat (NEWLINE+'Matriz X', Mx, Lx, Cx); Gauss (Mx, Lx, Cx); MostrarMat (NEWLINE+'Matriz X Triangular', Mx, Lx, Cx); BackSubst (Mx, Lx, Cx, Vx, Nx); MostrarVet (NEWLINE+'Vetor X', Vx, Nx); End. Introdução à Programação: Conceitos e Práticas. Página 268 A procedure recebe a matriz aumentada, trabalha seu conteúdo pelo método da e produz a matriz triangular. Observar que a lógica do código é equivalente aos passos feitos para o exemplo dado. Uma alteração que reduz a quantidade de cálculos é modificar o para: For I := 1 To L-1 Do For J := I+1 To L Do Begin T := -A[I,I]/A[J,I]; For K := I+1 To C Do A[J,K] := T*A[J,K] + A[I,K]; End; Com isto, os cálculos que zeram os elementos abaixo da diagonal principal não são realizados, pois de fato esta parte da matriz não será manipulada em nenhum momento. Fica assumido que é zero. Para efeito de visualização, foi mantido o que efetua os cálculos para todos os elementos da matriz. A procedure recebe a matriz triangular e calcula as raízes, armazenando o resultado no parâmetro . A seguinte interface ilustra uma entrada e saída do programa: Entre com a Matriz X {next} encerra LINHA {end} encerra MATRIZ M[1, 1] = 1 M[1, 2] = 3 M[1, 3] = 2 M[1, 4] = -4 M[1, 5] = -3 M[1, 6] = next M[2, 1] = 2 M[2, 2] = 1 M[2, 3] = -3 M[2, 4] = 1 M[2, 5] = -1 M[2, 6] = next M[3, 1] = 1 M[3, 2] = 2 M[3, 3] = -5 M[3, 4] = 3 M[3, 5] = 2 M[3, 6] = next M[4, 1] = -2 M[4, 2] = 2 M[4, 3] = 2 M[4, 4] = -1 M[4, 5] = 4 M[4, 6] = end Matriz X 1.0000 2.0000 1.0000 -2.0000 2.0000 -3.0000 -5.0000 2.0000 -4.0000 1.0000 3.0000 -1.0000 -3.0000 -1.0000 2.0000 4.0000 Matriz X Triangular 1.0000 3.0000 2.0000 0.0000 2.5000 3.5000 0.0000 0.0000 -14.0000 0.0000 0.0000 -0.0000 -4.0000 -4.5000 13.0000 -1.5385 -3.0000 -2.5000 10.0000 -6.1538 Vetor X V[1] = V[2] = V[3] = V[4] = 3.0000 1.0000 2.0000 2.0000 1.00000 2.00000 3.00000 4.00000 O método apresentado e sua respectiva implementação tem efeito introdutório e não exaure o tema de resolução de sistemas de equações lineares. Introdução à Programação: Conceitos e Práticas. Página 269 23. Arquivos O tópico anterior abordou o tipo de dado e quase todos os exemplos apresentaram a necessidade de entrada de dados via teclado. Esta situação é bastante comum, pois de alguma forma o deve ser populado com as informações que serão processadas. Porém, ao codificar a lógica de um problema e iniciar os testes é frequente o surgimento de erros. Então, inicia um ciclo de correção e teste que inclui a repetição de toda a entrada dos dados. Este processo torna cansativo o trabalho de depuração do programa. Este cenário se acentua quando a entrada de dados envolve grande volume de informações. A seguinte figura ilustra esta situação: Figura 72: Entrada/Saída de grande volume de dados sem o uso de arquivos A figura traz em destaque um programa para , composto pelas rotinas , e dados. O módulo para gerencia a entrada de dados, fazendo a leitura do valor digitado e o seu armazenando na memória. O módulo efetua alguma manipulação destas informações, enquanto que escreve os resultados na tela. Além da facilidade nos testes, problemas reais quase sempre lidam com situações que determinam a necessidade de armazenar dados em meios permanentes. Neste contexto, o ideal seria que os dados que servem de entrada para algum programa fossem digitados apenas uma vez e guardados em algum meio persistente, ou seja, com capacidade de reter estas informações. O recurso de proporciona esta facilidade, permitindo que dados sejam lidos e gravados em meios de armazenamento permanentes. A seguinte figura ilustra a adaptação da lógica anterior para incluir o uso de arquivos como origem de dados para o programa ou como destino dos dados enviados pelo programa: Introdução à Programação: Conceitos e Práticas. Página 270 Figura 73: Entrada/Saída de grande volume de dados com o uso de arquivos Dentre as várias possibilidades, esta ilustração apresenta uma configuração de módulos, onde a é feita através da leitura de informações prégravadas em . Também destaca, que quando conveniente parte das informações pode ser lida a partir de entrada via teclado (dispositivo de entrada padrão). A pode ser direcionada para ser gravada também em , mantendo quando conveniente a apresentação de informações na tela do computador (dispositivo de saída padrão). Um arquivo é uma entidade associada a uma sequência de bytes cujo armazenamento acontece em um meio com capacidade de retenção. A definição se assemelha a um vetor na memória , porém este sem a capacidade de retenção, pois na falta de energia o conteúdo da memória é perdido. A interpretação do conteúdo de um arquivo é dada pela rotina que fará a manipulação das informações. De forma geral, os arquivos são classificados em e . Em um a sequência de bytes que compõem seu conteúdo é interpretado como sendo números que representam caracteres que no conjunto formam um texto cuja visualização faz sentido a uma pessoa. No a sequência dos bytes que integram seu conteúdo representam informações cujo significado só pode ser interpretado por algum programa específico, ou representam códigos de máquina, que apenas o ou o estão aptos a tratar. O gerenciamento do , , é um das funções mais importantes de um , pois tem a responsabilidade de assegurar o adequado , bem como efetuar as operações típicas sobre arquivo, tais como , , , , e outras. A seguinte figura ilustra a visualização no formato texto para um arquivo que contém um programa fonte escrito em : Introdução à Programação: Conceitos e Práticas. Página 271 Figura 74: Visualização de um arquivo texto como sequência de caracteres. A visualização na tela do conteúdo deste arquivo sob a forma de texto faz sentido, e pode ser observado que se trata de um trecho do código fonte de um programa escrito em . Ou seja, o conteúdo do arquivo quando representado pelo caractere equivalente de cada é compreensível a uma pessoa. A próxima figura ilustra o mesmo processo, porém aplicado a um arquivo executável: Figura 75: Visualização de um arquivo executável como sequência de caracteres. A visualização na tela do conteúdo deste arquivo sob a forma de texto já não faz pleno sentido, mesmo que parte da sequência de bytes tenha equivalente textual coerente. O conteúdo de um arquivo executável representa as instruções e os códigos binários a serem tratados pelo e pelo . Analisando um novo caso, a seguinte figura ilustra um programa que percorre os elementos de um de células, cujo conteúdo varia de a , e os grava em arquivo no mesmo formato em que se encontram na memória (codificado em binário). Introdução à Programação: Conceitos e Práticas. Página 272 Em seguida, este arquivo é visualizado com um editor de textos, que simplesmente apresenta os caracteres equivalentes aos bytes do arquivo. Destacamos que a única informação processada pelo editor de textos são os bytes que indicam uma nova linha. Figura 76: Dado em Memória gravado em arquivo como binário A visualização pelo editor de textos expõe uma série de caracteres que em nada lembram os números do que deram origem ao arquivo. Porém, como sabemos de onde surgiram os dados, vemos que há uma coerência, pois a conversão para complemento de dos dados do array enquadrados em dá origem aos bytes do arquivo. Cabe destacar a sequência invertida dos bytes. Cada quatro bytes formam um número, e a sequência que representa o , dada por , foi armazenada como . Esta característica faz parte do padrão de armazenamento de números da arquitetura , denominada . Uma pequena variação desta abordagem pode ser vista na seguinte figura: Figura 77: Dado em Memória gravado em arquivo como texto Introdução à Programação: Conceitos e Práticas. Página 273 O dado que vem da memória em formato bruto é convertido para o padrão texto. Com as devidas orientações o programa utiliza a conversão adequada, de inteiro para , real para , ou outra conversão aplicável. A informação que vai para o arquivo é a sequência de bytes que representa os códigos do texto a ser gravado. Observar que o módulo de conversão recebe o dado binário e produz a string para o primeiro número. Desta forma, serão gravados em arquivos os bytes em hexadecimal, que representam os caracteres e . Também cabe destacar que após cada número aparece a sequência de dois bytes em hexadecimal , que é fruto da procedure , que adiciona ao final de cada linha gravada o código que representa o fim de linha. A procedure não adiciona estes dois bytes. Esta codificação dá condições para que os editores de texto entendam quando uma informação deve ser escrita em uma nova linha da tela. 23.1. Arquivo Texto: Funções e Procedimentos A linguagem Pascal oferece recursos básicos para manipular arquivos. Inicialmente serão abordados arquivos textos, a fim de substituir a entrada de dados feita via teclado. Desta forma, os problemas que exigem o fornecimento de grande quantidade de informações poderão ter a entrada de dados direcionada para arquivo em vez de teclado. O arquivo é um recurso gerenciado pelo e possui vários atributos, tais como: nome, extensão, pasta onde está armazenado, data de criação, permissões de uso, tipo dentre outros. Para que o programa possa trabalhar com um arquivo, é necessário fazer a ligação entre uma variável própria para isto e o arquivo identificado pelo seu nome completo. A seguinte figura ilustra este contexto: Figura 78: Manipulação de Arquivo por um programa Introdução à Programação: Conceitos e Práticas. Página 274 O uso do arquivo texto será contextualizado através de um exemplo que permitirá a comparação entre a leitura de dados via teclado e a partir de um arquivo texto. Extraindo um trecho de programa implementado anteriormente e isolando apenas a parte que faz entrada e saída de dados, tem-se: Program ExTEXT; Type TVReal = Array[1..100] Of Real; Procedure LerDados (Var V : TVReal; Var N : Integer); Var X : Real; Begin Writeln ('Entre com os dados'); N := 0; Readln (X); While X <> -1 Do Begin Inc (N); V[N] := X; Readln (X); End; End; Procedure MostrarVet (V : TVReal; N : Integer); Var I : Integer; Begin For I := 1 To N Do Writeln ('V[', I:2,'] = ', V[I]:8:3); End; Var Vx Nx : TVReal; : Integer; Begin LerDados (Vx, Nx); {...} MostrarVet (Vx, Nx); End. A procedure faz a leitura de dados do teclado até que o usuário digite o número . Vamos implementar uma versão semelhante, porém buscando os dados de um arquivo texto, cujo nome é e pode ser criado a partir de qualquer editor de textos. Seguem as primeiras e as últimas linhas do arquivo: Introdução à Programação: Conceitos e Práticas. Página 275 A procedure será reescrita para admitir leitura de dados de um arquivo texto. O nome do arquivo será fornecido como parâmetro. Segue a solução para este problema: Program ExTEXT; Type TVReal = Array[1..100] Of Real; Procedure LerDados (FN : String; Var V : TVReal; Var N : Integer); Var X : Real; F : Text; Begin Assign (F, FN); Reset (F); N := 0; Readln (F, X); While X <> -1 Do Begin Inc (N); V[N] := X; Readln (F, X); End; Close (F); End; Procedure MostrarVet (V : TVReal; N : Integer); Var I : Integer; Begin For I := 1 To N Do Writeln ('V[', I:2,'] = ', V[I]:8:3); End; Introdução à Programação: Conceitos e Práticas. Página 276 Var Vx Nx : TVReal; : Integer; Begin LerDados ('notas.txt', Vx, Nx); MostrarVet (Vx, Nx); End. Comparando as duas versões para , tem-se: Leitura do Teclado Leitura de Arquivo Texto Procedure LerDados (Var V : TVReal; Var N : Integer); Procedure LerDados (FN : String; Var V : TVReal; Var N : Integer); Var X : Real; Var X : Real; F : Text; Begin Begin Writeln ('Entre com os dados'); Assign (F, FN); Reset (F); N := 0; Readln (X); While X <> -1 Do Begin Inc (N); V[N] := X; Readln (X); End; N := 0; Readln (F, X); While X <> -1 Do Begin Inc (N); V[N] := X; Readln (F, X); End; Close (F); End; End; As adaptações para alterar a procedure para leitura do arquivo foram: i. Criação de uma variável do tipo arquivo arquivo texto; ii. Chamada da procedure arquivo designado pelo ); {} { } { } { } { } { } para migrar de leitura do teclado que será utilizada para se conectar ao que liga uma variável do tipo arquivo seu nome completo ( Introdução à Programação: Conceitos e Práticas. a um Página 277 iii. Chamada da procedure que reserva o arquivo para leitura dos seus dados; iv. Adaptação das chamadas a para indicar que a leitura deve ser redirecionada do teclado para o arquivo texto associado a uma determinada variável; ⋯ No exemplo, a chamada determina que seja lido do arquivo texto um número Real e armazenado na variável . Um indicador marca o ponto de parada do , de tal forma que o próximo lerá o número após este indicador. v. Liberação do arquivo associado a variável ; Para manter a coerência entre leituras consecutivas o programa possui um indicador que registra o ponto de parada do último ou . Assim que o arquivo é aberto para leitura ( ), este indicador fica posicionado no início do arquivo. Quando é executado um de um dado, a informação é copiada do arquivo para a variável informada, e o indicador é posicionado no ínicio da próxima linha. Neste caso, o uso de é útil quando a partir desta leitura não há mais dados disponíveis na linha. Porém, se a linha contém vários dados, então o correto é efetuar as leituras individuais com , e reservar o apenas para o último dado da linha. É possível ler apenas dados escalares ( )eo a partir dos arquivos textos. Não há como a partir de um único ler todo o conteúdo de um arquivo texto para um vetor, por exemplo. É necessário criar a lógica que faz a leitura de um escalar por vez e o armazena na célula correta do vetor. Podemos agora melhorar a lógica da procedure de leitura para que não seja necessário o valor como indicador de fim dos dados. A função retorna um que informa se o indicador alcançou o final de arquivo ( ). O valor corresponde ao final de arquivo. Assim, o de leitura pode ser executado enquanto o final de arquivo não for alcançado. O valor – pode e deve ser retirado do arquivo texto. Segue a mudança proposta: Leitura de Arquivo Texto Leitura de Arquivo Texto com EOF Procedure LerDados (FN : String; Var V : TVReal; Var N : Integer); Procedure LerDados (FN : String; Var V : TVReal; Var N : Integer); Var X : Real; F : Text; Begin Var X : Real; F : Text; Begin Introdução à Programação: Conceitos e Práticas. Página 278 Assign (F, FN); Reset (F); Assign (F, FN); Reset (F); N := 0; Readln (F, X); While X <> -1 Do Begin Inc (N); V[N] := X; Readln (F, X); End; Close (F); End; N := 0; While Not Eof (F) Do Begin Readln (F, X); Inc (N); V[N] := X; End; Close (F); End; Observar que houve uma simplificação da lógica, pois foi dispensada a leitura antes da repetição. O testa pelo final de arquivo, caso tenha valor a ser lido então libera a entrada para o corpo do . A primeira linha efetua a leitura do dado, em seguida vem o seu tratamento, e retorna para o testando novamente pelo final do arquivo ( ). A função tem o seguinte cabeçalho: A saída para este programa é: V[ 1] V[ 2] V[ 3] V[ 4] V[ 5] . . . V[35] V[36] V[37] V[38] = = = = = 34.300 83.800 15.400 45.800 44.200 = = = = 51.000 45.500 80.200 74.800 Vamos introduzir mais uma adaptação no programa que é redirecionar a saída para em vez de enviar as informações para a tela tenhamos elas em arquivo texto. A será reescrita para incluir esta modificação. Comparando as duas versões para , tem-se: Escrita na Tela Procedure MostrarVet (V : TVReal; N : Integer); Var I : Integer; Introdução à Programação: Conceitos e Práticas. Página 279 Begin For I := 1 To N Do Writeln ('V[', I:2,'] = ', V[I]:8:3); End; Escrita no Arquivo Texto Procedure MostrarVet (FN : String; V : TVReal; N : Integer); Var I : Integer; F : Text; {} Begin Assign (F, FN); { } Rewrite (F); { } For I := 1 To N Do Writeln (F, 'V[', I:2,'] = ', V[I]:8:3); { } { } Close (F); End; As adaptações para alterar a procedure escrita no arquivo estão em destaque e foram: para migrar de escrita na tela para i. Criação de uma variável do tipo texto; que será utilizada para se conectar ao arquivo ii. Chamada da procedure arquivo designado pelo ); iii. Chamada da procedure que reserva o arquivo para gravar dados. Esta foi a diferença mais importante em relação a rotina ; iv. Adaptação das chamadas a para indicar que a gravação deve ser redirecionada da tela para o arquivo texto associado a uma determinada variável; que liga uma variável do tipo arquivo seu nome completo ( a um ⋯ Observe que o conjunto de elementos que estava sendo escrito na tela foi redirecionado para o arquivo, bastando indicar a variável do arquivo como primeiro argumento do . v. Liberação do arquivo associado a variável Introdução à Programação: Conceitos e Práticas. ; Página 280 O programa principal foi adaptado para: Var Vx : TVReal; Nx : Integer; Begin LerDados ('notas.txt', Vx, Nx); MostrarVet ('relat.txt', Vx, Nx); End. Abrindo o arquivo conteúdo: com um editor de textos podemos certificar do seu 23.2. Exemplos i. Adaptar o programa de solução de um sistema de equações lineares para permitir entrada de dados via arquivo texto. Este exercício demonstra a facilidade proporcionada pela entrada de dados a partir de um arquivo texto. Um sistema de dez equações e dez incógnitas será utilizado para testar o programa. Segue o programa proposto: Program ExGAUSS; Const MAXL MAXC MAX = 100; = 100; = 100; Type TMReal = Array[1..MAXL, 1..MAXC] Of Real; TVReal = Array[1..MAX] Of Real; Introdução à Programação: Conceitos e Práticas. Página 281 Function FileExists (FN : String) : Boolean; Var F : File; Begin {$I-} Assign (F,FN); Reset (F); {$I+} FileExists := (IOResult = 0) and (FN <> ''); Close (f); end; Procedure LerDados (FN : String; Var M : TMReal; Var L, C : Integer); Var F : Text; Begin If FileExists (FN) Then Begin L := 0; C := 0; Assign (F, FN); Reset (F); While Not Eof (F) Do Begin Inc (L); C := 0; While (Not Eoln(F)) And (Not Eof(F)) Do Begin Inc (C); Read (F, M[L, C]); End; Readln (F); End; Close (F); End Else Writeln ('Arquivo [',FN,'] não existe'); End; Procedure MostrarMat (FN, Msg : String; M : TMReal; L, C : Integer); Var I, J : Integer; F : Text; Begin Assign (F, FN); If FileExists (FN) Then Append (F) Else Rewrite (F); Writeln (F, Msg); For I := 1 To L Do Begin For J := 1 To C Do Write (F, M[I,J]:9:4); Introdução à Programação: Conceitos e Práticas. Página 282 Writeln (F); End; Writeln (F); Writeln (F); Close (F); End; Procedure MostrarVet (FN, Msg : String; V : TVReal; N : Integer); Var I : Integer; F : Text; Begin Assign (F, FN); If FileExists (FN) Then Append (F) Else Rewrite (F); Writeln (F, Msg); For I := 1 To N Do Writeln (F, 'V[',I,'] =', V[I]:10:5); Close (F); End; Procedure ApagarArq (FN : String); Var F : Text; Begin If FileExists (FN) Then Begin Assign (F, FN); Erase (F); End; End; Procedure Gauss (Var A : TMReal; L, C : Integer); {igual ao implementado anteriormente} End; Procedure BackSubst (A : TMReal; L, C : Integer; Var V : TVReal; Var N : Integer); {igual ao implementado anteriormente} End; Var Mx : TMReal; Lx, Cx, Nx: Integer; Vx : TVReal; Begin LerDados ('matriz10x10.txt', Mx, Lx, Cx); ApagarArq ('relat10x10.txt'); MostrarMat ('relat10x10.txt', 'Matriz X', Mx, Lx, Cx); Introdução à Programação: Conceitos e Práticas. Página 283 Gauss (Mx, Lx, Cx); MostrarMat ('relat10x10.txt', 'Matriz X Triangular', Mx, Lx, Cx); BackSubst (Mx, Lx, Cx, Vx, Nx); MostrarVet ('relat10x10.txt', 'Vetor X', Vx, Nx); End. Este programa traz uma série de recursos úteis a manipulação de arquivos. O arquivo utilizado como teste tem o seguinte conteúdo: O conteúdo representa um sistema , o que proporciona dez linhas, sendo cada linha com onze números, que representam os dez coeficientes mais o termo independente. O organiza o processamento e consiste na seguinte sequência lógica: i. Executar a procedure que carrega o conteúdo do arquivo para a matriz atualizando também as variáveis e com a dimensão da matriz. Caso exista arquivo com o mesmo nome do arquivo a ser utilizado para conter o relatório com a saída do programa, então apague tal arquivo [ ]. Begin LerDados ApagarArq ii. ('matriz10x10.txt', Mx, Lx, Cx); ('relat10x10.txt'); Gravar o conteúdo da matriz no arquivo . A chamada [ ] informa o nome do arquivo de saída, uma linha de cabeçalho e a matriz com os dados. MostrarMat ('relat10x10.txt', 'Matriz X', Mx, Lx, Cx); iii. Triangularizar a matriz informada pelo método de Eliminação de Gauss [ ]. Gravar o conteúdo desta nova matriz no arquivo Introdução à Programação: Conceitos e Práticas. Página 284 destinado a conter o relatório de saída. Esta informação deve ser anexada ao final do arquivo. Gauss (Mx, Lx, Cx); MostrarMat('relat10x10.txt', 'Matriz X Triangular', Mx, Lx, Cx); iv. Obter a solução deste sistema realizando as substituições de baixo para cima e armazenar a solução no vetor [ ]. Gravar o conteúdo deste vetor solução no arquivo destinado a conter o relatório de saída. Esta informação deve ser anexada ao final do arquivo. BackSubst (Mx, Lx, Cx, Vx, Nx); MostrarVet ('relat10x10.txt', 'Vetor X', Vx, Nx); End. A procedure carrega o conteúdo do arquivo de dados para uma variável de memória. Porém a lógica deve funcionar para outras dimensões. Assim a estrutura da ficou: i. Verificar se o arquivo informado existe [ ]. Em caso positivo, iniciar o processamento. Zerar os parâmetros associados com a quantidade de linhas e de colunas da matriz. A função foi implementada. If FileExists (FN) Then Begin L := 0; C := 0; ii. Vincular a variável com o nome do arquivo em arquivo para leitura apenas [ ]. [ ]. Resevar o Assign (F, FN); Reset (F); iii. Iniciar uma repetição que encerra apenas quando for alcançado o fim do arquivo vinculado a variável [ ]. While Not Eof (F) Do Begin iv. Ao entrar na primeira repetição é sinal de que há linhas a serem lidas, então incrementar o contador de linhas e zerar o contador de colunas, antes de entrar na segunda repetição que fará a leitura de cada um dos dados de uma linha. Inc (L); C := 0; v. Iniciar uma segunda repetição, dentro da anterior, que encerra apenas quando for alcançado o . Para não incorrer em falha na última linha, testar Introdução à Programação: Conceitos e Práticas. Página 285 também pelo [ função testa se foi alcançado o fim de linha em um arquivo texto. ]. A While (Not Eoln(F)) And (Not Eof(F)) Do Begin vi. Incrementar a quantidade de colunas e ler para a memória um dado do arquivo. Manter o indicador de leitura posicionado para ler o próximo dado [ [ ] ]. Caso fosse utilizado o o indicador avançaria para o início da próxima linha, desprezando todos os dados restantes. Inc (C); Read (F, M[L, C]); End; vii. Após encerrar a repetição interna reposicione o indicador para o início da próxima linha. Sem esta instrução o indicador ficará estacionado no final da linha anterior, e não permitiria encerrar a repetição [ ]. Readln (F); End; viii. Fim do loop principal. Liberar o Arquivo [ então imprimir mensagem [ [ ] ]. ]. Caso o arquivo não exista adequada na tela Close (F); End Else Writeln ('Arquivo [',FN,'] não existe'); A função i. testa pela existência de um arquivo com o nome informado: Cabeçalho da função. Recebe uma string com um nome de arquivo. Retorna se o referido arquivo existe na pasta indicada. Não havendo indicado o caminho, a pasta onde o executável está sendo processado é usada como referência. Function FileExists (FN : String) : Boolean; Var F : File; Begin ii. Desabilita a diretiva de compilação que verifica erro de [{ }]. Nesta condição o programa não acusará a partir deste ponto. Vincular com [ ] e tentar abrir o arquivo [ ]. {$I-} Assign (F,FN); Reset (F); Introdução à Programação: Conceitos e Práticas. Página 286 iii. Habilita a diretiva de compilação para erro de I/O. Testar o retorno da função que informa o código de erro da última operação de I/O. Um valor zero indica que não houve erro. O nome do arquivo deve ser diferente de texto vazio. Liberar o arquivo. {$I+} FileExists := (IOResult = 0) and (FN <> ''); Close (f); end; A procedure i. elimina o arquivo com o nome informado: Cabeçalho da procedure. Recebe uma string com um nome de arquivo. Procedure ApagarArq Var F : Text; Begin ii. (FN : String); Testar pela existência do arquivo [ ]. Caso exista, então vincular com [ ]. Eliminar o arquivo informado por [ ]. If FileExists (FN) Then Begin Assign (F, FN); Erase (F); End; End; A procedure a) Vincular [ grava no arquivo indicado o conteúdo de uma matriz: [ com ]. Testar pela existência do arquivo ]. Begin Assign (F, FN); If FileExists (FN) b) Caso exista, então reservar o arquivo exclusivamente para escrita de linhas ao seu final [ ]. Caso o arquivo não exista, então criar um arquivo com o nome indicado e reservar para escrita de linhas [ ]. Then Append (F) Else Rewrite (F); c) Escrever uma linha explicativa no arquivo. Percorrer as linhas [ ] e as colunas [ ] da matriz. Escrever cada elemento de uma linha da matriz em sequência no arquivo [ [ ] ] . Após percorrer a linha da matriz, Introdução à Programação: Conceitos e Práticas. Página 287 então grave no arquivo uma nova linha o código de fim de linha [ ], dando início a Writeln (F, Msg); For I := 1 To L Do Begin For J := 1 To C Do Write (F, M[I,J]:9:4); Writeln (F); End; d) Para melhorar a a legibilidade do arquivo são inseridas duas linhas em branco [ ]. Liberar o arquivo [ ]. Writeln (F); Writeln (F); Close (F); End; A procedure grava no arquivo indicado o conteúdo do vetor solução. A lógica desta rotina utiliza recursos já explicados. ii. Elaborar um programa que recebe informações sobre as notas anuais de uma disciplina e gera um relatório com as médias ponderadas. O formato do arquivo para este exercício é dado por: o o o Primeira linha contém os quatro pesos adotados para cada nota; A segunda linha contém o nome da disciplina; Da terceira linha em diante vem vários blocos de cinco linhas com o nome do aluno e as quatro notas. Segue um exemplo de arquivo cujo conteúdo está de acordo com esta especificação. Introdução à Programação: Conceitos e Práticas. Página 288 O relatório esperado para este problema é uma relação de nomes com suas correspondentes notas e média ponderada. Os dados deverão ser carregados do arquivo para a memória, processados, e em seguida os resultados formatados devem ser gravados em arquivo. Assim, a primeira tarefa é escolher uma forma de organizar estes dados na memória. Com os recursos atuais, a estrutura mais adequada para acomodar o núcleo principal das informações é o tipo . Os pesos, os nomes e as notas serão acomodadas em variáveis do tipo . O seguinte diagrama apresenta uma proposta de organização: ARQUIVO MEMÓRIA 0.2 0.2 0.3 0.3 Calculo Adair Pereira 49 34 74 67 Disciplina (Disc) Calculo Adalto Cardoso Pesos (Mp, Lp, Cp) 0.2 0.2 0.3 0.3 97 69 66 74 Calcular Médias Afonso Vargas 49 74 58 100 Alexandre Teixeira 61 83 56 44 Andrea Conti 78 71 26 41 Nomes (Vn, N) Adair Pereira Adalto Cardoso Afonso Vargas Alexandre Teixeira . . . Notas (Mt, Lt, Ct) 49 34 74 67 97 69 66 74 49 74 58 100 61 83 56 44 . . . . . . . . . . . . Médias (Mm, Lm, Cm) xx xx xx xx Figura 79: Organização dos dados da disciplina na memória Introdução à Programação: Conceitos e Práticas. Página 289 A proposta para acomodar os pesos das provas em uma matriz com uma coluna visa facilitar o cálculo da média, pois desta forma a rotina que implementa o produto matricial poderá ser utilizada. Observar que a média de um aluno é a soma do produto das suas notas pelos respectivos pesos. Segue o programa proposto: Program ExMEDIAS; Const MAXL MAXC MAX = 100; = 100; = 100; Type TMReal = Array[1..MAXL, 1..MAXC] Of Real; TVString = Array[1..MAX] Of String; Function FileExists (FN : String) : boolean; {igual ao implementado anteriormente} End; Function PadRight (S : String; N : Integer) : String; Begin While Length(S) < N Do S := S + ' '; PadRight := S; End; Procedure LerDados (FN : String; Var Mp : Var Disc : Var Vnomes : Var Mt : Var F : Text; TMReal; Var Lp, Cp : Integer; String; TVString; Var Nnomes : Integer; TMReal; Var Lt, Ct : Integer); Procedure LerPesos; Begin Lp := 0; Cp := 1; While Not Eoln(F) Do Begin Inc (Lp); Read (F, Mp[Lp, Cp]); End; Readln (F); End; Introdução à Programação: Conceitos e Práticas. Página 290 Procedure LerNomesNotas; Var J : Integer; Begin Lt := 0; Ct := Lp; While Not Eof (F) Do Begin Inc (Lt); Readln (F, Vnomes[Lt]); For J := 1 To Lp Do Readln (F, Mt[Lt, J]); End; Nnomes := Lt; End; Begin If FileExists (FN) Then Begin Assign (F, FN); Reset (F); LerPesos; Readln (F, Disc); LerNomesNotas; Close (F); End Else Writeln ('Arquivo [',FN,'] não existe'); End; Procedure RelatNotas (FN Mp Disc Vnomes Mt Mm Var I, J : Integer; F : Text; : : : : : : String; TMReal; String; TVString; TMReal; TMReal; Procedure Cabecalho; Var J : Integer; Begin Writeln (F, 'Disciplina: ', Disc); Writeln (F); Writeln (F, ' Aluno Introdução à Programação: Conceitos e Práticas. N1 Lp, Cp : Integer; Nnomes : Integer; Lt, Ct : Integer; Lm, Cm : Integer); N2 N3 N4 Media'); Página 291 Write (F, PadRight(' ', 21)); For J := 1 To Ct Do Write (F, '[',Mp[J,1]:4:1,']'); Writeln (F); Writeln (F); End; Begin Assign (F, FN); Rewrite (F); Cabecalho; For I := 1 To Lt Do Begin Write (F, Padright(Vnomes[I],20)); For J := 1 To Ct Do Write (F, Mt[I,J]:6:1); Writeln (F, Mm[I,1]:6:1); End; Close (F); End; Procedure ProdMat (Ma : TMReal; La, Ca : Integer; Mb : TMReal; Lb, Cb : Integer; Var Mc : TMReal; Var Lc, Cc : Integer); Var I, J, K : Integer; S : Real; Begin Lc := 0; If Ca = Lb Then Begin Lc := La; Cc := Cb; For I := 1 To Lc Do For J := 1 To Cc Do Begin S := 0; For K := 1 To Ca Do S := S + Ma[I,K]*Mb[K,J]; Mc[I,J] := S; End; End; End; Var Mt, Mp, Mm : TMReal; Vn : TVString; Lt, Ct, Lp, Cp, Lm, Cm, N : Integer; Disc : String; Introdução à Programação: Conceitos e Práticas. Página 292 Begin LerDados ProdMat ('notas calculo.txt', Mp, Lp, Cp, Disc, Vn, N, Mt, Lt, Ct); (Mt, Lt, Ct, Mp, Lp, Cp, Mm, Lm, Cm); RelatNotas ('relat notas.txt', Mp, Lp, Cp, Disc, Vn, N, Mt, Lt, Ct, Mm, Lm, Cm); End. O programa principal explicita as três principais funcionalidades: Ler os dados do arquivo para a memória; Calcular a média e elaborar o relatório. A procedure é responsável em pegar as informações que estão no arquivo e trazê-las para a memória, acomodando cada informação na estrutura adequada. Os pesos das notas são armazenados em uma matriz com uma coluna e tantas linhas quantos forem os pesos. O título da disciplina será armazenado em uma variável string. Os nomes serão organizados em um vetor de strings, onde cada posição deste vetor conterá o nome do aluno. As notas serão estruturadas em uma matriz cuja quantidade de linhas será igual a quantidade de alunos e a quantidade de colunas será igual a quantidade de pesos. O corpo da procedure ficou com a seguinte lógica: Begin If FileExists (FN) Then Begin Assign (F, FN); Reset (F); LerPesos; Readln (F, Disc); LerNomesNotas; Close (F); End Else Writeln ('Arquivo [',FN,'] não existe'); End; A sequência de passos da consiste em: a) Testar pela existência do arquivo [ ]. Caso exista, então vincular com [ ] e reservar o arquivo para leitura [ ]. Begin If FileExists (FN) Then Begin Assign (F, FN); Reset (F); Introdução à Programação: Conceitos e Práticas. Página 293 b) Com o arquivo devidamente aberto, então proceda a leitura dos pesos: LerPesos; A leitura dos pesos é feita por uma rotina separada a fim de não prejudicar a legibilidade da procedure . Observe que a procedure não está no mesmo nível que , mas dentro do contexto de . Isto permite que os parâmetros e variáveis de sejam visíveis a . Como a lógica da procedure tem baixa probabilidade de ser reutilizada, então esta abordagem permite modularizar o código sem o formalismo da . Esta estratégia deve ser utilizada com critério, a fim de não negligenciar a prática de , que é fundamental para uma boa modularização do código. Procedure LerPesos; Begin Lp := 0; Cp := 1; While Not Eoln(F) Do Begin Inc (Lp); Read (F, Mp[Lp, Cp]); End; Readln (F); End; O número de linhas da matriz é inicializado com e quantidade de colunas com , conforme comentado anteriormente. Em seguida é iniciado um que se encerra quando for encontrado o código de . Para cada repetição é lido um número (peso) e armazenado na posição adequada da matriz de pesos. Ao encerrar o o indicador de posição no arquivo fica posicionado sobre os caracteres de . Para evitar erros nas próximas leituras, este indicador deve avançar para o início da próxima linha. Este avanço é feito pela instrução . c) Efetuar a leitura das disciplina: Readln (F, Disc); Os caracteres entre o e o string indicada. Além disso, os caracteres de indicador é posicionado no início da próxima linha. são lidos para a são saltados, e o d) Efetuar a leitura dos nomes e notas: LerNomesNotas; Introdução à Programação: Conceitos e Práticas. Página 294 A leitura dos nomes e notas também foram feitas em módulo separado em benefício da legibilidade. Procedure LerNomesNotas; Var J : Integer; Begin Lt := 0; Ct := Lp; While Not Eof (F) Do Begin Inc (Lt); Readln (F, Vnomes[Lt]); For J := 1 To Lp Do Readln (F, Mt[Lt, J]); End; Nnomes := Lt; End; O principal se repete até encontrar o fim de arquivo [ ]. Em cada passo do loop é incrementado o contador de alunos [ ] e lida uma linha inteira com o nome do aluno e armazenado no vetor de strings [ [ ] ]. Logo após a leitura do nome do aluno é executada uma repetição para ler cada uma das notas e armazená-las na matriz de notas. Após encerrar o , a quantidade de elementos do vetor de nomes recebe o mesmo valor que a quantidade de linhas da matriz de notas. e) Liberar o arquivo. Se a abertura inicial não foi bem sucedida então é apresentada uma mensagem. Close (F); End Else Writeln ('Arquivo [',FN,'] não existe'); End; Após ter os dados acomodados na memória, é processada a procedure , cuja tarefa é combinar as e os para produzir as . A média de um aluno é dada pela soma dos produtos das notas pelos pesos. Deve ser multiplicada a primeira nota pelo primeiro peso, a segunda nota pelo segundo peso e assim por diante, e ao final somar estas parcelas. No caso em questão a soma dos pesos sempre será , o que dispensa a divisão pela somatória dos pesos. Assim, o cálculo da média é o produto matricial entre notas e pesos. ProdMat (Mt, Lt, Ct, Mp, Lp, Cp, Mm, Lm, Cm); Introdução à Programação: Conceitos e Práticas. Página 295 Concluída a execução do produto matricial, o passo seguinte é elaborar um relatório integrando as informações. A rotina tem como parâmetros todas as informações para compor o relatório. A seguinte chamada é responsável por produzir um arquivo com os dados agregados: RelatNotas ('relat notas.txt', Mp, Lp, Cp, Disc, Vn, N, Mt, Lt, Ct, Mm, Lm, Cm); A sequência de passos da procedure consiste em: a) Vincular com [ ] e criar o arquivo e reservar para gravação [ ]. Caso exista arquivo com este nome ele será recriado e seu conteúdo anterior perdido. Begin Assign (F, FN); Rewrite (F); b) Invocar a procedure que grava linhas de cabeçalho: Cabecalho; c) Para cada aluno [ caracteres [ ] do vetor de nomes gravar o nome ajustado para [ ] ]: For I := 1 To Lt Do Begin Write (F, Padright(Vnomes[I],20)); d) A frente do nome gravar as notas [ notas gravar a média [ [ [ ] ] ] e após as ]: For J := 1 To Ct Do Write (F, Mt[I,J]:6:1); Writeln (F, Mm[I,1]:6:1); End; e) Liberar o arquivo: Close (F); End; Introdução à Programação: Conceitos e Práticas. Página 296 Segue um extrato do relatório produzido para uma entrada de dados típica é: Disciplina: Calculo Aluno Adair Pereira Adalto Cardoso Afonso Vargas Alexandre Teixeira Andrea Conti Antonio Bárbara Carlos Galhardo Carlos Pedreira . . . Josué Ribeiro Justino Roman Leona Mattos Maria dos Santos Mário Valdivino Pedro Antunes Roberto Diniz Romana Peres iii. N1 N2 N3 N4 Media [ 0.2][ 0.2][ 0.3][ 0.3] 49.0 97.0 49.0 61.0 78.0 99.0 65.0 26.0 34.0 69.0 74.0 83.0 71.0 96.0 26.0 53.0 74.0 67.0 66.0 74.0 58.0 100.0 56.0 44.0 26.0 41.0 88.0 38.0 23.0 75.0 66.0 34.0 58.9 75.2 72.0 58.8 49.9 76.8 47.6 45.8 90.0 47.0 24.0 84.0 100.0 61.0 85.0 34.0 44.0 30.0 26.0 28.0 33.0 31.0 74.0 65.0 96.0 87.0 71.0 90.0 62.0 54.0 25.0 30.0 62.5 62.5 45.4 63.8 53.3 42.1 67.5 51.6 23.0 70.0 47.0 48.0 27.0 25.0 94.0 76.0 Elaborar um programa que recebe informações sobre pesos e alturas e gera um relatório com os índices de massa corpórea. O seguinte diagrama ilustra o formato do arquivo de entrada, bem como a saída desejada: O arquivo de entrada contém uma sequência de informações agrupadas a cada duas linhas. A primeira linha se refere ao nome de uma pessoa e a segunda linha possui os dados de peso e altura. O arquivo de saída descreve o formato do relatório e ilustra os processamentos requeridos, tais como: o cálculo do IMC de cada pessoa, bem como a obtenção das médias do peso, altura e o IMC destas médias. Introdução à Programação: Conceitos e Práticas. Página 297 Segue o programa proposto: Program ExIMC; Const MAX = 100; Type TvReal = Array[1..MAX] Of Real; TVString = Array[1..MAX] Of String; Function FileExists (FN : String) : boolean; Var F : File; begin {$I-} Assign (F,FN); Reset (F); {$I+} FileExists := (IoResult = 0) and (FN <> ''); Close (f); end; function getIMC (P : Real; H : Real) : Real; Begin getIMC := P/Sqr(H); End; Function PadRight (S : String; Ch : Char; N : Integer) : String; Begin While Length(S) < N Do S := S + Ch; PadRight := S; End; Function PadLeft (S : String; Ch : Char; N : Integer) : String; Begin While Length(S) < N Do S := Ch + S; PadLeft := S; End; Function Center (S : String; N : Integer) : String; Begin Center := PadLeft (S, ' ', Length(S) + (N - Length(S)) Div 2); End; Procedure LerDados (FN : String; Var CN : TVString; Var CP, CA : TVReal; Var N : Integer); Introdução à Programação: Conceitos e Práticas. Página 298 Var F : Text; Begin If FileExists (FN) Then Begin Assign (F, FN); Reset (F); N := 0; While Not EOF (F) Do Begin Inc (N); Readln (F, CN[N]); Readln (F, CP[N], CA[N]); End; Close (F); End Else Writeln ('Arquivo [',FN,'] não existe'); End; Procedure CalcIMC (CP, CA : TVReal; Var CM : TVReal; N : Integer); Var I : Integer; Begin For I := 1 To N Do CM[I] := GetImc (CP[I], CA[I]); End; Function GetMedia (V : TVReal; N : Integer) : Real; Var I : Integer; S : Real; Begin S := 0; For I := 1 To N Do S := S + V[I]; GetMedia := S/N; End; Procedure Relat (FN : String; CN : TVString; CP, CA, CM : TVReal; N : Integer; PesoM, AltM, ImcM : Real); Var I : Integer; F : Text; Begin Introdução à Programação: Conceitos e Práticas. Página 299 Assign (F, FN); Rewrite (F); Writeln (F, Center ('Relatorio de IMC', 60)); Writeln (F); Writeln (F, ' N Nome Peso Alt IMC') ; For I := 1 To N Do Writeln (F, I:3, ' ', Padright(CN[I],' ', 30), CP[I]:8:2, CA[I]:8:2, CM[I]:8:2); Writeln (F); Writeln (F, PadRight(Center('Medias',34),' ', 34), PesoM:8:2, AltM:8:2, ImcM:8:2); Close (F); End; Var VN VP, VA, VM MedP, MedA, MedM N Begin LerDados CalcIMC MedP := MedA := MedM := : : : : TVString; TVReal; Real; Integer; ('peso-alt.txt', VN, VP, VA, N); (VP, VA, VM, N); GetMedia (VP, N); GetMedia (VA, N); GetIMC (MedP, MedA); Relat ('rimc.txt', VN, VP, VA, VM, N, MedP, MedA, MedM); End. O programa principal explicita as três principais funcionalidades: Ler os dados do arquivo para a memória; Processar os cálculos: Índice de Massa Corpórea e Médias; e elaborar o relatório. As rotinas e as lógicas utilizadas se assemelham ao exemplo anterior para o cálculo das médias aritméticas, ficando a cargo leitor se aprofundar no entendimento. Introdução à Programação: Conceitos e Práticas. Página 300 Segue um extrato do relatório produzido: Relatorio de IMC N 1 2 3 4 5 6 7 . . . 23 24 25 26 27 28 29 30 Nome Adair Pereira Adalto Cardoso Afonso Vargas Alexandre Teixeira Andrea Conti Santos Antonio Barbara Carlos Galhardo Peso 66.90 73.10 85.00 70.90 82.30 55.20 50.50 Alt 1.72 1.54 1.66 1.78 1.53 1.64 1.77 IMC 22.61 30.82 30.85 22.38 35.16 20.52 16.12 Josue Ribeiro Justino Roman Leona Mattos Maria dos Santos Mario Valdivino Pedro Antunes Roberto Diniz Romana Peres 93.90 64.80 62.00 92.00 85.30 56.20 78.40 97.00 1.95 1.68 1.60 1.85 1.78 1.51 1.87 1.94 24.69 22.96 24.22 26.88 26.92 24.65 22.42 25.77 Medias 75.69 1.75 24.70 Introdução à Programação: Conceitos e Práticas. Página 301 24. Conjuntos Em algumas circunstâncias o uso de conjuntos melhora a legibilidade do programa, pois proporciona um mecanismo mais natural para operar sobre coleção ordenada de elementos não repetidos. O Pascal oferece este recurso, porém limitado a conjuntos formados por ordinais ( ). Além disso, são permitidos apenas ordinais cujo tamanho seja de byte e domínio de a . A solução empregando conjuntos é geralmente mais eficiente do que uma lógica equivalente utilizando array. O tipo conjunto tem a seguinte sintaxe: 24.1. Operadores As soluções baseadas em conjunto tendem a ser bastante eficiente, pois a representação na memória para conjuntos geralmente consiste em uma sequência de bits onde o valor significa elemento ausente e o valor elemento presente. São reservados ( ) para a variável , que é a quantidade máxima de elementos permitida. As operações de { } são eficientemente traduzidas para operações lógicas com bits. É importante consultar o documento do compilador para certificar da abordagem utilizada na representação de conjuntos. Além das operações permitidas, a tabela23 também ilustra alguns exemplos. Operador Ação Exemplos Program ExCONJUNTO; União Type TSByte = Set Of Byte; TSChar = Set Of Char; Diferença Var Vogais Digitos Unidades Consoantes Letras M31 : : : : : : TSChar; TSChar; TSByte; TSChar; TSChar; TSByte; 23 O Pascal padrão prevê as operação de União, Diferença, Interseccção e Pertinência. As demais operações fazem parte da extensão provida pela implementação da linguagem. Introdução à Programação: Conceitos e Práticas. Página 302 M30 AlfaNum Interseccção Está Contido Contém Pertinência Begin Vogais Vogais Letras Consoantes M30 M31 Digitos Unidades AlfaNum : TSByte; : TSChar; := := := := := := := := := ['a', 'e', 'i', 'o']; Vogais + ['u']; ['a'..'z']; Letras Vogais; [4, 6, 9, 11]; [1, 3..12] M30; ['0'..'9']; [0..9]; Letras + Digitos; If 'a' in Vogais Then Writeln ('''a'' é vogal'); If Digitos <= AlfaNum Then Writeln ('Digitos contido em AlfaNum'); If AlfaNum >= Digitos Then Writeln ('AlfaNum contém Digitos'); Igual If Digitos <= Digitos Then Writeln ('Digitos contido em Digitos'); If Digitos = Digitos Then Writeln ('Digitos é igual a Digitos'); Diferente If Digitos <> TSChar(Unidades) Then Writeln ('Digitos diferente de Unidades'); End. Os elementos do conjunto são designados listando seus componentes de forma individual ou por faixa de valores separados por vírgulas, e envolvidos pelos símbolos [ ], como segue: ['a', 'e', 'i', 'o'] ['a'..'z'] [4, 6, 9, 11] [1, 3..12] ['0'..'9'] [0..9] Introdução à Programação: Conceitos e Práticas. Página 303 Além das atribuições que ilustram algumas operações sobre conjuntos, uma sequência de complementam este exemplo. Porém, a expressão do último apresenta um detalhe que o distingue dos demais: If Digitos <> TSChar(Unidades) Then Writeln ('Digitos diferente de Unidades'); O nome do tipo envolve a variável expressão fosse escrita da seguinte forma: que é do tipo . Caso a If Digitos <> Unidades Then Writeln ('Digitos diferente de Unidades'); A intenção é testar se os dois conjuntos são diferentes, porém ocorrerá erro de compilação pois há incompatibilidade na comparação de dois conjuntos de elementos de tipos distintos ( e ). Entretanto, a diferença é apenas no nível abstrato, pois no nível concreto de representação de dados as duas variáveis são semelhantes. Assim, para contornar o erro sintático e permitir a comparação se utiliza o conceito de (conversão de tipo), que de fato força que o valor da variável seja interpretado como , ficando assim compatível com a variável . Além dos exemplos que seguem, fica a sugestão de modificar a função para utilizar conjuntos. 24.2. Exemplos i. Elaborar um programa que informa se um determinado caractere é uma consoante, vogal, dígito, alfanumérico, maiúscula, minúscula, .... A solução proposta adota exclusivamente operações com conjuntos. Program ExLOGICA; Function IsLetra (Ch : Char) : Boolean; Begin IsLetra := UpCase(Ch) in ['A'..'Z']; End; Function IsVogal (Ch : Char) : Boolean; Begin IsVogal := UpCase(Ch) in ['A', 'E', 'I', 'O', 'U']; End; Function IsConsoante (Ch : Char) : Boolean; Begin Introdução à Programação: Conceitos e Práticas. Página 304 IsConsoante := UpCase (Ch) in (['A'..'Z'] – ['A', 'E', 'I', 'O', 'U']); End; Function IsDigit (Ch : Char) : Boolean; Begin IsDigit := Ch in ['0'..'9']; End; Function IsLower (Ch : Char) : Boolean; Begin IsLower := Ch in ['a'..'z']; End; Function IsUpper (Ch : Char) : Boolean; Begin IsUpper := Ch in ['A'..'Z']; End; Begin Writeln Writeln Writeln Writeln Writeln Writeln End. (IsConsoante('a')); (IsConsoante('B')); (IsDigit('x')); (IsDigit('3')); (IsVogal('a')); (IsVogal('b')); As soluções codificadas ficaram simples e intuitivas utilizando o operador , que retorna se o elemento representado pelo argumento da esquerda pertence ao conjunto representado pelo argumento a direita. É importante destacar que conjunto se aplica apenas a tipos ordinais, não sendo possível construções como: [ ], ou [ ], pois e não são ordinais. A interface de entrada e saída para o programa é: FALSE TRUE FALSE TRUE TRUE FALSE ii. Elaborar um programa que faça as seguintes operações: a. Relacionar em um conjunto as letras de uma string; b. Compor uma string com as letras de um conjunto; c. Eliminar os caracteres de uma string que pertençam a um conjunto. Introdução à Programação: Conceitos e Práticas. Página 305 Segue uma proposta de solução: Program ExSTRSET; Type TSChar = Set Of Char; Function DelChars (S : String; Chars : TSChar) : String; Var R : String; I : Integer; Begin R := ''; For I := 1 To Length(S) Do If Not (S[I] in Chars) Then R := R + S[I]; DelChars := R; End; Function StrToSet (S : String) : TSChar; Var R : TSChar; I : Integer; Begin R := [ ]; For I := 1 To Length(S) Do R := R + [S[I]]; StrToSet := R; End; Function SetToStr (S : TSChar) : String; Var R : String; C : Char; Begin R := ''; For C := #0 To #255 Do If C in S Then R := R + C; SetToStr := R; End; Begin Writeln (DelChars ('ola mundo belo', ['a', 'o'])); Writeln (SetToStr(StrToSet('ola mundo belo'))); End. A lógica da função que recebe uma string e retorna um conjunto de caracteres está estruturada como segue: a. Inicializar a variável com o conjunto vazio: { Introdução à Programação: Conceitos e Práticas. [] }; Página 306 Function StrToSet (S : String) : TSChar; Var R : TSChar; I : Integer; Begin R := [ ]; b. Percorrer cada caractere da string [ ] e adicionar o caractere ao conjunto temporário { [ [ ]] }. Retornar como resultado da função [ ]. For I := 1 To Length(S) Do R := R + [S[I]]; StrToSet := R; End; Observar que a expressão [ [ ]] equivale a um conjunto com o caractere [ ]. A função monta uma com os elementos de um conjunto de . É importante destacar que conjunto não é array, logo não é possível acessar um elemento do conjunto utilizando indexação. Assim, a solução deve perguntar para cada um dos valores possíveis para um char se ele se encontra na string. Em caso positivo este elemento é adicionado à string resultado. Seguem os detalhes: a. Inicializar a variável string com vazio: { }; Function SetToStr (S : TSChar) : String; Var R : String; C : Char; Begin R := ''; b. Percorrer cada caractere [ ] possível de pertencer a um conjunto de char e perguntar se pertence ao conjunto informado [ ]. Em caso afirmativo, concatenar o caractere à string temporária { }. Retornar como resultado da função [ ]. For C := #0 To #255 Do If C in S Then R := R + C; SetToStr := R; End; Introdução à Programação: Conceitos e Práticas. Página 307 O programa principal, composto pelas seguintes linhas, gerou a saída indicada na sequência: Writeln (DelChars ('ola mundo belo', ['a', 'o'])); Writeln (SetToStr(StrToSet('ola mundo belo'))); <l mund bel> < abdelmnou> A eliminação das letras e da string produziu a . A formação de um conjunto com as letras da produziu o conjunto [ ]. Obsevar que o conjunto não apresenta elementos repetidos e está ordenado. Introdução à Programação: Conceitos e Práticas. Página 308 25. Registro A qualidade de um software depende de vários fatores que combinados indicam o grau de adequação do produto. Conforme a finalidade do sistema determinados fatores apresentam maior ou menor relevância. Alguns fatores de qualidade de software são: legibilidade, confiabilidade, usabilidade, eficiência, modularidade, dentre outros. A engenharia de software se ocupa principalmente de estabelecer um conjunto de técnicas, metodologias e ferramentas que contribuam com a gestão de todo o ciclo de vida de um software. As linguagens de programação, como parte do ambiente de desenvolvimento de software, são elementos fundamentais para transformar especificações e projetos em programas de computador. Neste sentido, as linguagens devem proporcionar mecanismos de abstração que permitam produzir programas alinhados com um processo de engenharia de software. Citamos anteriormente que de forma simplificada a equação ilustra os componentes principais de um projeto de um programa, que deve ter suporte nas linguagens de programação. Também, é forte a presença da componente abstração no ambiente computacional, onde desempenha papel essencial no ciclo de refinamento do problema até a sua materialização em um produto acabado. Desta forma, podemos expressar a seguinte relação: , reforçando a importância não só apenas em organizar o código e os algoritmos visando a qualidade, mas de forma idêntica dedicar esforços para uma boa modelagem dos dados. O exercício que abordou o problema de emitir um relatório de notas e médias de uma disciplina de uma turma de um curso demandou algumas variáveis para acomodar as informações requeridas para a solução. Se estendermos o problema para modelar todo um sistema acadêmico, certamente chegaríamos a uma quantidade intratável de variáveis para acomodar todas as informações que um contexto deste exige. Assim, é fundamental que a linguagem ofereça recursos que proporcione mecanismos de organização de dados, onde seja possível abstrair novos tipos através do agrupamento de informações de naturezas distintas e que tenham relação mais natural com a entidade modelada. O primeiro passo na direção desta melhor organização é o conceito de , onde um conjunto de dados correlacionados podem ser estruturados em um novo tipo. A ampliação do conceito de ou dá origem ao que agrupa dados e rotinas para formarem uma nova entidade mais ampla, cujo agrupamento esconde informações e comportamentos. A extensão do conceito de a fim de permitir um ordenamento hierárquico destes agrupamentos, bem como proporcionar maior reusabilidade de código deu origem a visão . A seguinte figura esboça um diagrama com os recursos de uma linguagem tipo organizados por elementos de abstração: Introdução à Programação: Conceitos e Práticas. Página 309 Figura 80: Abstrações típicas de uma linguagem procedural Introdução à Programação: Conceitos e Práticas. Página 310 Este diagrama destaca que a linguagem proporciona de forma similar recursos voltados tanto para a codificação da lógica do problema quanto para a modelagem dos dados. Assim, o programador deve dedicar atenção e tempo às duas vertentes da abstração. 25.1. Sintaxe As seguintes regras simplificadas24 indicam a sintaxe para uso do tipo { Segue um programa que exemplifica o uso de [ ] : } : Program ExRECORD; Type TPonto = Record X, Y, Z : Real; End; TComplex = Record Re, Im : Real; End; TData = Record D, M, A : Integer; End; TNome = Record First, Middle, Last : String; End; TPessoa = Record Nome : TNome; Nasc : TData; End; Var P, Q Z Pessoa T 24 : : : : TPonto; TComplex; TPessoa; Record H, M, S : LongInt; Não incluída a parte variante. Introdução à Programação: Conceitos e Práticas. Página 311 End; Begin Readln (P.X, P.Y, P.Z); Z.Re := P.X + P.Y; Z.Im := P.Y + P.Z; Q.X := P.X + 1; Q.Y := P.Y + 1; Q.Z := P.Z + 1; Readln (Pessoa.Nome.First); Readln (Pessoa.Nome.Middle); Readln (Pessoa.Nome.Last); Pessoa.Nasc.D := 10; Pessoa.Nasc.M := 8; Pessoa.Nasc.A := 1998; T.H := 17; T.M := 34; T.S := 23; End. Neste exemplo são apresentados diversos registros que agregam informações correlacionadas por algum conceito. Por se caracterizar com um tipo de dado, o programador pode definir uma variável diretamente como ou criar um tipo associado com a nova definição. Esta última abordagem é a preferida por facilitar o reuso e contribuir com a legibilidade. A variável foi criada qualificando o na própria definição do tipo. As demais variáveis ( , , , e ) são de tipos definidos no bloco . O programa principal mostra o uso de variáveis do tipo bem como a forma de acesso aos seus campos. A seguinte figura ilustra a definição do tipo e a criação das variáveis e . TPonto TIPO VARIÁVEIS X Y Z P X Y Z Q X Y Z O Pascal utiliza a notação (ponto) para acesso aos campos. Assim, a célula da variável é referenciada como . Da mesma forma podem ser referenciadas as células , , , e . O seguinte diagrama ilustra o tipo são registros: Introdução à Programação: Conceitos e Práticas. e a variável cujos campos também Página 312 Nasc Nome TPessoa TNome TData D Pessoa M A ... ... ... First Nasc VARIÁVEL Nome TIPO 10 8 1998 J O R L U I S I L D M A G E First Z Middle V A Last Middle Last D M A J O R G E First Middle L U I Z S I L V A Last A variável possui dois campos que também são registros, sendo que o seguinte código tráz referência a estes campos: Readln (Pessoa.Nome.First); Readln (Pessoa.Nome.Middle); Readln (Pessoa.Nome.Last); Pessoa.Nasc.D := 10; Pessoa.Nasc.M := 8; Pessoa.Nasc.A := 1998; É notória a vantagem do uso de registros para organizar e agrupar informações correlacionadas entre si. É importante destacar que o conceito de variável se aplica integralmente aos campos de um registro. A designação de um campo através da descrição completa do caminho que referencia um endereço de memória constitui-se uma variável, e pode ser utilizada da mesma forma, tal como compor expressões e serem passadas como parâmetro. Assim, podemos generalizar o conceito de variável para englobar as seguintes variações: Introdução à Programação: Conceitos e Práticas. Página 313 Tipo de Variáveis Variável normal25 Variável indexada Variável identificadora de campo Exemplos A, X, Soma V[I], M[I,J] Data.Dia, VE[I].Salario Não há restrições para composição entre variáveis possível modelar um , com campo , e , sendo , dentre outras. 25.2. Exemplos i. Refazer o programa que recebe informações sobre as notas anuais de uma disciplina e gera um relatório com as médias ponderadas. Segue o programa proposto: Program ExMEDIAS; Const MAXN MAXNOTAS Type TVReal = 100; = 10; = Array[1..MAXNOTAS] Of Real; TAluno = Record Nome : String; End; TVAluno = Array[1..MAXN] Of TAluno; TNota = Record N : Integer; NtBim : TVReal; Media : Real; End; TVNota = Array[1..MAXN] Of TNota; TDisc = Record Nome : String; N : Integer; Pesos : TVReal; End; TDados = Disc Alunos Notas 25 Record : TDisc; : TVAluno; : TVNota; “Entire Variable” – [WIRTH, 1973] Introdução à Programação: Conceitos e Práticas. Página 314 N End; : Integer; Function FileExists (FN : String) : boolean; {igual ao exemplo anterior} End; Function PadRight (S : String; N : Integer) : String; {igual ao exemplo anterior} End; Procedure LerDados (FN : String; Var Dados : TDados); Var F : Text; Procedure LerPesos; Begin With Dados.Disc Do Begin N := 0; While Not Eoln(F) Do Begin Inc (N); Read (F, Pesos[N]); End; Readln (F); End; End; Procedure LerNomesNotas; Var J : Integer; Begin Dados.N := 0; While Not Eof (F) Do Begin Inc (Dados.N); Readln (F, Dados.Alunos[Dados.N].Nome); For J := 1 To Dados.Disc.N Do Readln (F, Dados.Notas[Dados.N].NtBim[J]); Dados.Notas[Dados.N].N := Dados.Disc.N; End; End; Begin If FileExists (FN) Then Begin Assign (F, FN); Reset (F); Introdução à Programação: Conceitos e Práticas. Página 315 LerPesos; Readln (F, Dados.Disc.Nome); LerNomesNotas; Close (F); End Else Writeln ('Arquivo [',FN,'] não existe'); End; Procedure RelatNotas (FN : String; Dados : TDados); Var I, J : Integer; F : Text; Procedure Cabecalho; Var J : Integer; Begin Writeln (F, 'Disciplina: ', Dados.Disc.Nome); Writeln (F); Writeln (F, ' Aluno N1 N2 N3 Write (F, PadRight(' ', 21)); For J := 1 To Dados.Disc.N Do Write (F, '[',Dados.Disc.Pesos[J]:4:1,']'); Writeln (F); Writeln (F); End; N4 Media'); Begin Assign (F, FN); Rewrite (F); Cabecalho; For I := 1 To Dados.N Do Begin Write (F, Padright(Dados.Alunos[I].Nome,20)); For J := 1 To Dados.Notas[I].N Do Write (F, Dados.Notas[I].NtBim[J]:6:1); Writeln (F, Dados.Notas[I].Media:6:1); End; Close (F); End; Procedure CalcMedia (Var Dados : TDados); Introdução à Programação: Conceitos e Práticas. Página 316 Var I, J : Integer; S : Real; Begin For I := 1 To Dados.N Do Begin S := 0; For J := 1 To Dados.Disc.N Do S := S + Dados.Notas[I].NtBim[J]*Dados.Disc.Pesos[J]; Dados.Notas[I].Media := S; End; End; Var X : TDados; Begin LerDados ('notas calculo.txt', X); CalcMedia (X); RelatNotas ('relat notas rec.txt', X); End. Introdução à Programação: Conceitos e Práticas. Página 317 NOTAÇÃO Record Campo 1 Campo 2 Campo 3 Vetor Vetor[1] Vetor[2] TVNota N NtBim[1] NtBim[2] ... NtBim[N] Media N NtBim[1] NtBim[2] ... NtBim[N] Media N NtBim[1] NtBim[2] ... NtBim[N] Media N NtBim[1] NtBim[2] ... NtBim[N] Media ... N Alunos TDisc Nome N Pesos[1] Pesos[2] ... Pesos[N] TDados Nome N Pesos[1] Pesos[2] ... Pesos[N] Nome Nome Nome ... Nome N NtBim[1] NtBim[2] ... NtBim[N] Media N NtBim[1] NtBim[2] ... NtBim[N] Media Notas TVAluno Nome Nome Nome ... Nome TNota N NtBim[1] NtBim[2] ... NtBim[N] Media N TAluno Nome Disc A seguinte figura ilustra a forma com que as informações foram agrupadas em novos tipos: Vetor[N] Introdução à Programação: Conceitos e Práticas. Página 318 26. Tipos Ordinais Definidos pelo Usuário O tipo ordinal definido pelo usuário é um recurso da linguagem que permite aprimorar a representação de dados através da adição de significado bem como da restrição de domínio para o elemento modelado. A linguagem Pascal oferece estes conceitos através de enumerados e subdomínio. 26.1. Enumerados O tipo também é classificado como e , e é definido como uma relação de valores representados por identificadores. Desta forma, o procedimento normal é criar um tipo enumerado na cláusula associando um nome a uma relação de identificadores, sendo que cada identificador representa um valor que é a ordem do identificador no conjunto de enumerados. Segue um programa que exemplifica o uso de : Program ExENUM; Type TDia = (dom, seg, ter, qua, qui, sex, sab); Const Dias : Array[dom..sab] of string = ('dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sab'); Function DiaToStr (D : TDia) : String; Begin DiaToStr := Dias[D]; End; Function StrToDia (S : String) : TDia; Var d : TDia; Begin StrToDia := TDia (-1); For d := dom To sab Do If Dias[d] = S Then Begin StrToDia := d; Break; End; End; var Dia : TDia; begin Dia := qua; Writeln (DiaToStr(Dia)); Writeln (Ord (StrToDia('ter'))); Introdução à Programação: Conceitos e Práticas. Página 319 Dia := Succ(Dia); Writeln (DiaToStr(Dia)); Dia := Pred(Dia); Writeln (DiaToStr(Dia)); Dia := StrToDia ('qau'); Writeln (Ord(Dia)); end. Neste exemplo são apresentados diversos aspectos associados a enumerados. O problema em questão é a conversão de um valor enumerado representando dia da semana em uma e vice-versa. Os seguintes comentários explicam os principais elementos do programa: a. Criar o tipo que relaciona os dias da semana: Type TDia = (dom, seg, ter, qua, qui, sex, sab); Foram enumerados os dias da semana e associados ao novo tipo . É fundamental compreender que os identificadores não são strings. Não foram utilizadas apóstrofes para delimitar o identificador. Os elementos do enumerados são representados na memória por meio do número vinculado à posição do identificador no enumerado. Assim, o identificador está vinculado ao valor , ao valor e assim por diante. Os valores equivalem ao que retornaria a função para estes componentes. b. Criar a variável que serve de base para a conversão de para : Const Dias : Array[dom..sab] of string = ('dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sab'); A cláusula serve tanto para vinclular constantes com identificadores, quanto para criar e inicializar uma variável. Assim, foi criada a variável como um array de strings com índice do tipo ordinal , sendo o índice do primeiro elemento do vetor e o índice do último elemento. Este serve para mapear um índice para uma . O conteúdo da célula [ ] é a o que materializa a conversão. c. Criar a função que mapeia um valor em uma equivalente: Function DiaToStr (D : TDia) : String; Begin DiaToStr := Dias[D]; End; A função apenas retorna o valor da célula do vetor passado como parâmetro. Introdução à Programação: Conceitos e Práticas. cujo índice é Página 320 d. Criar a função que mapeia um valor em um ordinal equivalente: Function StrToDia (S : String) : TDia; Var d : TDia; Begin StrToDia := TDia (-1); For d := dom To sab Do If Dias[d] = S Then Begin StrToDia := d; Break; End; End; A função implementa o mapeamento de uma contendo três letras que designam o dia da semana para o valor equivalente em . A lógica inicia atribuindo ao resultado da função o valor . Porém, como a atribuição direta não é possível em virtude da incompatibilidade de tipo entre o ( ) e o resultado da função ( ), foi necessário utilizar o para alterar o tipo do valor e permitir que a atribuição [ ] seja aceita. Em seguida é implementada uma repetição que busca no vetor pela informada em . Ao encontrar, o valor da variável de controle contém o índice do elemento procurado, que por construção, é o resultado procurado pela função. Cabe destacar que a variável de controle é do tipo , reforçando a opção que o programador possui de utilizar qualquer ordinal como variável do . e. Criar o programa principal para testar a implementação: var Dia : TDia; begin Dia := qua; Writeln (DiaToStr(Dia)); Writeln (Ord (StrToDia('ter'))); Dia := Succ(Dia); Writeln (DiaToStr(Dia)); Dia := Pred(Dia); Writeln (DiaToStr(Dia)); Dia := StrToDia ('qau'); Writeln (Ord(Dia)); end. O programa principal serve para ilustrar o uso dos recursos disponíveis para o tipo enumerado e para testar as funções codificadas. A linguagem não permite escrever valores enumerados na tela ou em arquivo texto, o que demanda a necessidade de uma função que converta ( ) elementos desta natureza para string, podendo este ser escrito na tela. A legibilidade é um dos efeitos Introdução à Programação: Conceitos e Práticas. Página 321 positivos do uso de enumerados, pois auxilia no entendimento do programa. A atribuição é mais facilmente compreendida do que uma do tipo , e sem perda de eficiência, pois na compilação as duas formas são equivalentes. É muito comum no caso de inteiros expressões do tipo , ou mesmo , que não podem ser estendidos para qualquer ordinal. Neste caso foram utilizadas as funções e que retornam o próximo elemento e o elemento anterior de um valor ordinal, inclusive enumerados. Estas funções se aplicam também para inteiros, como em . O uso de e para o último elemento e o primeiro elemento do enumerado leva a erro de domínio, e dependendo das configurações ajustadas para a compilação a inclusão de código para a detecção deste tipo de erro pode ser desabilitada26. Na linha de código é utilizado um argumento para que não possui equivalente no tipo e cujo resultado esperado é . A linha seguinte confirma esta expectativa. O uso conveniente de enumerados melhora a confiabilidade do programa ao inibir construções que tentem atribuir valores que não pertençam a relação definida para o enumerado. Se na codificação deste problema fosse utilizado inteiro para representar um dia da semana, então uma atribuição do tipo não seria inibida pela compilação, e erros decorrentes desta linha só seriam percebidos na execução do programa. A execução deste programa principal produz a saída indicada na sequência: qua 2 qui qua -1 26.2. Subdomínio O tipo consiste na especialização de um tipo , limitando os dados aceitos a uma faixa contínua de valores e permitindo as mesmas operações válidas para este tipo base. Os seguintes exemplos ilustram a forma de uso: Nestes exemplos, são definidos dois novos tipos, sendo o tipo baseado no tipo , porém limitando o domínio de valores a faixa de , enquanto que o tipo tem como base o tipo e a faixa especificada para . As vantagens 26 Compiladores Borland e Free Pascal permitem incluir {$R+} ou não {$R+} código para verificação de erro de domínio (Range Check Error). Introdução à Programação: Conceitos e Práticas. Página 322 na utilização deste recurso são principalmente legibilidade e confiabilidade (erro de domínio). Segue um programa que exemplifica o uso de : Program ExSUBDOM; Type TDiaMes = 1..31; TMes = 1..12; {$R+} Function IsLeap (A : Integer) : Boolean; Begin IsLeap := (A Mod 4 = 0) And ((A Mod 100 <> 0) Or (A Mod 400 = 0)); End; Function NumberOfDays (M : TMes; A : Integer) : TDiaMes; Begin If M in [1, 3, 5, 7, 8, 10, 12] Then NumberOfDays := 31 Else If M in [4, 6, 9, 11] Then NumberOfDays := 30 Else If (M = 2) Then NumberOfDays := 28 + Ord(IsLeap (A)) Else NumberOfDays := 0; End; Begin Writeln Writeln Writeln Writeln Writeln Writeln End. (NumberOfDays (NumberOfDays (NumberOfDays (NumberOfDays (NumberOfDays (NumberOfDays ( 2, ( 1, ( 2, (12, ( 2, (15, 2000)); 1996)); 1996)); 1600)); 2013)); 2000)); Este programa é uma adaptação da implementação anterior, onde foram incorporados os recursos de e . As variáveis, parâmetros e retorno de função deixaram de ser do tipo genérico e foram redefinidos para os tipos e . A expressão lógica que testa meses de ou dias utiliza operação com conjuntos, como em: [ ]. Introdução à Programação: Conceitos e Práticas. Página 323 Ao compilar este programa com o e com a diretiva de verificação de erro de domínio habilitada { }, são identificados dois erros nas linhas destacadas27. O erro em ocorre pela atribuição de ao resultado da função que está definido como (fora da faixa). Na expressão que invoca a função , o compilador identifica o argumento como sendo incompatível (fora da faixa) com o parâmetro que está definido como . Caso a diretiva de compilação seja definida como { } este mesmo programa irá compilar normalmente. 27 Erros: – subdominio.pas(23,22) Error: range check error while evaluating constants – subdominio.pas(33,28) Error: range check error while evaluating constants Introdução à Programação: Conceitos e Práticas. Página 324 27. Expressões Binárias As expressões binárias são recursos da linguagem que permitem manipular os da representação de um número inteiro. Algumas aplicações que envolvem aquisição de dados, programação em baixo nível para sistemas operacionais, drivers, protocolos de comunicação, criptografia e computação gráfica exigem frequentemente lógicas que operem diretamente sobre os bits de um número. Além disso, determinadas operações matemáticas envolvendo multiplicação ou divisão são mais eficientemente implementadas utilizando operações binárias ( ). Na sequência são apresentados os operadores binários e de deslocamento de bits. 27.1. Operadores Binários Os operadores binários são equivalentes aos operadores lógicos, porém aplicáveis aos da representação de um inteiro. Os operadores e as tabelas verdade são as mesmas apresentadas no , que em resumo são: AND 0 1 OR 0 1 XOR 0 1 0 0 0 0 0 1 0 0 1 1 0 1 1 1 1 1 1 0 A 0 0 1 1 AND B 0 1 0 1 A 0 0 1 1 OR B 0 1 0 1 A 0 0 1 1 XOR B 0 1 0 1 X 0 0 0 1 X 0 1 1 1 NOT 0 1 1 0 NOT A X 0 1 1 0 X 0 1 1 0 Abaixo das tabelas estão indicadas as formas de uso dos operadores , , . O seguinte quadro ilustra uma expressão com o operador e o efeito nos Binário 1 1 1 0 0 1 1 0 0 0 1 1 1 0 1 1 0 0 1 0 0 0 1 0 Introdução à Programação: Conceitos e Práticas. Hexa $E6 $3B $22 e : Decimal 230 59 34 Página 325 A expressão com e produz um resultado em que é a aplicação da operação bit a bit na representação binária de e . Neste caso, considera-se que e foram declarados como sendo do tipo , que equivale a inteiro sem sinal com . Este outro exemplo ilustra a operação que equivale a inteiro com sinal com envolvendo operandos do tipo , : Binário 1 1 1 1 1 1 1 1 0 0 1 1 1 0 1 1 0 0 1 1 1 0 1 0 Hexa $FF $3B $2A Decimal 255 59 42 Os inteiros com sinal são codificados em binário utilizando a notação em . Logo, ao atribuir o valor para a variável a codificação binária resultante é . Uma análise na tabela verdade destaca que o operador é adequado para zerar ( ) determinados bits de uma palavra. O operador é aplicado nos casos em que se deseja ligar ( ) bits de uma palavra. O operador é mais indicado quando é necessário inverter ( ) determinados , enquanto que o operador é útil para inverter todos os bits de uma representação. 27.2. Operadores de Deslocamento de Bits A manipulação de se completa com os operadores que executam a movimentação dos bits para a esquerda ou para a direita. O operador ( ) desloca os bits de um inteiro para a esquerda, acrescentando zeros a direita. O seguinte diagrama ilustra esta operação para o deslocamento de a esquerda em um inteiro de . Tabela 3: Operador Shift Left 0 Introdução à Programação: Conceitos e Práticas. Página 326 O efeito numérico que a operação produz em um inteiro sem sinal é equivalente a operação matemática de multiplicação por , onde é o número de deslocados. Se e , teremos então: 0 O valor de é que equivale ao decimal , ou ao valor de . Os compiladores procuram otimizar o código, e sempre que possível traduzem expressões do tipo por . Um segundo operador é o ( ), que desloca os bits de um inteiro para a direita, acrescentando zeros à esquerda. O seguinte diagrama ilustra esta operação para o deslocamento de a direita em um inteiro de . 0 O efeito numérico que a operação produz em um inteiro sem sinal é equivalente a operação matemática de divisão de inteiro por , onde é o número de bits deslocados. Se e , teremos então: 0 O valor de é que equivale ao decimal , ou ao valor de . Os compiladores procuram otimizar o código, e sempre que possível Introdução à Programação: Conceitos e Práticas. Página 327 traduzem expressões do tipo por perdidos equivalem matematicamente ao valor de . Observar que os . Na sequência serão apresentados exemplos que ilustram as aplicações com operações binárias. 27.3. Exemplos i. Elaborar um programa que efetue as seguintes lógicas28: a. Ligar o da posição de um inteiro . ⋯ b. Zerar o ⋯ ⋯ da posição de um inteiro . ⋯ c. Substituir o ⋯ da posição de um inteiro ⋯ f. Girar os g. Girar os ⋯ ⋯ e. Testar se o ⋯ ⋯ ⋯ d. Inverter o valor do ⋯ ⋯ ⋯ menos significativo de . ⋯ da posição de um inteiro . ⋯ ̅̅̅ ⋯ da posição de um inteiro ⋯ pelo está ligado. ⋯ ⋯ de , posições para a esquerda. de , posições para a direita. Segue o programa proposto: Program ExBITWISE; function dectobin (x, n : longint) : string; Var S : String; begin S := ''; while x <> 0 do begin 28 http://bits.stephan-brumme.com/ Introdução à Programação: Conceitos e Práticas. Página 328 S := chr (ord('0') + x and 1) + S; x := x shr 1; end; while length(s) < n do S := '0' + S; dectobin := S; end; function Var mask begin mask SetBit end; SetBit (x, t : longint) : longint; : longint; := 1 shl t; := x or mask; function ClearBit (x, t : longint) : longint; Var mask : longint; begin mask := 1 shl t; ClearBit := x and (not mask); end; function ModifyBit (x, t, r : longint) : longint; Var mask : longint; begin mask := 1 shl t; ModifyBit := (x and (not mask)) Or ((r shl t) and mask); end; function FlipBit (x, t : longint) : longint; Var mask : longint; begin mask := 1 shl t; FlipBit := x xor mask; end; function isBitSet (x, t : longint) : boolean; begin x := x shr t; isBitSet := (x and 1) = 0; end; function rol (x, t : longint) : longint; var s : longint; begin s := (sizeof (x) shl 3) - t; rol := (x shl t) or (x shr s); Introdução à Programação: Conceitos e Práticas. Página 329 end; function ror (x, t : longint) : longint; var s : longint; begin s := (sizeof (x) shl 3) - t; ror := (x shl s) or (x shr t); end; Var XL : longint; Begin XL := 0; Writeln (dectobin(XL, 32)); XL := SetBit (XL, 3); XL := SetBit (XL, 5); XL := SetBit (XL, 31); Writeln (dectobin (XL, 32)); XL := ClearBit (XL, 5); Writeln (dectobin (XL, 32)); XL := ModifyBit (XL, 5, 1); XL := ModifyBit (XL, 6, 1); Writeln (dectobin (XL, 32)); XL := FlipBit (XL, 31); Writeln (dectobin (XL, 32)); XL := FlipBit (XL, 31); Writeln (dectobin (XL, 32)); XL := rol (XL, 5); Writeln (dectobin (XL, 32)); XL := ror (XL, 5); Writeln (dectobin (XL, 32)); End. A fim de visualizar os efeitos das operações sobre os de um inteiro optou-se em mostrar na tela o equivalente binário da variável utilizada como teste. A função que converte um inteiro para binário foi adaptada em relação a versão já apresentada anteriormente. A seguinte tabela compara as duas versões: Function DecToBin (X : Integer) : String; Var S : String; Begin S := ''; While X <> 0 Do Introdução à Programação: Conceitos e Práticas. function DecToBin (x, n : longint) : string; Var S : String; Begin S := ''; While x <> 0 do Página 330 Begin If X Mod 2 = 0 Then S := '0' + S Else S := '1' + S; X := X Div 2; End; Begin S := chr (ord('0') + x And 1) + S; x := x shr 1; End; While Length(S) < N do S := '0' + S; DecToBin := S; End; DecToBin := S; end; A versão implementada para este exemplo necessita dois parâmetros, sendo o inteiro a ser convertido e a quantidade de dígitos na formatação da resultado. Esta formatação é recomendada, pois facilita a visualização e comparação dos . A primeira diferença que se observa é que esta versão não utiliza e para obter os dígitos binários. A lógica empregada isola o menos significativo através da expressão . A constante deve ser vista na forma representada em binário com tamanho estendido para o mesmo tamanho de . Assim, o resultado desta expressão será ⋯ ou ⋯ . Após o tratamento do menos significativo, a linha atualiza o valor de com o deslocamento de todos os uma posição a direita, cujo efeito é a eliminação do menos significativo. Esta lógica é repetida enquanto o valor de é diferente de zero. A expressão retorna o caractere que se encontra na posição dada por somado ou , que resulta no caractere ou . Em seguida vem a lógica que ajusta a para ficar com comprimento , efetuando a concatenação de zeros à esquerda. Esta construção se assemelha à utilizada na função , e poderia ser codificada como rotina separada. Na sequência são comentadas algumas das funções de manipulação de bits: a. Ligar o da posição de um inteiro . ⋯ ⋯ ⋯ ⋯ Para ligar um determinado bit de um inteiro faz-se uso de uma propriedade do operador em que , para qualquer valor do bit e . Assim, deve ser montada uma máscara que tenha na posição e nas demais. A operação desta máscra com o número dado resulta no bit desejado com valor e os demais inalterados. Segue o código da função: function Var mask begin mask SetBit SetBit (x, t : longint) : longint; : longint; := 1 shl t; := x or mask; Introdução à Programação: Conceitos e Práticas. Página 331 end; A máscara com o bit na posição é obtida efetuando um deslocamento à esquerda de posições no inteiro ⋯ . Como sabemos que o decimal 1 é representado em binário e estendido para equiparar ao tamanho da variável destino , então a operação produzirá a máscara desejada. Em seguida, a expressão resultará em um valor que é um inteiro com todos os bits iguais aos bits de com exceção do bit da posição que será forçosamente . As funções e c. Substituir o ⋯ ⋯ seguem o mesmo princípio. da posição de um inteiro ⋯ pelo bit menos significativo de . ⋯ Neste problema, tem-se um com seus bits ⋯ ⋯ , um com seus bits ⋯ e um número . A combinação destes dados produzirá ⋯ ⋯ , onde o bit de deverá ser substituído pelo bit de . A seguinte rotina codifica esta lógica: function ModifyBit (x, t, r : longint) : longint; Var mask : longint; begin mask := 1 shl t; ModifyBit := (x and (not mask)) Or ((r shl t) and mask); end; O quadro que segue ilustra o passo a passo das principais transformações feitas com os bits bem como as combinações necessárias para produzir o resultado desejado. A leitura do quadro deve se dar de cima para o meio e de baixo para o meio, onde então se posiciona a sequência de bits com o resultado da função. t Introdução à Programação: Conceitos e Práticas. Página 332 f. Girar os de , posições para a esquerda. Esta funcionalidade é muito comum na linguagem da maioria das arquiteturas. Neste problema, tem-se um com seus bits ⋯ e um número que indica quantos bits devem girados para a esquerda. Não há perda de bits, pois os bits que saem pela direita servem de preenchimento pela esquerda. Assim, supondo , então a rotação de posições produziria ⋯ . Ao desmembrar a sequência ⋯ chega-se a duas sequências abaixo indicadas que podem ser obtidas por deslocamento de bits e unidas pela operação para produzir o giro solicitado: ⋯ ⋯ ⋯ Generalizando para quaisquer bits tem-se . Ainda é possível parametrizar o número , uma vez que representa a quantidade de bits do valor a ser rotacionado. A função fornece a quantidade de bytes destinada para , que multiplicado por fornece a quantidade de bits. Esta multiplicação por foi substituída pela operação que matematicamente equivale a multiplicar por . Segue a codificação da função : function rol (x, t : longint) : longint; var s : longint; begin s := (sizeof (x) shl 3) - t; rol := (x shl t) or (x shr s); end; A rotação para a direita tem lógica equivalente. O quadro abaixo apresenta tanto a saída resultante da execução do programa principal quanto os comentários passo a passo sobre as alterações sofridas pelos bits: 00000000000000000000000000000000 10000000000000000000000000101000 10000000000000000000000000001000 10000000000000000000000001101000 00000000000000000000000001101000 10000000000000000000000001101000 00000000000000000000110100010000 10000000000000000000000001101000 Introdução à Programação: Conceitos e Práticas. XL := 0; set bits 3, 5 e 31 clear bit 5 modify bits 5 e 6 para 1 flip bit 31 flip bit 31 rotate left 5 bits rotate right 5 bits Página 333 28. Ponteiros O conceito de ponteiro é bastante simples e está associado a um tipo de dado que permite criar variáveis destinadas a guardar um número cujo significado é um endereço de memória, conforme ilustra a seguinte figura: Variável Endereço X -1 $00409000 Y 32 $00409004 Z -250 $00409008 P $00409004 $0040900C Este diagrama ilustra três variáveis quaisquer , e ocupando determinados endereços de memória, e a variável do tipo ponteiro ocupando o endereço e cuja célula contém o valor que é uma referência a uma posição de memória. Apesar da simplicidade do conceito, ponteiros são essenciais para a construção de estruturas de dados dinâmicas, tais como lista encadeada, árvores e grafos. Variáveis ponteiros são invariavelmente vinculadas a alocação dinâmica, que consiste na reserva e liberação de memória conforme a necessidade do problema. 28.1. Sintaxe A declaração de variáveis ponteiros tem a seguinte sintaxe: O símbolo utilizado em Pascal para denotar ponteiros é a declaração de variável ponteiro: . O seguinte programa ilustra Var X, Y, Z : LongInt; P : ^LongInt; Begin X := -1; Introdução à Programação: Conceitos e Práticas. Página 334 Y := 32; Z := -250; P := @Y; End. Da mesma forma, uma variável de tipo ponteiro tem conteúdo incerto até que se atribua um valor válido. A declaração faz com que seja reservada memória para acomodar única e exclusivamente a variável . Na arquitetura um ponteiro ocupa ou de memória. 28.2. Operação com Ponteiros As operações típicas com variável ponteiro são: atribuição de um endereço de memória e a referência à célula apontada. Variável ponteiro pode ser inicializada com um valor especial, identificado como , que equivale ao zero para inteiros. Assim, o comando atribui um valor conhecido para o que permite maior controle sobre as operações com este ponteiro. No exemplo anterior foi utilizada uma expressão para inicializar com o endereço da variável . O endereço de memória associado com uma variável, procedure ou function pode ser obtido com o operador ou com a função . Assim, a expressão ou retornam o endereço de memória da variável . Agora, a variável , declarada como ponteiro para , contém um endereço válido de células associadas a um . A seguinte figura ilustra a notação comum para mostrar o vínculo de uma variável ponteiro com um endereço de memória. A leitura que se faz é que a variável ponteiro contém o endereço da célula indicada pela seta. Y 32 P Uma variável de tipo ponteiro com conteúdo seguintes notações: é normalmente ilustrada em uma das P P Introdução à Programação: Conceitos e Práticas. Página 335 A segunda operação com ponteiros é o acesso à área apontada cuja denominação é . Esta referência é feita com a variável ponteiro seguida do símbolo , o que produz . Assim, no exemplo anterior a célula associada ao endereço pode ser acessada de duas formas distintas, sendo uma de modo convencional através da variável e outra através de por meio do . P^ 32 LongInt Var P : ^LongInt; P Ponteiro Esta figura reforça o conceito e diferencia a variável do tipo ponteiro ( ) e o conteúdo da área por ela apontada O deferenciamento é do tipo e pode ser entendido como se fosse uma variável escalar normal, também denominada . Assim, podemos completar a tabela apresentada anteriormente, e acrescentar mais esta forma de designar uma célula de memória: Tipo de Variáveis Variável normal Variável indexada Variável identificadora de campo Variável referenciada Exemplos A, X, Soma V[I], M[I,J] Data.Dia, VE[I].Salario P^, Q^.Next^, T[1]^.pnome^ O Pascal padrão considera apenas estas duas operações com ponteiro, atribuição e , porém os dialetos disponíveis, tal como , estendem as possibilidades e incorporam vários dos recursos típicos da . Nos exemplos que seguem serão construídos programas que ilustram várias operações com ponteiro. 28.3. Exemplos i. Adaptar, utilizando ponteiros, as lógicas que calcula a soma dos elementos de um vetor e obtem o maior elemento: Segue o programa proposto: Program ExPTR; Const MAX = 100; Type TVReal = Array[1..MAX] Of Real; TPReal = ^Real; Introdução à Programação: Conceitos e Práticas. Página 336 Procedure LerDados (FN : String; Var V : TVReal; Var N : Integer); {igual aos exemplos anteriores} End; Procedure MostrarVet (V : TVReal; N : Integer); {igual aos exemplos anteriores} End; Function GetMaior (V : TVReal; N : Integer) : Real; Var I : Integer; M, P : TPReal; Begin M := @V[1]; P := @V[2]; While P <= @V[N] Do Begin If P^ > M^ Then M := P; Inc(P); End; GetMaior := M^; End; Function GetSoma (V : TVReal; N : Integer) : Real; Var S : Real; P : TPReal; Begin S := 0; P := @V[1]; While P <= @V[N] Do Begin S := S + P^; Inc(P); End; GetSoma := S; End; Var VR NR Soma Maior : : : : TVReal; Integer; Real; Real; Begin LerDados ('pesos.txt', VR, NR); MostrarVet (VR, NR); Soma := GetSoma (VR, NR); Writeln ('Soma = ', Soma:8:3); Maior := GetMaior (VR, NR); Writeln ('Maior = ', Maior:8:3); Introdução à Programação: Conceitos e Práticas. Página 337 End. A procedure já foi explicada e serve para inicializar um vetor de números reais com dados que estão gravados em arquivo, enquanto que apresenta os dados de um vetor na tela. a) A função de ponteiros. teve sua lógica implementada para fazer o maior uso possível A estrutura da lógica está baseada em uma variável ponteiro utilizada para percorrer cada elemento do vetor. O seguinte diagrama ilustra o uso deste ponteiro. V [1] [2] [3] [4] P [N] [MAX] Inicialmente a variável é declarada como ponteiro para o tipo que por conveniência foi definido um novo tipo . . Observe Function GetSoma (V : TVReal; N : Integer) : Real; Var S : Real; P : TPReal; Begin A variável é utilizada para acumular a soma dos valores de e, portanto inicializada com . A variável ponteiro é inicializada com o endereço do primeiro elemento de [ ] . No momento em que foi executado este programa, é inicializado com o valor decimal , que é o endereço de [ ] S := 0; P := @V[1]; Na sequência a repetição para percorrer os elementos de utiliza o em vez do , para explorar mais o conceito de ponteiro. Assim, a cada ciclo do o conteúdo de é atualizado para receber o endereço do próximo elemento Introdução à Programação: Conceitos e Práticas. Página 338 do vetor. Isto é obtido fazendo um incremento em . Neste caso, o incremento no conteúdo de uma variável ponteiro é entendido como adicionar um número que expressa o tamanho da célula. Desta forma, como foi declarado como ponteiro para , o incremento de significa incremento de uma célula , ou seja de para esta arquitetura. Assim, a repetição é executada até que contenha um endereço além do limite final do vetor . While P <= @V[N] Do Begin S := S + P^; Inc(P); End; GetSoma := S; End; Os valores que a variável assume a cada ciclo são { } A diferença entre dois valores consecutivos é de oito unidades. Antes de fazer avançar para o próximo elemento, o conteúdo da célula por apontada é acumulada em b) A função também teve sua lógica implementada para fazer o maior uso possível de ponteiros. A estrutura da lógica é semelhante a função , onde uma variável ponteiro é utilizada para percorrer cada elemento do vetor e a variável , também ponteiro, vai sendo atualizada com o endereço do maior elemento. Inicialmente as variáveis e M são declaradas como ponteiro para o tipo . Function GetMaior (V : TVReal; N : Integer) : Real; Var I : Integer; M, P : TPReal; A variável ponteiro é utilizada para acomodar o endereço do maior elemento e é inicializada com o endereço do primeiro elemento de V [ ] A variável ponteiro é inicializada com o endereço do segundo elemento de [ ] . Begin M := @V[1]; P := @V[2]; Na sequência a repetição para percorrer os elementos de utiliza a mesma estrutura da função . O tratamento dado a cada elemento no interior do é testar se o conteúdo apontado por é maior que o conteúdo apontado por . Em caso positivo a variável recebe o conteúdo de , que é o endereço do elemento do vetor naquele ciclo . Introdução à Programação: Conceitos e Práticas. Página 339 While P <= @V[N] Do Begin If P^ > M^ Then M := P; Inc(P); End; GetMaior := M^; End; O diagrama apresentado abaixo ilustra o comportamento de ao longo da repetição. Para melhorar a legibilidade foram omitidos os passos para a variável , cujo comportamento é semelhante ao descrito anteriormente, porém iniciando com o endereço do segundo elemento de . A variável vai assumindo, quando necessário, o endereço do maior elemento até aquele momento da execução. Ao encerrar a repetição, o conteúdo de é o endereço do elemento de com o maior valor. A função retorna ao final o valor da célula apontada por . V [1] [2] [3] [4] [5] [6] [7] [8] [9] 75.60 67.20 87.60 56.90 54.30 99.80 101.60 77.40 69.75 ... M [MAX] O quadro abaixo apresenta a saída resultante da execução do programa: V[ 1] V[ 2] V[ 3] V[ 4] V[ 5] V[ 6] V[ 7] V[ 8] V[ 9] Soma Maior = = = = = = = = = = = Introdução à Programação: Conceitos e Práticas. 75.600 67.200 87.600 56.900 54.300 99.800 101.600 77.400 69.750 690.150 101.600 Página 340 ii. Elaborar um programa que procure pelas primeiras ocorrências de um texto em um vetor de e retorne os endereços das que contém o texto procurado e o endereço do primeiro caractere dentro da . O programa deverá ser alimentado a partir de um arquivo texto com uma relação de nomes. O processamento deverá pesquisar por um texto e informar os nomes que o contém e a posição inicial deste texto dentro do nome. As estruturas de dados de apoio criadas para representar este problema são: TPChar = ^Char; l TPString = ^String; l TRPos = Record PS : TPString; l PS PC : TPChar; l PC End; A relação de nomes será armazenada em um estrutura do tipo vetor de [ ] , enquanto que o resultado da pesquisa de um texto neste vetor será armazenado em um com as informações de cada busca bem sucedida [ ] . O que se deseja armazenar para o acerto é um record com o endereço da string ) que contém a palavra pesquisa e o endereço do primeiro caractere que coincide. A figura após o código fonte ilustra um caso exemplo. Segue o programa proposto: Program ExSEARCH; Const MAX = 100; Type TVString = Array[1..MAX] Of String; TPChar = ^Char; TPString = ^String; TRPos = Record PS : TPString; PC : TPChar; End; TVRPos = Array[1..MAX] Of TRPos; Introdução à Programação: Conceitos e Práticas. Página 341 Procedure LoadTXT (FN : String; Var V : TVString; Var N : LongInt); Var F : TEXT; P : TPString; Begin Assign (F, FN); Reset (F); P := @V[1]; N := LongInt(P); While Not Eof(F) Do Begin Readln (F, P^); Inc (P); End; N := (LongInt(P) - N) div sizeof(String); Close (F); End; Procedure Buscar (S : String; V : TVString; N : LongInt; Var VX : TVRPos; Var NX : LongInt); Var I, T : LongInt; Begin NX := 0; For I := 1 To N Do Begin T := Pos (S, V[I]); If T <> 0 Then Begin Inc (NX); VX[NX].PS := @V[I]; VX[NX].PC := @V[I] + T; End; End; End; Procedure Mostrar (VX : TVRPos; NX : LongInt); Var I : LongInt; Begin For I := 1 To NX Do Writeln (VX[I].PS^, ' [', LongInt(VX[I].PC) – LongInt(VX[I].PS),']'); End; Var VS : TVString; VP : TVRPos; NS, NP : LongInt; Begin LoadTXT ('nomes.txt', VS, NS); Buscar ('Santos', VS, NS, VP, NP); Mostrar (VP, NP); Introdução à Programação: Conceitos e Práticas. Página 342 End. O seguinte diagrama ilustra a estrutura de memória para este problema: VS Adair Pereira Adalto Cardoso Afonso Vargas Alexandre Teixeira Andrea Conti Santos Antonio Barbara Carlos Galhardo Carlos Pedreira Cristian Mendes VP PS Deodato Fonseca PC Dirceu Carrera PS Eduarda Valente PC Elisa Andreato PS Emerson Santos Muniz PC Everton Moreira PS Fernanda Madeira PC Fernando Videira George Salgueiro Giovani Carli Inacio Castilho Jose de Oliveira Jose Simoes dos Santos Josue Ribeiro Justino Roman Leona Mattos Maria dos Santos Mario Valdivino Pedro Antunes Roberto Diniz Romana Peres Introdução à Programação: Conceitos e Práticas. Página 343 A variável armazena as linhas contidas no arquivo texto e o vetor é um de , onde cada contém dois campos, sendo um deles ponteiro para uma e o outro campo um ponteiro para o primeiro caractere da palavra pesquisada. A variável é populada pela rotina e a variável junto com as conexões indicadas são tratadas pela rotina . a) A procedure realiza a leitura das informações contidas em um arquivo texto para um vetor de . A lógica clássica foi adaptada para ilustrar o conceito de ponteiros. Inicialmente a variável é declarada como ponteiro para por conveniência foi definido um novo tipo é aberto para leitura. . Observe que . O arquivo texto Procedure LoadTXT (FN : String; Var V : TVString; Var N : LongInt); Var F : TEXT; P : TPString; Begin Assign (F, FN); Reset (F); A variável é iniciada com o endereço da célula [ ] ( [ ]), ou seja, aponta para [ ]. O parâmetro , que conterá a quantidade de linhas lidas, é obtido através de operações com endereços. Inicialmente recebe o endereço contido em , porém convertido para inteiro. Observe o para permitir a atribuição . P := @V[1]; N := LongInt(P); A lógica de leitura dos dados do arquivo para a memória inclui uma repetição enquanto o fim de arquivo não for detectado. É lida uma linha inteira do arquivo para a célula . Em seguida é atualizado para conter o endereço da próxima célula ). While Not Eof(F) Do Begin Readln (F, P^); Inc (P); End; Ao final da leitura de todo o arquivo, a variável conterá o endereço da célula após a última. Neste momento ainda contém o endereço da primeira célula. A quantidade de elementos do array pode ser calculadoa pela diferença entre estes endereços, divido pelo tamanho de cada célula. Em seguida o arquivo é liberado. Introdução à Programação: Conceitos e Práticas. Página 344 N := (LongInt(P) - N) div sizeof(String); Close (F); End; b) A realiza a pesquisa no vetor de nomes pelas ocorrências de um determinado texto e vai preenchendo o vetor de resultados com os endereços relacionados com as linhas que contém o texto pesquisado. O parâmetro sucedida por é iniciado com zero e será incrementado a cada busca bem em [ ]. Procedure Buscar (S : String; V : TVString; N : LongInt; Var VX : TVRPos; Var NX : LongInt); Var I, T : LongInt; Begin NX := 0; É realizada uma repetição para cada linha do vetor de nomes. Em cada ciclo é testado se o texto de referência está contido no nome. Em caso positivo, é incrementado, e os campos de [ ] são preenchidos com o resultado da pesquisa. A atribuição [ ] [ ] armazena no campo o valor do endereço da em [ ], enquanto que [ ] [ ] armazena em o endereço do primeiro caractere com a palavra pesquisada. Este endereço é dado pela soma do endereço do início da acrescido da posição do primeiro caractere do texto coincidente. For I := 1 To N Do Begin T := Pos (S, V[I]); If T <> 0 Then Begin Inc (NX); VX[NX].PS := @V[I]; VX[NX].PC := @V[I] + T; End; End; End; O quadro abaixo apresenta a saída resultante da execução do programa, onde é feita a seguinte pesquisa: : Andrea Conti Santos [14] Emerson Santos Muniz [9] Jose Simoes dos Santos [17] Maria dos Santos [11] Introdução à Programação: Conceitos e Práticas. Página 345 29. Estrutura de dados dinâmica A seção anterior abordou o conceito e algumas aplicações de ponteiros, porém sempre referenciando memória alocada pela linguagem. É importante compreender a estrutura de memória de um programa em execução a fim de entender adequadamente a manipulação de endereços e ponteiros, e consequentemente visualizar o suporte computacional para as estruturas de dados dinâmicas. 29.1. Organização da memória de um programa em execução A compilação de um programa fonte (ver Figura 43: Processo de Tradução) produz um módulo objeto com o seu conteúdo refletindo em forma binária as instruções codificadas em linguagem de alto nível. O módulo objeto e demais módulos da biblioteca são ligados para produzir o módulo executável final. Enfim, é possível processar este arquivo executável no ambiente computacional gerenciado pelo . Neste momento o arquivo executável é interpretado pelo e carregado para a memória onde adquire uma nova estrutura, a saber: a) As variáveis globais são agrupadas em uma área de memória denominada . De forma geral neste segmento residem as variáveis estáticas, aquelas que têm o seu espaço de memória reservado no momento da carga do executável e ficam ligadas a esta memória até o fim do programa. b) Os bytes que representam as instruções codificadas ficam residentes em uma área designada como . São estes que fluem para a para serem interpretados e processados formando assim o ciclo de execução das instruções. c) Uma terceira região de memória, denominada , é destinada à suportar as chamadas de sub-programas (funções e procedures) acomodando de forma temporária os dados necessários a execução destas rotinas, tais como: parâmetros, endereço de retorno, variáveis locais (semi-dinâmicas), resultado da função, link estático e link dinâmico. Este agrupamento de dados é denominado de , e cada chamada de rotina demanda o empilhamento de um . Assim, uma variável local fica ligada a um espaço de memória apenas enquanto a rotina na qual ela foi declarada estiver ativa. d) O programa dispõe ainda, de uma área extra de memória, chamada , destinada a alocação dinâmica de dados, onde o momento de reserva de espaço de memória e o momento de liberação deste espaço são definidos pelo programador através de comandos explícitos para tais fins. O quadro a seguir ilustra um programa exemplo bem como suas partes distribuídas pelos diversos segmentos de memória. A primeira coluna apresenta os endereços de memória de uma compilação em . Pelos endereços iniciais e finais é Introdução à Programação: Conceitos e Práticas. Página 346 possível obter a quantidade de bytes ocupada pelo código binário: , o que equivale a . Program ExSEG; Type TVInt = Array[1..1000] Of Integer; $004013e0 Procedure LerDados (V : TVInt; N : Integer); Var I, J : Integer; Begin { Segmento de Dados TPVInt = ^TVInt; } $00401416 End; $00401417 $00401420 Procedure MostrarDados (S : String; PV : TPVInt; N : Integer); Var T : String; Segmento de Código ... Segmento de Pilha I, J : Integer; Begin { ... } $00401440 End; $00401441 Var PX : TPVInt; Heap NX : Integer; $00401450 Begin {...} New (PX); $00401479 LerDados ( PX^ , NX); $00401488 MostrarDados ('teste', PX, NX); $0040148f Dispose (PX); {...} $004014a0 End. $004014a1 As cores indicam onde cada parte do programa irá residir na memória durante a execução. A parte não código das residirão no Introdução à Programação: Conceitos e Práticas. Página 347 pelo tempo que durar a execução do módulo. Neste exemplo, há apenas duas chamadas de procedimentos no programa principal, a ativação de e , e foram destacados os para onde deve retornar o fluxo do programa quando encerrar a execução dos módulos. Estes endereços são armazenados no no momento de cada chamada. As variáveis globais e são de natureza estática e irão residir no segmento de dados. A variável é do tipo ponteiro e ocupará de memória na arquitetura , o que é suficiente para armazenar um endereço de memória. A definição de , quando devidamente inicializado deverá apontar para o início de um vetor de inteiros. [ ] 29.2. Alocação dinâmica A consiste na requisição de memória em tempo de execução de forma explícita pelo programador através da inclusão de uma linha de código com a instrução própria para este fim. De forma semelhante, a liberação desta memória alocada deverá se dar por ordem explícita do programador. A alocação de memória em é feita pela seguinte procedure: O parâmetro é do tipo ponteiro e a forma de passagem é por referência, o que significa que no momento da chamada o argumento indica o local onde deverá ser armazenado o endereço da memória alocada. No exemplo anterior a linha de código faz com que o reserve uma área de memória para um , suficiente para acomodar células do tipo , sendo que o endereço do ínicio deste bloco é armazenado em . Inicialmente, antes da alocação o conteúdo da variável é algo incerto e sem vinculação com área de memória válida. Nesta situação o correto é atribuir o valor á , oferecendo assim uma possibilidade para que o código adiante possa testar o conteúdo da variável ponteiro antes de efetivar o uso da área apontada . O seguinte diagrama ilustra um mapa de memória em dois momentos distintos, sendo o primeiro logo após a criação de , porém sem inicialização, e o segundo após a atribuição do valor à : Introdução à Programação: Conceitos e Práticas. Página 348 VARIÁVEL NÃO INICIALIZADA PX VARIÁVEL INICIALIZADA COM NIL ??? PX O valor inicial de é indefinido e o local apontado é incerto, e consequentemente o uso de deve acarretar em erro de execução. O segundo cenário indica que foi inicializado com . Esta próxima ilustração apresenta o cenário após a alocação de memória com , onde o processamento da rotina efetiva a reserva de tantos bytes quantos forem necessários para acomodar células do tipo eo endereço inicial deste bloco é atribuído ao argumento . Assim, podemos afirmar que contém o endereço do vetor e tem todas as prerrogativas de um vetor de inteiros. HEAP PX^ PX Assim, caso o programador queira inicializar as células alocadas em zero, poderá fazêlo da seguinte forma: [ ] A liberação da área alocada é feita chamando a seguinte procedure: O parâmetro é do tipo ponteiro e a forma de passagem é por cópia-valor. Geralmente o argumento passado é o mesmo utilizado para a procedure . De fato é necessário Introdução à Programação: Conceitos e Práticas. Página 349 fornecer o endereço alocado e com esta informação o gerenciador da executará a liberação. Neste exemplo, a liberação poderá ser feita com . A partir deste momento a utilização da área apontada por é inválida podendo acarretar erro de execução. É recomendado que após a liberação a variável ponteiro seja reinicializada com . 29.3. Exemplos i. Elaborar um programa para manipular uma lista encadeada: A é uma estrutura de dados que pode ser utilizada para organização de dados. O problema de referência será carregar de um arquivo texto para a memória uma relação de nomes de pessoas e executar algum tipo de processamento. Até o momento o mecanismo mais adequado para receber estes dados seria uma variável do tipo de . Supondo que o seguinte arquivo terá seu conteúdo carregado para uma lista encadeada do tipo invertida: ARQUIVO Adair Pereira Adalto Cardoso . . . Roberto Diniz Romana Peres O seguinte diagrama ilustra como a lista encadeada invertida será estruturada na memória: MEMÓRIA MEMÓRIA - HEAP Romana... Roberto... Adalto... Adair... Head Introdução à Programação: Conceitos e Práticas. Página 350 Os principais elementos da lista são a ( ) e os . A cabeça da lista é uma variável do tipo ponteiro que deverá conter o endereço do primeiro nodo. No caso de uma lista sem nodo o conteúdo de será . Os nodos são todos do mesmo tipo e consistem de um registro com pelo menos dois campos, sendo um reservado para o dado que se deseja armazenar e o outro nodo um ponteiro para uma estrutura do mesmo tipo do nodo. As seguintes definições de tipo e de variáveis proporcionam o suporte inicial necessário para estruturar uma lista encadeada invertida: Program ExLISTA; Type TPNodo TNodo Dado Next End; = = : : ^TNodo; Record String; TPNodo; Var Head : TPNodo; Begin Head := Nil; End. O registro representa o modelo de cada nodo e é composto pelo campo do tipo e pelo campo do tipo . O tipo representa um ponteiro para o tipo . O campo dado foi definido para conter o nome da pessoa e o campo next apontará para o próximo elemento da lista. Neste exemplo será adotada a onde o dado mais recentemente adicionado é inserido na primeira posição. De outra forma teríamos uma . A variável é inicializada com indicando lista vazia. Na sequência são descritos os passos para criar e inserir um nodo na lista. Inicialmente a lista está vazia: PASSO 0 H Introdução à Programação: Conceitos e Práticas. Página 351 O próximo passo é inserir o primeiro nodo na lista. Será utilizada uma variável ponteiro , do tipo para auxiliar no processo de montagem da lista: i. É criado um nodo na receber a informação desejada ( e vinculado a . O campo . PASSO 1.1 T já pode 1 New (T); 2 T^.Next := H; 3 H := T; 1 Adair... H ii. A inserção deste nodo na lista se dá fazendo com que o próximo de seja o atual primeiro (onde aponta). PASSO 1.2 T 1 New (T); 2 T^.Next := H; 3 H := T; 1 Adair... H iii. 2 Em seguida o nodo criado torna-se cabeça da lista ( PASSO 1.3 T ). 1 New (T); 2 T^.Next := H; 3 H := T; 1 Adair... H 3 Introdução à Programação: Conceitos e Práticas. 2 Página 352 Para fixar o conceito, seguem os passos para inserir o segundo nodo na lista: i. É criado um nodo na receber a informação desejada ( PASSO 2.1 e vinculado a . O campo já pode . 1 Adalto... 1 New (T); 2 T^.Next := H; 3 H := T; T Adair... H ii. A inserção deste nodo na lista se dá fazendo com que o próximo de seja o atual primeiro (onde aponta). PASSO 2.2 1 Adalto... T 1 New (T); 2 T^.Next := H; 3 H := T; 2 Adair... H iii. Em seguida o nodo criado torna-se cabeça da lista ( PASSO 2.3 1 Adalto... T ). 1 New (T); 2 T^.Next := H; 3 H := T; 2 3 Adair... H Introdução à Programação: Conceitos e Práticas. Página 353 De modo semelhante os demais nodos são criados e inseridos na lista. As informações atribuídas ao campo são oriundas de um arquivo texto contendo a relação de nomes. Procedure Load (FN : String; Var H : TPNodo); Var F : Text; T : TPNodo; Begin Assign (F, FN); Reset (F); While Not Eof(F) Do Begin New (T); Readln (F, T^.Dado); T^.Next := H; H := T; End; Close (F); End; Para percorrer uma lista encadeada e utilizar o dado armazenado no nodo também é utilizada uma variável ponteiro auxiliar . Inicialmente faz apontar para onde aponta o cabeça da lista ( ). Em seguida é iniciada uma repetição que a cada ciclo faz com que avance e aponte para o próximo elemento ( ). A repetição é executada enquanto o valor for diferente de , indicando assim que alcançou o final da lista. A seguinte figura ilustra uma lista encadeada e destaca a variável se deslocando pelos nodos a cada ciclo do . T Romana... Roberto... Adalto... Adair... H A seguinte função conta a quantidade de nodos de uma lista encadeada: Function ContaNodos (H : TPNodo) : Integer; Var C : Integer; T : TPNodo; Begin C := 0; Introdução à Programação: Conceitos e Práticas. Página 354 T := H; While T <> Nil Do Begin Inc (C); T := T^.Next; End; ContaNodos := C; End; Segue o programa proposto com várias operações com uma lista encadeada: Program ExLISTA; Type TPNodo TNodo Dado Next End; = = : : ^TNodo; Record String; TPNodo; Procedure Load (FN : String; Var H : TPNodo); Var F : Text; T : TPNodo; Begin Assign (F, FN); Reset (F); While Not Eof(F) Do Begin New (T); Readln (F, T^.Dado); T^.Next := H; H := T; End; Close (F); End; Procedure Relat (FN : String; H : TPNodo); Var F : Text; T : TPNodo; Begin Assign (F, FN); Rewrite (F); T := H; While T <> Nil Do Begin Writeln (F, T^.Dado); T := T^.Next; Introdução à Programação: Conceitos e Práticas. Página 355 End; Close (F); End; Function ContaNodos (H : TPNodo) : Integer; Var C : Integer; T : TPNodo; Begin C := 0; T := H; While T <> Nil Do Begin Inc (C); T := T^.Next; End; ContaNodos := C; End; Procedure Liberar (Var H : TPNodo); Var T : TPNodo; Begin While H <> Nil Do Begin T := H; H := H^.Next; Dispose (T); End; End; Procedure LiberarFirst (Var H : TPNodo); Var T : TPNodo; Begin If H <> Nil Then Begin T := H; H := H^.Next; Dispose (T); End; End; Procedure EliminarNodos (var H : TPNodo; Nome : String); Var T, Q : TPNodo; Begin While (H <> Nil) And (Pos(Nome, H^.Dado) <> 0) Do LiberarFirst (H); Introdução à Programação: Conceitos e Práticas. Página 356 Q := H; T := H; While T <> Nil Do Begin If Pos(Nome, T^.Dado) <> 0 Then Begin Q^.Next := T^.Next; Dispose (T); T := Q^.Next; End; Q := T; T := T^.Next; End; End; Procedure Add (var H : TPNodo; Nome : String); Var Novo, T, Q : TPNodo; Begin New (Novo); Novo^.Dado := Nome; Q := H; T := H; While (T <> Nil) And (T^.Dado > Nome) Do Begin Q := T; T := T^.Next; End; If Q = T Then Begin Novo^.Next := H^.Next; H := Novo; End Else Begin Novo^.Next := Q^.Next; Q^.Next := Novo; End; End; Var Head : TPNodo; Begin Head := Nil; Load ('nomes.txt', Head); Introdução à Programação: Conceitos e Práticas. Página 357 Relat ('nomes-lista.txt', Head); Writeln (ContaNodos (Head)); EliminarNodos (Head, 'Romana'); Writeln (ContaNodos (Head)); EliminarNodos (Head, 'Santos'); Writeln (ContaNodos (Head)); Relat ('nomes-lista2.txt', Head); Add (Head, 'Aabrao da Silva'); Writeln (ContaNodos (Head)); Relat ('nomes-lista3.txt', Head); Liberar (Head); Writeln (ContaNodos (Head)); End. A procedure já foi explicada e serve para criar a lista encadeada invertida a partir dos dados contidos em um arquivo texto. A procedure gera um relatório a partir dos dados contidos nos nodos, permitindo que seja verificada a correção da lógica de montagem. O módulo retira da lista e libera a memória alocada dos nodos cujos dados contenham um determinado texto. A rotina insere um novo nodo na posição que mantenha a ordem alfabética. Neste caso supõe-se que a lista foi montada em ordem alfabética descendente. A procedure desaloca todos os nodos da lista. Introdução à Programação: Conceitos e Práticas. Página 358 30. Interpretador da Linha Comando O interpretador de comando ou de um sistema operacional é um programa que proporciona interação entre o usuário e o ambiente computacional. Antes do surgimento das interfaces mais elaboradas, notadamente as interfaces gráficas, a era o único meio de execução de comandos. Os comandos típicos incluem: i. ii. iii. iv. gerenciamento de pastas ou diretórios (consultar, criar, renomear, apagar, copiar); gerenciamento de arquivos; controle de processos (consultar, executar e ); manipulação de data e hora (consultar e alterar), dentre outros. Além do comando é necessário conhecer os parâmetros de execução, o que na maioria dos casos dificulta o uso dos recursos disponíveis no sistema operacional. O conhecimento das funcionalidades proporcionadas por um interpretador de comandos é fundamental para profissionais responsáveis pela administração de serviços e recursos computacionais. Principalmente nos sistemas ou as são verdadeiros ambientes de programação que disponibilizam poderosas linguagens interpretadas que permitem o desenvolvimento de programas ( ) com as mais variadas lógicas para execução de comandos. O surgimento e a disseminação de interfaces gráficas reduziu drasticamente a necessidade do uso do interpretador de comandos para tarefas básicas. O seguinte comando lista o conteúdo de uma pasta ou diretório no ambiente : $ pwd /home/arquivos $ ls –l *.txt drwxr--r-1 arquivos -rw-r--r-1 arquivos -r-xr-xr-x 1 arquivos O editors editors fred 4096 30405 8460 dados.txt pessoas.txt notas.txt comando admite vários parâmetros via linha de comando, como – – – – – – – - , que orientam a forma de apresentar o conteúdo da pasta. Ao utilizar uma interface gráfica, o usuário navega facilmente pelas pastas a um simples toque no mouse ou na tela, e muitas vezes não tem noção que as informações apresentadas foram obtidas através de chamadas ás funções do sistema operacional. Assim, é importante conhecer o mecanismo da linguagem que permite capturar os argumentos passados através da linha de comando. O original não fornece este recurso, porém descendentes do incoporaram as funções e que permitem acessar os argumentos passados via linha de comando no momento que o programa foi executado. Introdução à Programação: Conceitos e Práticas. Página 359 A função retorna a quantidade de argumentos da linha de commando. Caso só tenha sido executado o programa em si, sem argumentos, o resultado é zero. A função retorna o argumento da linha de comando. O valor de deve estar ser . O valor de retorna a identificação completa do arquivo executado incluindo o caminho e o nome. O seguinte exemplo ilustra a utilização deste recurso: Var I : Integer; Begin Writeln ('Linha de Comando processada: [', ParamStr(0),']'); Writeln ('Quantidade de argumentos : [', ParamCount,']'); For I := 1 To ParamCount Do Writeln (ParamStr(I)); End. Na sequência o resultado para a execução indicada. C:\dados\programa>linhadecomando -a -d 100 *.txt ola mundo belo Linha de Comando processada: [C:\dados\programa\linhadecomando.exe] Quantidade de argumentos : [7] -a -d 100 *.txt ola mundo belo O espaço em branco é interpretado como separador de argumentos. O arquivo executado foi o com os argumentos . Introdução à Programação: Conceitos e Práticas. Página 360 31. Recursividade O estilo de programação determina a aparência de um programa. A linguagem pertence a família das linguagens imperativas e provê bons mecanismos para a programação estruturada. A solução de um problema conduz geralmente a uma organização do programa que combina as construções de controle clássicas, agrupadas em procedures e functions, executando operações sobre estruturas de dados. Porém, há uma forma diferenciada de representação de problemas que resulta em programas com estilo bem específico, denominada . 31.1. Conceito A programação recursiva é um estilo de resolução de problemas que consiste na descrição da lógica em dois passos, sendo um passo elementar e direto e um segundo passo mais abrangente que generaliza a lógica invocando a si mesmo até reduzir o problema à chamada do passo elementar. O poder desta técnica influenciou fortemente os paradigmas funcional ( e )e lógico ( ) onde a forma principal de resolução de problemas é pelo uso da lógica recursiva. Um exemplo clássico de recursividade é o cálculo do fatorial de do problema pode ser feita da seguinte forma: { , onde a declaração } Reescrevendo esta definição tem-se: Observamos que aparece no lado esquerdo e direito do segundo passo, o que caracteriza a recursão. O programa que implementa esta lógica é: Program ExFATORIAL; Function Begin If N = Then Else End; Fatorial (N : Integer) : Real; 0 Fatorial := 1 Fatorial := N * Fatorial (N-1); Var K : Integer; Begin Introdução à Programação: Conceitos e Práticas. Página 361 For K := 0 To 30 Do Writeln (K:3, ' ', Fatorial(K):1:0); End. A seguinte figura ilustra a sequência das chamadas recursivas para o cálculo do fatorial de : Fatorial (5) 5*Fatorial (4) 5*4*3*2*1*1 4*3*2*1*1 4*Fatorial (3) 3*2*1*1 3*Fatorial (2) 2*1*1 2*Fatorial (1) 1*Fatorial (0) 1*1 1 As chamadas recursivas acumulam uma sequência de contextos abertos que começam a ser resolvidos e fechados quando encontram uma definição básica não recursiva. A figura anterior ilustra a execução de forma descendente e o fechamento dos contextos de forma ascendente, iniciando com . A partir deste ponto, os resultados vão retornando às chamadas abertas, resolvendo o fatorial, e assim sucessivamente até chegar à chamada inicial com Na sequência o resultado para a execução indicada. 0 1 2 3 4 5 . . . 25 26 27 28 29 30 1 1 2 6 24 120 15511210043331000000000000 403291461126606000000000000 10888869450418400000000000000 304888344611714000000000000000 8841761993739700000000000000000 265252859812191000000000000000000 Introdução à Programação: Conceitos e Práticas. Página 362 31.2. Exemplos i. Elaborar um programa que: a. retorna a posição da primeira ocorrência de b. busca pela última ocorrência de em . A busca recursiva por um em um vetor em um vetor ; não ordenado pode ser definido como: [ ] [ ] { } Em síntese a declaração deste problema estabelece que a busca será feita em um determinado intervalo de elementos ( a ), comparando o valor de com o primeiro elemento deste invervalo. Caso este primeiro elemento não seja igual a , então é feita uma nova busca em um intervalo que exclua o antigo primeiro elemento ( a ). O elemento não pertencerá ao vetor quando após uma sucessão de estreitamentos do intervalo de busca chega-se ao ponto que o índice do primeiro elemento do intervalo supera o índice do último elemento. A busca pela última ocorrência de em é feita de modo semelhante, porém comparando-se ao o último elemento do intervalo e o estreitamento ocorre no índice do último elemento ( a ). { [ ] [ ] } O programa que implementa está lógica é: Program ExBUSCAR; Const MAX = 100; Type TVInt = Array[1..MAX] Of Integer; Procedure FillVetSeq (Var V : TVInt; N : Integer); Var I : Integer; Begin For I := 1 To N Do V[I] := I; End; Function BuscarDir (V : TVInt; I, N, X : Integer) : Integer; Begin If I > N Then Introdução à Programação: Conceitos e Práticas. Página 363 BuscarDir If V[I] = X BuscarDir Else BuscarDir End; := 0 Else Then := I := BuscarDir (V, I+1, N, X); Function BuscarRev (V : TVInt; I, N, X : Integer) : Integer; Begin If I > N Then BuscarRev := 0 Else If V[N] = X Then BuscarRev := N Else BuscarRev := BuscarRev (V, I, N-1, X); End; Var VX : TVInt; P : Integer; Begin FillVetSeq (VX, 30); P := BuscarDir (VX, 1, 30, 5); Writeln (P); P := BuscarRev (VX, 1, 30, 25); Writeln (P); End. É notório o poder da técnica de recursividade tanto na descrição do problema quanto na implementação do programa. Notadamente, a solução é compacta, porém com relevante conteúdo computacional. Além disso, pode ser constatado que o código produzido é bastante semelhante à descrição do problema. O programa principal ilustra duas chamadas. Antes porém, o vetor é preenchido com valores de a através da procedure . Em seguida são chamadas as funções recursivas para a busca direta e a busca reversa. Neste caso, os resultados apresentados serão para e para . ii. Elaborar um programa que forneça os termos de uma série de . A série ou sequência de é uma sucessão de números, onde os dois primeiros elementos são e 1 e os demais são obtidos pela soma dos dois valores anteriores. Assim, podemos enumerar os seguintes termos: ⋯ Vários eventos da natureza podem ser modelados através da série de , tais como o crescimento das pétalas de determinadas flores, espiral de conchas, população de coelhos, dentre outros. A definição recursiva para os termos desta série é dada por: Introdução à Programação: Conceitos e Práticas. Página 364 { } O programa que implementa esta lógica é: Program ExFIBO; Function Fib (N : LongInt) : LongInt; begin if N = 0 Then Fib := 0 Else if N = 1 Then Fib := 1 Else Fib := Fib(N-1) + Fib(N-2); End; Var I : LongInt; Begin For I := 0 To 30 Do Writeln (Fib (I)); End. Esta solução não é a mais adequada devido a sua pouca eficiência, pois os cálculos feitos para não são aproveitados para . Segue uma versão mais eficiente: Program ExFIBO; function Begin If N = Then Else Fib2(N, P0, P1 : LongInt) : LongInt; 1 Fib2 := p1 Fib2 := Fib2(N-1, P1, P0 + p1); End; Function Begin If N = Then Else End; Fib (N : LongInt) : LongInt; 0 Fib := 0 Fib := Fib2 (N, 0, 1); Var I : LongInt; Begin For I := 0 To 30 Do Writeln (Fib (I)); End. Introdução à Programação: Conceitos e Práticas. Página 365 Na sequência o resultado para a execução indicada. 0 1 1 2 3 5 8 13 21 34 55 . . . 75025 121393 196418 317811 514229 832040 iii. Elaborar um programa para: a. calcular a soma dos elementos de um vetor; b. contar a quantidade de vezes que um determinado valor aparece em um vetor. A soma dos elementos de um vetor pode ser descrita de forma recursiva. O passo primitivo é para o caso do vetor não possuir elemento algum, resultando em soma igual a zero. O passo geral estabelece que a soma dos elementos do vetor é igual à adição do primeiro elemento com a soma dos demais elementos. { } [ ] A quantidade de vezes que um elemento aparece em um vetor descrita na forma recursiva, conforme segue: { Introdução à Programação: Conceitos e Práticas. [ ] [ ] também pode ser } Página 366 O programa que implementa esta lógica é: Program ExSOMAR; Const MAX = 100; Type TVInt = Array[1..MAX] Of Integer; Procedure FillVetSeq (Var V : TVInt; N : Integer); Var I : Integer; Begin For I := 1 To N Do V[I] := I mod 10; End; Function Begin If N = Then Else End; Somar (V : TVInt; N : Integer) : Integer; 0 Somar := 0 Somar := V[N] + Somar (V,N-1); Function Contar (V : TVInt; N, X : Integer) : Integer; Begin If N = 0 Then Contar := 0 Else If V[N] = X Then Contar := 1 + Contar (V, N-1, X) Else Contar := Contar (V, N-1, X); End; Var VX : TVInt; C, S : Integer; Begin FillVetSeq (VX, 30); S := Somar (VX, 30); C := Contar (VX, 30, 5); Writeln (S); Writeln (C); End. Introdução à Programação: Conceitos e Práticas. Página 367 32. Referências Bibliográficas I. Wirth, Niklaus. The Programming Language Pascal: Revised Report. Eidgenössische Technische Hochschule. 1973. Modo de acesso: World Wide Web: . II. GHEMAWAT, Pankaj (Adaptado do Livro de). A estratégia e o cenário de negócios – Caso 1: Intel Corporation 1968-1997, Editora Bookman, 1999. III. Programa Educar: Curso para professores de 1ª a 4ª série do Ensino Fundamental. Sistemas de Numeração. ICMC/USP. Modo de acesso: World Wide Web: . IV. Fonseca Filho, Cléuzio. História da computação [recurso eletrônico]: O Caminho do Pensamento e da Tecnologia. Porto Alegre. EDIPUCRS, 2007. 205 p. Modo de acesso: World Wide Web: V. "The ENIAC Story". Modo de acesso: World Wide Web: . Acessado em 03-02-2014. VI. "History of Computer Printers". Modo de acesso: World Wide Web: Acessado em 03-02-2014. VII. Intel Corporation. Intel® 64 and IA-32 Architectures Software Developer’s Manual. Volume 1: Basic Architecture. Order Number: 253665-043US. May 2012. VIII. Lancharro, Eduardo Alcalde. Informatica Basica. MAKRON BOOKS ISBN: 0074605100. IX. Meirelles, Fernando S. Informática: microcomputadores. Makron Books. Novas aplicações com X. Barroso, L. C. et al., Cálculo Numérico com Aplicações, 2 ed. Ed. HARBRA, São Paulo, 1987. ISBN:8529400895. XI. Wirth, Niklaus. Algoritmos e estruturas de dados. Rio de Janeiro: LTC - Livros Técnicos e Científicos, 1989. 255p. ISBN 8521611900. XII. Wirth, Niklaus. Programação Sistemática. Editora Campus. 1978. XIII. Ralston, Anthony; Neill, Hugh. Algorithms (Teach Yourself). Editora McGrawHill. 1997. 194p. Introdução à Programação: Conceitos e Práticas. Página 368 ÍNDICE REMISSIVO A ábaco, 10, 12, 28 ábaco chinês, 10 Ada, 14 Álgebra de Boole, 15 Anticítera, 10, 11 Assembly, 20, 21, 23 B Blaise Pascal, 12 Ch Charles Babbage, 14 C Colossus, 15 Comando de Atribuição, 92 D decimal, 27, 29, 31, 32, 35, 36, 43, 63, 64, 139, 169, 171, 194, 195, 196, 312, 317, 323 Decimal Codificado em Binário, 15 E ENIAC, 16 Enigma, 15 Entrada de Dados Read, 110 Readln, 110 Expressões Numéricas, 93 Introdução à Programação: Conceitos e Práticas. F FORTRAN, 20, 21, 83 Função Argumentos, 115 Cabeçalho, 115 Chamada, 115 Forma Geral, 113 Passagem de Parâmetros, 115 Principais Elementos, 113 Funções Matemáticas Abs, 103 ArcTan, 104 Cos, 104 , 105 , 106 Int, 106 Ln, 106 PI, 105 Round, 107 Sin, 105 Sqr, 106 Sqrt, 107 , 108 Funções Textuais Chr, 141 Copy, 143 Length, 140 Ord, 142 Pos, 142 Str, 144 UpCase, 141 Val, 144 G George Boole, 15 Página 369 , 32 Precedência de Operadores, 103 Progressão Geométrica, 36 H Hollerith, 15 R I relés, 17 Representação de Dados, 55 Números Inteiros, 56 Inteiro com Sinal Módulo Sinal, 56 J S Jacquard, 13 John Napier, 11 Saída de Dados Write, 111 Writeln, 111 , 105, 120, 122 sistema de numeração, 27, 28, 29, 31, 32, 33, 34 Sistema de Numeração binário, 20, 31, 33, 35, 36, 37, 39, 43, 46, 56, 57, 58, 59, 61, 62, 63, 65, 67, 68, 75, 99, 101, 195, 196, 257, 258, 259, 311, 315, 316, 317, 332 decimal, 32 hexadecimal, 32, 46, 97, 137, 138, 169, 196, 259 L Leibniz, 13 N Napier, 11, 12 Números Inteiros Inteiro Com Sinal, 56 T O tear, 13 Tipos Inteiros Byte, 93 Int64, 93 Integer, 93 LongInt, 93 LongWord, 93 QWord, 93 ShortInt, 93 SmallInt, 93 Word, 93 Tipos Reais Comp, 94 Double, 94 Extended, 94 Real, 94 Single, 94 Turing, 15 Operadores Lógicos AND, 148 NOT, 149 OR, 149 XOR, 149 Operadores Matematicos *, 102 /, 102 +, 102 Div, 102 Mod, 102 Operadores Relacionais <, 151 <=, 151 <>, 151 =, 150 >, 150 >=, 150 V P válvulas, 17 Passagem de Parâmetros Referência, 123 Valor, 123 , 34, 35 posicional, 27, 31, 34 Introdução à Programação: Conceitos e Práticas. W Wilhelm Schickard, 11 Página 370
Documentos relacionados
solução
write('Da 5 volores inteiros:');
while b<>5 do
(* ≠ nao e permittido *)
begin
read(e);
if (e mod 3) = 0 then
begin
c := c + 1 ;
if d
[java] Sequencia de escape,[ras.] A experiência da ALMG com o uso
Chegando com a Palestra já iniciada, apresento em seguida um breve resumo das estratégias adotadas pela direção de informática da ALMG, que reduziu em 35% os gastos com atualização de softwares. A ...
Leia mais