Prévia do material em texto
ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA Prof. Roque Maitino Neto ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I Marília/SP 2023 “A Faculdade Católica Paulista tem por missão exercer uma ação integrada de suas atividades educacionais, visando à geração, sistematização e disseminação do conhecimento, para formar profissionais empreendedores que promovam a transformação e o desenvolvimento social, econômico e cultural da comunidade em que está inserida. Missão da Faculdade Católica Paulista Av. Cristo Rei, 305 - Banzato, CEP 17515-200 Marília - São Paulo. www.uca.edu.br Nenhuma parte desta publicação poderá ser reproduzida por qualquer meio ou forma sem autorização. Todos os gráficos, tabelas e elementos são creditados à autoria, salvo quando indicada a referência, sendo de inteira responsabilidade da autoria a emissão de conceitos. Diretor Geral | Valdir Carrenho Junior ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 5 SUMÁRIO CAPÍTULO 01 CAPÍTULO 02 CAPÍTULO 03 CAPÍTULO 04 CAPÍTULO 05 CAPÍTULO 06 CAPÍTULO 07 CAPÍTULO 08 CAPÍTULO 09 CAPÍTULO 10 CAPÍTULO 11 CAPÍTULO 12 09 19 29 38 48 58 70 76 84 93 101 110 INTRODUÇÃO A ALGORITMOS E LÓGICA DE PROGRAMAÇÃO VARIÁVEIS, CONSTANTES E TIPOS DE DADOS EXPRESSÕES ARITMÉTICAS, LÓGICAS E RELACIONAIS INTRODUÇÃO À ALGORITMOS ESTRUTURADOS FORMAS DE REPRESENTAÇÕES DOS ALGORITMOS ESTRUTURAS CONDICIONAIS ESTRUTURA DE REPETIÇÃO COM TESTE NO INÍCIO ESTRUTURA DE REPETIÇÃO COM TESTE NO FIM ESTRUTURA DE REPETIÇÃO COM NÚMERO DEFINIDO DE REPETIÇÕES ESTRUTURA DE DADOS HOMOGÊNEA UNIDIMENSIONAL: VETORES ESTRUTURA DE DADOS HOMOGÊNEA BIDIMENSIONAL: MATRIZES ESTRUTURA DE DADOS HETEROGÊNEA: REGISTROS ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 6 SUMÁRIO CAPÍTULO 13 CAPÍTULO 14 CAPÍTULO 15 119 125 134 CONCEITO E UTILIZAÇÃO DE STRINGS MODULARIZAÇÃO PARÂMETROS E ARGUMENTOS EM SUBPROGRAMAS ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 7 INTRODUÇÃO No vasto universo da ciência da computação, os algoritmos servem como a espinha dorsal que sustenta o desenvolvimento de soluções para os mais diversos tipos de problemas. Eles são a chave para as mudanças que temos presenciado em nosso cotidiano e o domínio de conceitos e práticas relacionados a eles certamente abrem as portas para o sucesso profissional nesta área. Este material foi planejado e criado para ser um guia que oferecerá a você uma sólida compreensão dos fundamentos essenciais da criação de algoritmos através das ferramentas oferecidas pela programação estruturada. Neste sentido, cada capítulo proporcionará uma imersão progressiva em aspectos distintos destas ferramentas, proporcionando uma base sólida para quem dá os passos iniciais nos algoritmos. Nos dois primeiros capítulos mergulharemos na essência dos algoritmos, explorando seu conceito fundamental e suas diversas aplicações. Um breve histórico dos algoritmos nos permitirá compreender a evolução dessa disciplina, enquanto uma introdução às variáveis estabelecerá a base para a manipulação de dados ao longo do material. Ainda nesta parte inicial, expandiremos nosso entendimento sobre as variáveis, ao abordarmos tipos de dados, assim como as operações de entrada e saída que constituem a interação vital entre o programa e o usuário. Este será um passo crucial para a criação de algoritmos dinâmicos e interativos. No terceiro capítulo, aprofundaremos nosso conhecimento em expressões aritméticas, lógicas e relacionais, essenciais para a manipulação e avaliação de dados que atuarão em nossos códigos. Esses conceitos formarão a base para a construção de algoritmos mais complexos e eficientes. Os capítulos subsequentes abordarão tópicos cruciais da programação estruturada, desde estruturas sequenciais e condicionais até formas distintas de representação de algoritmos. Estruturas condicionais e de repetição são discutidas detalhadamente, o que fornecerá a você as ferramentas necessárias para criar programas robustos e flexíveis. Nos capítulos 10, 11 e 12 adentramos nas estruturas de dados homogêneas e heterogêneas, e teremos oportunidade de explorar conceitos e aplicações de vetores, matrizes e registros, enquanto o capítulo treze introduz o fascinante mundo das strings. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 8 A modularização, discutida no capítulo quatorze, é um conceito fundamental para a criação de programas mais legíveis, reutilizáveis e fáceis de manter. Finalmente, o capítulo quinze aborda a importância dos parâmetros e argumentos em subprogramas, revelando técnicas fundamentais para criar código modular e eficiente. Este material, enfim, é endereçado a estudantes que, como você, estão dando seus primeiros passos na criação de algoritmos. Cada capítulo é estruturado para oferecer uma experiência de aprendizado progressiva, construindo uma sólida base para o entendimento prático da criação de algoritmos. Sua jornada está prestes a começar e, para bem cumpri-la, seu esforço e perseverança serão decisivos. Bons estudos! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 9 CAPÍTULO 1 INTRODUÇÃO A ALGORITMOS E LÓGICA DE PROGRAMAÇÃO Aqui começa nossa jornada rumo aos saberes e habilidades do que seguramente podemos entender como um dos pilares da Computação moderna. O conhecimento das estruturas e da lógica próprias de um algoritmo certamente abrirá caminhos para seu progresso na Tecnologia da Informação e garantirá bom alicerce para a construção do seu sucesso profissional. Para que você se sinta estimulado a prosseguir, cuidamos para que o início do nosso estudo seja repleto de descobertas e de fatos interessantes a respeito dos algoritmos. Por isso, neste primeiro encontro você terá a oportunidade de fazer contato com conceitos e utilizações do nosso objeto de estudo, com alguns de seus elementos históricos e, por fim, com os componentes mais básicos de sua estrutura. Sigamos adiante! 1.1 Conceito de Algoritmo Não dá para negar que os algoritmos desempenham papel fundamental em nosso mundo e nosso estilo de vida cada vez mais dependente do meio digital. Eles constituem a própria essência da resolução de problemas, da tomada de decisões e de muitos aspectos do nosso cotidiano que que seriam dificultados – para dizer o mínimo – sem a atuação deles. Apenas como exercício, tente imaginar como seria uma simples operação bancária sem o controle exercido por um algoritmo. Com tamanha utilidade, não é de se estranhar que os algoritmos sejam quase onipresentes em nossa vida. Antes de termos contato com a complexidade das suas aplicações, vale a pena investirmos tempo na correta conceituação de algoritmos. De forma simples e prática, um algoritmo é uma sequência de passos finitos e ordenados, executados com a finalidade de resolver um problema. Como você certamente já imaginou, há uma categoria específica de problemas que podem ser resolvidos pelos algoritmos. Não se pode atribuir a eles, portanto, a missão de resolver um problema existencial ou comportamental, por exemplo. Embora o tratamento deste assunto venha na sequência, é necessário registrar que as tarefas que um algoritmo ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 10 pode resolver são aquelas que podem ser formuladas por meio de uma sequência lógica e precisa de comandos. Para que possa estar apto a desempenhar sua função, um algoritmo deve obedecer a algumas regras e a primeira delas está relacionada a sua finitude. Embora esta premissa possa parecer bem óbvia, a sequência de passos deve ter início e fim definidos. Neste sentido, um bom exemplo vem de problemas cujoresultado compreende uma sequência numérica potencialmente infinita. Uma condição de parada deve ser adotada de tal modo que, ao ser satisfeita, o processamento do algoritmo seja interrompido. A segunda regra estabelece que um algoritmo não deve ser ambíguo, ou seja, que as instruções não devem ser passíveis de interpretação e/ou de geração de dúvidas. Duas situações da vida cotidiana esclarecerão esta ideia: 1. Imagine que você está diante de um problema cuja resolução inclua o abastecimento de um veículo, de modo que, após o procedimento, o indicador aponte que exatamente metade do tanque está preenchido com combustível. Neste caso, você não deve se dirigir ao atendente do posto pedindo a ele que “coloque meio tanque de combustível”, simplesmente porque ele não sabe quanto combustível há no tanque. Ao invés disso, um comando preciso e livre de interpretação deve ser dado. Neste comando, a quantidade exata de combustível (em litros) seja apontada. 2. Imagine agora uma receita de bolo que contenha a instrução “adicione farinha a gosto”. Neste caso, a quantidade de farinha a ser adicionada não está especificada, deixando espaço para interpretações variadas. O ponto ideal de farinha pode ser (e certamente será!) diferente para pessoas diferentes, o que levará a resultados inconsistentes e imprevisíveis relacionados ao bolo. Neste caso, a instrução “incluir 200g de farinha” constituirá uma instrução objetiva e sem margem para dúvida. Nos algoritmos que você aprenderá a construir, a clareza e a precisão dos comandos serão cruciais. Neste sentido, cada passo da resolução deve ser estabelecido de forma inequívoca, a fim de garantir que o algoritmo funcione corretamente e produza resultados previsíveis. A ambiguidade em algoritmos pode levar a erros de interpretação e, potencialmente, a consequências indesejadas. Por isso, a não ambiguidade é uma característica fundamental dos algoritmos eficazes. Por fim, a terceira regra está relacionada a entrada de dados. Um algoritmo deverá dar ao seu usuário a possibilidade de inserção de dados para que sejam processados. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 11 Além disso, para quaisquer entradas de um certo conjunto de dados, o algoritmo deverá ser capaz de fornecer o resultado esperado. Um pouco abstrato, não é mesmo? Um exemplo servirá para esclarecer o caso: imagine um algoritmo que realiza a soma de dois valores inteiros e fornece outro inteiro como resultado. Usando o conceito de variáveis – que logo você aprenderá a usar – podemos expressar assim o processamento envolvido: a + b = c Para quaisquer valores de a e para quaisquer valores de b, o algoritmo deverá ser capaz de realizar a soma e fornecer o resultado esperado. Não se pode admitir que o algoritmo seja capaz, por exemplo, de calcular o resultado para os valores 3 e 4, mas não seja capaz de fazer o mesmo para 5 e 6. Conforme já mencionado, um algoritmo serve para resolver um problema que possa ser estruturado em uma sequência de passos, o que possibilita a qualquer pessoa (ou equipamento computacional) seguir as instruções e obter o resultado desejado. Os algoritmos, portanto, podem ser encontrados em todas as áreas da ciência da computação, matemática, engenharia e em vários aspectos da nossa vida cotidiana. A próxima seção trata das principais aplicações modernas dos algoritmos. 1.2 Principais aplicações dos Algoritmos Optamos por apresentar essa seção em itens, a fim de que você possa compreender por completo a caracterização das aplicações mais interessantes e úteis dos algoritmos, desde as mais técnicas até as mais corriqueiras. Temos certeza de que você encontrará nesta lista uma ou mais aplicações que fazem parte do seu cotidiano. 1. Ordenação e Pesquisa: algoritmos de ordenação são usados para organizar dados em ordem crescente ou decrescente. Já os algoritmos de pesquisa são utilizados para encontrar itens em listas ordenadas de maneira eficiente. Como exemplo prático de utilização destes algoritmos podemos idealizar a seguinte situação: um gerente de agência bancária poderá querer ordenar a lista de seus clientes em ordem de saldo para fins de concessão de taxas especiais de juros e, em ação posterior, pesquisar todos os clientes que residem em uma determinada rua da cidade. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 12 2. Aprendizado de máquina: algoritmos de aprendizado de máquina são usados para treinar modelos que podem fazer previsões, classificações e tomadas de decisão com base em dados. 3. Processamento de imagem e de vídeo: sistemas modernos de monitoramento vão muito além da mera geração de um vídeo de uma certa localidade. Algoritmos de reconhecimento facial, de detecção de presença humana e de classificação de objetos conferem a estes sistemas capacidades de observação até então exclusivas dos seres humanos. 4. Segurança da Informação: algoritmos criptográficos garantem a segurança de dados sensíveis, como nossas senhas de cartão de crédito, quando eles são transmitidos e armazenados. Um conjunto de regras matemáticas – expressas em um algoritmo – realiza a transformação de um texto que um humano pode compreender para um texto cifrado. Há, no entanto, uma ampla gama de aplicações de algoritmos que transcendem o contexto mais técnico da Computação e se acomodam em nossa utilização diária. Vejamos. 5. Redes Sociais: o vídeo ou a foto sobre aquele assunto que tanto provoca seu interesse não aparece por acaso em seu feed de notícias nas redes sociais. Algoritmos especializados determinam o que você vê com base no seu interesse em determinado tema, apurado pelo seu engajamento com o tema. 6. Navegação por GPS: são algoritmos capazes de calcular a melhor rota entre o ponto de origem e o de destino, considerando as condições de tráfego em tempo real. Convém registrar que GPS vem de Global Positioning System, ou Sistema de Posicionamento Global, e que se tornou recurso indispensável em nossos deslocamentos, sobretudo em lugares desconhecidos. Aplicativos como o Waze e Google Maps utilizam este tipo de algoritmo para funcionarem. 7. Assistência Médica: algoritmos são usados na análise de dados médicos para auxiliar em diagnósticos, identificação de padrões e desenvolvimento de tratamentos personalizados. Além destas aplicações, há muitas outras que poderiam ser exploradas neste contexto, incluindo algoritmos usados para indicar conteúdos personalizados em ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 13 plataformas de streaming, comércio eletrônico e serviços de música. Agora que já compreendeu o conceito e as principais aplicações dos algoritmos, você é nosso convidado para conhecer um pouco da história e evolução deste tema tão interessante. 1.3 Breve histórico dos algoritmos Engana-se quem acha que a ideia de um algoritmo surgiu em nossos dias. A começar pela escolha do nome, sua história remonta a séculos atrás. O termo “algoritmo” tem sua origem no nome de Muhammad ibn Musa al-Khwarizmi, um matemático persa do século IX. Com algum esforço (e muita licença poética), conseguimos associar a última parte do nome ao som da palavra algoritmo. Em uma obra chamada “O Livro Compendioso sobre Cálculo por Conclusão e Equação”, nosso personagem sistematizou os métodos de resolução de equações lineares e quadráticas, fato que impulsionou o desenvolvimento da álgebra e da matemática (CITAÇÃO). A utilização de um algoritmo para a resolução de problemas lógicos e matemáticos, contudo, foi verificada em civilizações anteriores ao século IX. Os egípcios, por exemplo, desenvolveram métodos para resolver equações lineares e calcular áreas usando fórmulas que, essencialmente, constituíam uma sequência de passos que levava à resolução de um problema. Em outras palavras, um algoritmo. Como nãopoderia deixar de ser, os gregos antigos - muito bem representados por Euclides e Arquimedes - também contribuíram para o desenvolvimento dos algoritmos, ao formularem procedimentos para a resolução de problemas geométricos. Tempos depois, com a invenção da imprensa, o compartilhamento dos algoritmos – e de todo conhecimento relacionado a eles – foi bastante facilitado. Os trabalhos de matemáticos como Fibonacci e John Napier acrescentaram muito na evolução dos algoritmos. A revolução digital que presenciamos no século XX viabilizou grandes avanços na utilização dos algoritmos. A criação de uma máquina que atende a comandos escritos em linguagem específica constituiu abriu caminho para a criação de algoritmos de diferentes complexidades e aptos a resolverem uma miríade de problemas, desde cálculos científicos voltados a certos nichos de pesquisa até processamento de dados em larga escala. Desta forma, algoritmos de classificação, pesquisa, otimização e aprendizado de máquina ajudaram a construir a Computação como a conhecemos. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 14 ANOTE ISSO O matemático italiano Leonardo Fibonacci foi o criador de uma das sequências matemáticas mais intrigantes que conhecemos. Por padrão, ela se inicia com os números 0 e 1 e, a partir do terceiro termo, cada número é a soma dos dois anteriores. Assim, os primeiros números da sequência são: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, e assim por diante, infinitamente. Mais do que uma simples cadeia de números, esta criação tem aplicações na ciência e na natureza. Ela está relacionada à proporção áurea, uma constante irracional denotada pela letra grega φ (phi), que é aproximadamente igual a 1,618033988749895. A proporção áurea é famosa por sua presença na arquitetura, arte e natureza, e é obtida ao dividir um número da sequência de Fibonacci pelo número anterior na mesma sequência. Além disso, a sequência de Fibonacci aparece em fenômenos naturais, como o arranjo de sementes em flores, a forma espiralada de conchas de moluscos, a disposição de folhas em algumas plantas e até mesmo na reprodução de coelhos, inspirando assim o nome “sequência de Fibonacci”. Em plena era da inteligência artificial e do aprendizado de máquina, os algoritmos desempenham papel crucial em tarefas como reconhecimento de fala, visão computacional e até mesmo na condução autônoma de veículos. A constante evolução deste tema tem ajudado a moldar nosso mundo e a tornar nosso estudo ainda mais interessante. Na próxima seção serão abordados os elementos mais básicos dos algoritmos, incluindo as ideias de entrada, processamento e saída de dados. 1.4 Elementos fundamentais dos algoritmos Um dos elementos mais interessantes sobre os algoritmos é a simplicidade conceitual que os caracteriza, especialmente quando comparada à sua complexidade de aplicações. Na seção 1.1 tivemos oportunidade de fornecer um conceito para algoritmo, comparando-o a uma sequência de passos finitos e ordenados, executados com a finalidade de resolver um problema. Basta refletir um pouco sobre este conceito para que se verifique seu enorme alcance, já que executamos “sequências de passos” em muitas das ações que empreendemos diariamente. Pensemos juntos: ao nos levantarmos da cama, normalmente seguimos uma rotina definida para estarmos aptos ao trabalho ou ao estudo; a criação de um prato segue uma receita igualmente definida de ações e o planejamento de uma festa requer o cumprimento de certas etapas em ordem definida. Os exemplos, enfim, são muitos. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 15 1.4.1 Esquema geral de um algoritmo Uma diferença fundamental entre os “algoritmos” que executamos em nossas ações corriqueiras e os algoritmos que compõem o objeto de nosso estudo é a capacidade do segundo de receber entradas, realizar processamento e fornecer saídas, em formato de expressões, números e/ou textos. Para que esta ideia comece a parecer mais familiar a você, observe a figura 1. Figura 1: Esquema geral dos algoritmos. Fonte: o autor. Do algoritmo complexo até o mais simples, o esquema “entrada > processamento > saída” comporá sua estrutura, mesmo considerando as inevitáveis variações das quais trataremos em encontros futuros. Tomemos uma situação bem simples como exemplo: você deseja criar um algoritmo que some dois números e mostre o resultado da operação. Neste caso, os dados de entrada serão dois números quaisquer, representados por duas variáveis. Neste ponto cabe uma observação e uma lembrança: (i) o conceito de varável será introduzido adiante e, (ii) para quaisquer entradas de um certo conjunto de dados, o algoritmo deverá ser capaz de fornecer o resultado esperado, conforme tratamos na seção 1.1. Decidimos então que a e b representarão os dois números quaisquer que serão processados. Como você pode imaginar, o processamento se dará pela aplicação da operação de soma, em a e b. Ao colocarmos a entrada, o processamento e a saída em uma sequência de passos, teremos: 1. obter a 2. obter b 3. somar a com b 4. exibir o resultado Embora a menção seja quase sempre feita no singular, uma entrada pode ser efetivada por diversas variáveis, conforme podemos constatar nas linhas 1 e 2 do ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 16 nosso exemplo. A linha 3 corresponde ao processamento do algoritmo e, na linha 4, a saída está representada. Parece simples, não é mesmo? Na maioria dos casos, uma boa prática para se começar a criar um algoritmo é identificar a(s) entrada(s), o processamento e a(s) saídas. Como último ato deste primeiro encontro será apresentado o conceito de variáveis, seguido de exemplos e aplicações. 1.4.2 Conceito e aplicações de variáveis A utilização de entidades literais (ou seja, representadas por letras) para simbolizar um valor numérico não é uma “invenção” do mundo dos algoritmos. O uso de letras que representam números (e compõem expressões) é prática comum na matemática, desde sempre. Embora a ideia que permeia a prática em ambas as áreas seja praticamente a mesma, será necessário fazer certas distinções, a fim de que o conceito e aplicação das variáveis nos algoritmos sejam livres de dúvidas. Em nosso contexto de estudo, as variáveis são como “pequenas caixas” situadas na memória, capazes de guardar um ou mais dados, a depender do tipo da variável. Esse dado será usado e modificado (daí a denominação de “variável”) durante a execução do algoritmo. Pense nas variáveis como contêineres em que será possível armazenar dados de diferentes tipos, incluindo número e texto, entre outros. As variáveis são constituídas por três elementos principais: Nome: meio pelo qual uma variável é identificada. Trata-se de uma palavra escolhida pelo criador do algoritmo para representar o valor armazenado. As condições para criação do nome serão abordadas em encontros futuros, mas é importante ressaltar, desde já, que o nome dado à variável em sua criação deverá ser reproduzido fielmente em qualquer atualização da variável feita posteriormente. ISTO ACONTECE NA PRÁTICA Se você criar uma variável chamada nota1 e, ao fazer alguma atualização, referenciá-la como nota_1, por exemplo, seu algoritmo conterá um defeito. Embora os ambientes integrados de desenvolvimento que nos ajudam na criação dos algoritmos contenham recursos para que esses defeitos sejam rapidamente descobertos, esse tipo de distração já tirou o sono de muita gente, principalmente quando os recursos de edição de um algoritmo não eram tão sofisticados. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 17 Tipo do dado que será armazenado: um algoritmo é capaz de armazenar e processar vários tipos de dados, incluindo números inteiros (ou seja, sem casas decimais), caracteres (dea – z, por exemplo) e textos, entre outros. Por isso, antes de estar apta a armazenar e processar valores, a variável deve receber o tipo de dado com que lidará. Valor: por fim, este elemento é aquele que representa o valor real que a variável, em dado momento, possui. A utilização da expressão “em dado momento” neste contexto sinaliza que uma variável simples poderá conter um – e apenas um – valor em um certo momento. Se, durante a execução do algoritmo, uma variável recebe, por exemplo, o valor 5, ele substituirá o valor anteriormente guardado. Por fim, o valor presente na variável seguirá o critério determinado pelo seu tipo. Uma variável definida como inteira não poderá, por exemplo, conter o valor “casa”. A figura 2 exibe uma possível representação de variável. Figura 2: Representação de uma variável. Fonte: o autor. Observe que a letra “a”, situada acima do quadrado, representa o nome da variável. O número 5, posicionado dentro do quadrado, representa o valor momentâneo da variável e, por fim, “inteiro” indica o tipo da variável. Esta representação serve para transmitir a ideia de que a função de uma variável se assemelha a de uma caixa que contém um nome, e é capaz de guardar um valor de determinado tipo. Uma aplicação possível para variáveis pode ser resgatada de um contexto bastante familiar: o cálculo da média obtida nas provas bimestrais. Imagine que o criador do algoritmo tenha declarado cinco variáveis para esta demanda. A declaração é o ato de dar nome e tipo a uma variável. Para que coincidam com suas funções, os nomes ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 18 escolhidos para as variáveis foram nota1, nota2, nota3, nota4 e media, todos grafados em letras minúsculas e sem acentos. Já a definição dos tipos requer, por assim dizer, um certo “conhecimento de mundo” do criador do algoritmo. Em outras palavras, será necessário saber que uma nota é muito comumente expressa em valores com casas decimais, o que acontece também com a média. Por isso, neste caso, você deverá declarar as cinco variáveis com o tipo decimal. Finalizamos aqui este primeiro encontro. Procure resolver os exercícios e realizar suas leituras a fim de se preparar bem para o que vem por aí. Até breve! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 19 CAPÍTULO 2 VARIÁVEIS, CONSTANTES E TIPOS DE DADOS Seja bem-vinda e seja bem-vindo ao segundo encontro de Algoritmos e Lógica de Programação. No capítulo inicial deste material tratamos dos elementos mais básicos do nosso tema, incluindo conceituação, principais aplicações, breve histórico e, por fim, uma breve introdução a variáveis. Por causa da importância e da larga extensão de aplicações deste último item, retomaremos o estudo justamente por ele. Conforme introduzido no capítulo 1, variáveis se assemelham a caixas (ou contêineres) capazes de armazenar um dado, cujo valor será alterado durante a execução do algoritmo. Mas será que as definições sobre variáveis param por aí? Além disso, teremos a oportunidade de entendermos os conceitos relacionados a constantes e a tipos de dados, além de desenvolvermos o primeiro exemplo completo de algoritmo. Fique conosco! 2.1 Definições sobre variáveis e constantes nos algoritmos Em nosso contexto, criar um algoritmo é o equivalente a criar uma sequência de declarações e de comandos que transformarão dados de entrada em resultados disponibilizados ao utilizador do algoritmo, a fim de solucionar um problema. Em outras palavras, dados de entrada passarão por transformações (ou processamento) e os dados resultantes serão entregues como saída ao usuário. Este processo, que constitui o esquema geral dos algoritmos, pede a existência de um elemento que permita o armazenamento e recuperação dos dados que mencionamos, seja durante a entrada, durante o processamento ou durante a saída. A este elemento damos no nome de variável. As variáveis, portanto, são os pilares dos algoritmos e, sem elas, a resolução de um problema através de uma sequência de passos seria inviável. Iniciaremos as nossas definições sobre variáveis pelas regras de constituição do nome que as identificam. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 20 2.1.1 Definição do nome de uma variável O objetivo final da criação de um algoritmo é a transformação dele em um programa de computador, criado em determinada linguagem de programação, de modo que possa ser executado pelo computador. Embora devamos retomar este tema em encontros futuros, a menção a este fato já faz sentido neste item, posto que os critérios de criação de um nome de variável (e de demais identificadores do algoritmo) deverão seguir as regras da linguagem de programação adotada para a implementação do algoritmo. Sobre esta questão, fixe o seguinte: as regras de composição do nome de uma variável aqui apresentadas têm uma origem definida e estão em sintonia com nossos objetivos futuros. Os nomes das nossas variáveis deverão ser iniciados com uma letra, nos intervalos de a – z ou de A – Z. Há ainda a possibilidade de se iniciar um nome de variável com um sublinhado ( _ ). Depois deste primeiro caractere, você poderá usar combinações de letras, números e sublinhados. Além disso, as letras maiúsculas e minúsculas são diferenciadas na composição do nome da variável, de modo que nota1 e Nota1 sejam consideradas duas variáveis distintas. Por último, o espaço não é admitido no nome da variável. Nomes separados por espaço são consideradas duas variáveis, e não apenas uma. Embora estes apontamentos sejam regras das quais não podemos nos esquivar, adotaremos algumas convenções não obrigatórias que nos ajudarão na padronização dos nomes. Escreveremos os nomes todos em letras minúsculas e, no caso de haver uma composição de termos, eles serão separados por sublinhados. Vejamos alguns exemplos de nomes de variáveis, com a indicação de validade ou não. nota_1: válido 1_nota: inválido Nota: válido, mas fora do padrão primeiro_nome: válido primeiro nome: inválido Antes de avançarmos, cabe uma última recomendação: é fundamental que você escolha o nome da variável de acordo com a função que ela irá exercer em seu algoritmo. Por óbvio que pareça, uma variável criada com o nome de “media” não deverá conter outro valor que não a média calculada entre números. Além disso, chamar uma variável de “arvore_frondosa” em uma operação aritmética, por exemplo, em nada ajudará no entendimento do algoritmo, embora ele possa executar exatamente o que se pretendia. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 21 2.1.2 Definições sobre o tipo de uma variável Tente imaginar a variedade de dados com os quais lidamos em nosso cotidiano e nos formatos que eles assumem: uma data de nascimento tem composição, formato e utilização diferentes de um nome próprio, por exemplo. Um número com o qual se pretende expressar elementos da física quântica tem forma e função diferentes do número que indica uma idade. Os exemplos podem ser variados e numerosos, mas um fato já parece claro: para construir um algoritmo, será necessário contar com a possibilidade de se criar variáveis de diferentes tipos, a fim de poder contemplar a miríade de problemas a serem resolvidos. Por isso, o tipo da variável será mais um elemento a ser considerado em sua criação. Na sequência serão descritos alguns tipos de dados disponíveis para armazenar diferentes tipos de dados, a começar pelo mais comum. 1. Inteiro: este tipo de variável é usado para armazenar números inteiros, positivos ou negativos. Vale sempre lembrar que um número inteiro não contém a parte decimal, ou seja, não contém vírgula em sua composição. Grandezas como idade, quantidade de passos e número de repetições de um determinado evento podem ser exemplos que demandema criação de variáveis inteiras. 2. Ponto Flutuante: variáveis do tipo ponto flutuante são usadas para armazenar números com parte decimal. A altura de uma pessoa, uma nota escolar em formato numérico e o peso de um produto são exemplos típicos de grandezas que devem ser mantidas em variáveis deste tipo. 3. String: também conhecido como cadeia de caracteres, este tipo é usado para armazenar texto, normalmente formado por uma sequência de caracteres alfanuméricos. 4. Booleano: também conhecido como tipo lógico, um booleano poderá conter apenas dois valores possíveis: verdadeiro ou falso. ISTO ESTÁ NA REDE A compreensão do nome “booleano” como tipo de dado passa pela descoberta da vida e da obra do homem que o inspirou. George Boole foi um matemático inglês, criador da Álgebra Booleana, que serve da lógica computacional. Para saber mais, consulte https://www.ebiografia.com/george_boole/. Acesso em 26 set. 23. https://www.ebiografia.com/george_boole/ ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 22 5. Lista: as variáveis do tipo “lista” são usadas para armazenar uma coleção ordenada de valores, que podem ser de diferentes tipos. Uma lista, portanto, é um tipo de variável conhecida como composta, por ser capaz de armazenar mais do que um valor em sua estrutura. Um bom exemplo do uso de uma lista pode se efetivar quando o criador do algoritmo deseja armazenar todas as idades apuradas a partir de uma amostra de eleitores de uma certa região. De modo geral, sempre que você precisar armazenar um conjunto de valores – ao invés de apenas um – a utilização de uma lista poderá ser uma boa ideia. Por enquanto, esses são os tipos de variáveis mais comuns que usaremos nos algoritmos. A exemplo das regras para composição do nome da variável, os tipos aqui descritos seguem padrões provenientes de ferramentas que serão usadas por você num futuro próximo. 2.1.3 Definições sobre constantes Com base no que estudamos até o momento, pudemos entender que a função de manter valores – sejam numéricos ou não – tornam as variáveis indispensáveis na criação de um algoritmo. O nome de “variável” dado a este elemento reflete seu caráter não permanente e justificar a necessidade de sua existência é bem simples: os valores por ela mantidos variam de acordo com inúmeros fatores, incluindo a entrada feita pelo usuário ou o tipo de transformação aplicada sobre ela. Todos esses fatos, contudo, se contrapõem ao nosso próximo objeto de estudo: a constante. Constantes são elementos que não se modificam durante a execução do algoritmo. Embora ocorram com menos frequência na maioria dos algoritmos, uma constante tem sua importância resguardada por vários motivos. Imagine que seu algoritmo demande a utilização da constante PI em várias ocasiões. A depender da precisão numérica necessária, o número que o representa poderá ser extenso e reproduzi-lo com frequência certamente aumentará a propensão ao erro. Em casos como este, não hesite: declare uma constante e dê a ela o valor desejado, conforme se vê na sequência: PI = 3.141592 ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 23 Embora a ideia seja a de manter o valor da constante PI inalterado durante todo o algoritmo, seu criador poderá concluir pela necessidade de se acrescentar mais 4 casas de precisão no número. Ao optar por usar uma constante, ele não precisará buscar todas as ocorrências do valor de PI para acrescentar as casas necessárias. Ao invés disso, bastará proceder a alteração na constante. Uma outra vantagem desta prática é a de aumentar a legibilidade e a clareza do seu algoritmo, já que o nome da constante idealmente descreve sua função. As constantes desempenham um papel importante na clareza e na manutenção de um algoritmo, pois ajudam a tornar o código mais compreensível e fácil de gerenciar. Lembre-se, então, de usar constantes sempre que for aplicável ao escrever seu algoritmo. Como última recomendação, use letras maiúsculas ao declará-las. 2.2 Declaração de variável e operação de atribuição Existem diversas formas de uma variável ser utilizada durante a execução do algoritmo e cada uma destas formas merecerá oportunamente nossa atenção. O que precisa ser pontuado desde já é a necessidade de se fazer uma espécie de anúncio da variável ao algoritmo que está sendo escrito. A esta operação damos o nome de declaração da variável. Declarar uma variável é, portanto, informar ao algoritmo nome e tipo do recurso que será usado logo na sequência. Declara-se a variável uma única vez e, depois, usa-se a variável quantas vezes forem necessárias. Vamos ao primeiro exemplo: inteiro idade Esta simples linha informa que o algoritmo utilizará uma variável chamada idade e que seu tipo é inteiro, ou seja, só aceitará números sem a parte decimal. Eventualmente você poderá querer declarar uma variável e, na mesma linha, indicar seu valor inicial. Vejamos: inteiro contador = 0 ANOTE ISSO Linguagens de Programação modernas geralmente dispensam a prévia declaração de uma variável e a definição do tipo correspondente. O tipo de uma variável é determinado automaticamente com base no valor atribuído a ela. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 24 Embora a atribuição de valor a uma variável possa ser feita no ato da sua declaração, é mais comum que ela seja efetivada durante o algoritmo. Realizar uma atribuição significa inserir nela um valor, que deve ser coerente com seu tipo. Para nossos propósitos, esta operação se faz com o sinal de igual ( = ). Observe que a atribuição contador = 0 diferencia-se do exemplo anterior justamente pelo momento em que aparece no algoritmo: no primeiro exemplo, temos uma declaração seguida de atribuição e, no segundo, temos uma simples atribuição, feita depois de o tipo ter sido associado à variável. A operação de atribuição, no entanto, não é a única forma de se associar um valor a uma variável. Sigamos adiante para descobrirmos mais a esse respeito. 2.3 Introdução a comandos de entrada e saída Como você bem se lembra, o esquema geral de um algoritmo prevê a aquisição de dados, o processamento destes dados e, por fim, o fornecimento do resultado obtido por meio do processamento. Esquematicamente, temos “entrada > processamento > saída” e, nesta seção, trataremos especificamente das formas de entrada e de saída de dados em nossos algoritmos. Antes, porém, de nos aventurarmos neste assunto, vale a pena tratarmos de um elemento que será praticamente onipresente em nossas atividades: o comando. ANOTE ISSO Será muito comum você se deparar com o termo “pseudocomando” quando estiver buscando referências a respeito de comandos em algoritmos. Os autores usam essa denominação para diferenciar uma instrução escrita segundo as regras formais de uma linguagem de programação e outra escrita numa linguagem meramente algorítmica e que não poderá ser executada por um computador, a não ser em ferramentas específicas para criação de algoritmos. Por ora, estaremos usando apenas pseudocomandos em nossos estudos. Outro apontamento importante neste contexto está relacionado aos meios físicos de entrada e saída de dados. Conforme já mencionado, entendemos que nossos algoritmos poderão, em estágio mais avançado, ser convertidos em comandos de linguagem de programação para que possam ser executados por um computador. Assumimos, portanto, desde já, que o teclado será nosso meio universal de entrada e o monitor de vídeo (também referenciado como tela) será nosso meio de saída. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 25 Um comando representa uma ordem – ou uma instrução - dada ao algoritmo para que uma ação seja tomada. Um comando, portanto, remete a uma ação e inclui uma entrada de dado, uma atribuição, uma operação lógica, umaoperação de repetição e tantas outras que teremos oportunidade de estudar. Entrada de dados: em nossos algoritmos, usaremos o comando leia() para realizar as entradas de dados e uma variável deverá ser inserida entre os parênteses do comando. Objetivamente, o comando leia()permite que o usuário do algoritmo insira um dado pelo teclado, que deve ser do mesmo tipo da variável que ocupa o espaço entre parênteses. Após a execução do comando, esta variável conterá o valor digitado. Depois de tratarmos da saída de dados, um exemplo completo será desenvolvido. Saída de dados: usaremos o comando escreva() para indicar ao algoritmo que desejamos que um valor ou um conteúdo textual seja enviado a tela, como forma de realizar uma saída de dado. As regras para utilização deste comando incluem: a) Para que um texto seja enviado à tela, ele deve estar entre aspas. Exemplo: ao se deparar com a instrução escreva (“Este comando serve para enviar texto à tela. “), o algoritmo enviará à tela o exatamente o texto situado entre aspas. b) Para que o conteúdo de uma variável seja enviado a tela, ela deverá ser colocada entre os parênteses do comando. Exemplo: o comando escreva (idade) irá enviar à tela o conteúdo da variável idade, que será aquele informado pelo usuário durante a entrada de dados ou será resultado de algum processamento previamente realizado. c) É possível também que o resultado de uma expressão, resolvida pelo próprio comando de saída, seja enviada para a tela. Exemplo: o comando escreva (principal + acrescimo) enviará a tela o resultado da soma entre o conteúdo da variável chamada principal e o conteúdo da variável chamada acrescimo. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 26 Postos estes elementos, poderemos então desenvolver nosso primeiro exemplo completo de algoritmo. Para que a explicação de cada uma das linhas seja facilitada, elas serão numeradas. 1 algoritmo exemplo1_soma_duas_idades 2 //Este algoritmo soma duas idades e mostra o resultado em tela 3 var 4 idade1, idade2, soma_das_idades: inteiro 5 inicio 6 escreva (“Informe a sua idade: “) 7 leia (idade1) 8 escreva (“Informe a idade do seu irmão ou irmã: “) 9 leia (idade2) 10 soma_das_idades = idade1 + idade2 11 escreva (“A soma das idades é “, soma_das_idades) 12 fimAlgoritmo Linha a linha, este algoritmo realiza o que segue: Linha 1: aqui o algoritmo simplesmente recebe um nome. A partir desta linha, a forma correta de fazer referência a ele será exemplo1_soma_duas_idades. Repare que a composição do nome segue uma das regras de composição de nomes de variáveis: não pode haver espaço entre as palavras. Linha 2: esta linha contém um texto inserido como comentário. Ao longo do seu algoritmo, você poderá inserir comentários em linhas inteiras (como é o caso deste exemplo) ou logo após a inclusão de um comando. Os comentários não compõem os elementos de execução de um algoritmo, mas servem para documentá-lo. Neste ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 27 nosso caso, o comentário informa a funcionalidade do algoritmo, em poucas palavras. Para distinguir um comentário de uma linha de comando, você deve inserir duas barras (//). Durante nossos encontros, trataremos de outra forma de apontar ao algoritmo que o texto que segue compõe um comentário. Linha 3: aqui é indicado o início da área de declarações de variáveis. Linha 4: nesta linha, três variáveis são declaradas. Como elas são do mesmo tipo, foi possível utilizar a mesma linha para declaração das três. Note que o tipo designado foi inteiro, pois os números que indicam idades pertencem ao domínio dos inteiros. Linha 5: aqui é apontado o início da área em que os comandos serão escritos, ou seja, nesta linha é que o algoritmo efetivamente tem início. Linha 6: o primeiro comando do algoritmo foi colocado na linha 6. Trata-se de uma instrução para que o texto situado entre aspas seja colocado em tela. Tenha em mente que textos enviados à tela fazem parte da comunicação do algoritmo com a pessoa que o utilizará. Por isso, a recomendação é que você seja o mais claro e assertivo possível nestas ocasiões. Linha 7: o comando leia (idade1) provoca uma interrupção na execução do algoritmo, a fim de que o usuário digitar a idade dele. Uma vez digitada, a idade informada pelo teclado será atribuída à variável idade1. No momento, não dispomos de recursos técnicos para instruir o algoritmo a validar o valor informado pelo usuário. Se a digitação tiver sido, por exemplo, 320, o algoritmo assumirá que este é um valor coerente com uma idade humana, o que não é verdade. No entanto, em sendo digitado um número inteiro, a falta de coerência que apontamos não será impeditivo para a correção do algoritmo. Linha 8: esta linha tem finalidade semelhante a apontada na linha 6, que é a de obter uma idade. Neste caso específico, no entanto, o usuário é orientado a informar a idade de outra pessoa, qual seja de um irmão ou irmã. Linha 9: aqui valem os mesmos apontamentos feitos para a linha 7. A variável envolvida, no entanto, é outra. Se, por desatenção do criador do algoritmo, a variável idade1 for novamente usada, seu valor anteriormente lido será apagado. Linha 10: esta linha contém uma expressão aritmética e um comando de atribuição. A expressão aritmética é efetivada pela operação de adição entre duas variáveis e a atribuição é realizada pelo sinal de igual. É necessário observar que as ações ocorrem da direita para esquerda: o conteúdo da variável idade1 é somado ao conteúdo da variável idade2 e o resultado é atribuído à variável soma_das_idades. Será comum ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 28 você se deparar com o comando de atribuição representado por uma seta orientada para a esquerda, justamente para reforçar a direção em que ocorrem as ações. Linha 11: esta linha realiza a apresentação do resultado em tela, precedido por um texto que o apresenta. É necessário novamente pontuar a importância da clareza que deve caracterizar este texto, já que dele dependerá boa parte do entendimento do usuário sobre a saída exibida. Note que nesta linha o comando de saída contém elemento fixo (texto colocado entre aspas) e parte variável, representada por soma_ das_idades. Linha 12: esta linha simplesmente indica o final do algoritmo. Bastante informação para um primeiro algoritmo, não é mesmo? Há ainda uma última observação a ser feita: da linha 6 até a linha 11 o recuo do comando está diferente em relação às demais linhas. Isso se deve ao recurso da indentação, que consiste em inserir espaçamento horizontal para que as estruturas dos blocos de comandos fiquem aparentes. Embora essa providência não altere a execução do seu algoritmo, ela servirá para conferir organização e boa legibilidade ao seu código. Até o próximo encontro! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 29 CAPÍTULO 3 EXPRESSÕES ARITMÉTICAS, LÓGICAS E RELACIONAIS Como você teve a oportunidade de constatar em nosso encontro anterior, a criação de um algoritmo está vinculada, principalmente, à escrita de comandos e à realização de operações com variáveis. Conforme avançarmos em nossos estudos, trataremos de comandos que realizam ações muito além das atribuições, das leituras e das escritas em tela. Por ora, no entanto, teremos como objeto de estudo as operações com variáveis e as transformações que poderemos aplicar nessas variáveis, por meio da utilização de expressões aritméticas e lógicas. 3.1 Operações aritméticas Como o próprio nome sugere, estas operações envolvem transformações aritméticas aplicadas a variáveis e constantes, por meio de expressões. É necessário salientar, desde já, que todas as expressões que abordaremos exigem que uma variável receba, emforma de atribuição, o valor calculado na expressão. Isso quer dizer que não é permitida a escrita, por exemplo, da expressão x + y, sem uma variável à qual seja atribuída o resultado da soma. Em outras palavras, nesta expressão faltou a variável que deverá receber o resultado da soma entre x e y. Na sequência você terá a oportunidade de constatar como essa regra funciona. As operações aritméticas mais comuns incluem: 3.1.1 Adição Operação efetivada pelo sinal de adição ( + ) e apta a somar dois ou mais números, sejam eles representados por variáveis ou por constantes. Em nosso contexto, essa operação é útil para calcular totais, médias, acumular valores e realizar contagens. Na sequência, alguns exemplos de suas aplicações e as respectivas explicações. a) distancia_total = distancia1 + distancia2 ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 30 Supondo que todas as variáveis sejam do tipo inteiro, a expressão começa a ser resolvida pela soma entre distancia1 e distancia2. Na sequência, o valor resultante é atribuído à variável distancia_total. Caso esta última variável já contenha algum valor atribuído, ele será simplesmente sobreposto pelo novo valor. b) contador = contador + 1 Esta expressão aritmética é certamente um dos componentes mais importantes dos algoritmos. De tão utilizada, ela recebeu um nome próprio: contador. Esta denominação expressa justamente função que a caracteriza. Um breve olhar sobre essa expressão pode levantar uma questão bastante oportuna: numa equação matemática comum não é possível que uma variável assuma um valor igual a ele próprio mais um. No entanto, a expressão que implementa um contador não é uma equação e seria errado tratá-la como tal. A leitura que devemos fazer da expressão contador = contador + 1 é a seguinte: ao valor contido na variável contador deve ser somado 1, e o resultado deve ser atribuído à própria variável contador. Desta forma, a execução desta linha provoca o incremento da variável que ocupa o lado esquerdo da expressão. Não levará muito tempo até desenvolvermos um exemplo completo utilizando este recurso, mas é prudente pontuar que o nome da variável que usaremos em nossos futuros desenvolvimentos nem sempre será contador. O que caracteriza o funcionamento desta expressão não é o nome da variável que a compõe, mas a sua estrutura. c) acumulador = acumulador + numero Ainda no escopo das operações aritméticas efetivadas por meio de adição, é necessário que você conheça a expressão conhecida por acumulador ou somador. A cada execução desta linha, o valor contido na variável numero é somado ao valor já existente na variável acumulador, o que faz a expressão operar somas sucessivas. Novamente, os nomes das variáveis poderão variar entre exemplos, o que não invalida a funcionalidade da expressão. 3.1.2 Subtração A subtração é usada para o algoritmo possa encontrar a diferença entre dois números e efetiva-se por meio da aplicação do sinal - . Ela é útil para calcular variações, saldos, e outras quantidades relacionadas à diferença entre valores. A expressão diferenca ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 31 = numero1 - numero2 subtrai o valor contido em numero2 do valor contido em numero1, e atribui a diferença à variável diferenca. Assim como as demais expressões, a resolução se dá no sentido da direita para a esquerda. 3.1.3 Multiplicação Esta operação é usada para calcular o produto de dois ou mais números. Ela é essencial para calcular áreas, volumes, e para criar repetições controladas por multiplicação. Em encontros futuros trataremos de repetições de trechos de algoritmos. A expressão produto = numero1 * numero2 multiplica o valor de numero1 por numero2 e atribui o produto à variável produto. Observe que operador da multiplicação é um asterisco (*) e não a letra x, como na Matemática. 3.1.4 Divisão Colocado da forma mais simples possível, a divisão é usada para dividir um número pelo outro, o que pode ser útil para calcular taxas, médias ponderadas e distribuir quantidades. Na expressão quociente = dividendo / divisor, a variável divisor jamais deve assumir o valor 0. Por meio da aplicação da correta validação, você deve assegurar-se de que este fato não acontecerá durante a execução do seu algoritmo. Note que o operador da divisão é a barra (/). 3.1.5 Divisão inteira Esta operação retorna apenas a parte inteira do resultado da divisão, o que pode será quando você deseja calcular quantas vezes um número cabe em outro sem considerar os decimais. Este operador é representado pela barra dupla (//) e a expressão resultado_inteiro = numero1 // numero2 é um exemplo de sua utilização. 3.1.6 Módulo O operador de módulo, representado pelo símbolo de porcentagem (%), retorna o resto da divisão entre dois números. Isso pode ser útil para verificar se um número é divisível por outro ou para determinar se o valor contido em certa variável é par ou não. Como exemplo, considere a expressão resto = numero1 % 2. Caso o valor resultante em resto seja 0, teremos que o valor contido em numero1 é par. Qualquer número que retorne resto 0 quando dividido por 2 é par. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 32 3.1.7 Potenciação A operação de potenciação, efetivada pelo operador duplo asterisco (**), eleva um número a uma potência específica, e pode ser usada em cálculos exponenciais próprios de juros compostos, por exemplo. Um exemplo de expressão para esta operação é resultado_potencia = numero_base ** expoente. No decorrer de nosso estudo poderá ser introduzido um ou outro novo operador, conforme necessidade. É importante lembrar que, ao usar operações aritméticas em algoritmos, é necessário levar em consideração a precedência dos operadores, do mesmo jeito que fazemos na Matemática. Por exemplo, multiplicação e a divisão são resolvidas antes da adição e da subtração e os parênteses servem para controlar a ordem das operações quando necessário. Em resumo, as operações aritméticas serão ferramentas essenciais em nossos algoritmos e permitirão que você resolva uma ampla variedade de problemas matemáticos e quantitativos de forma eficaz. Elas desempenham um papel fundamental em muitos campos, desde cálculos financeiros até simulações científicas. 3.2 Operações lógicas Assim como as transformações aritméticas, as operações lógicas desempenharão papel fundamental na construção dos nossos algoritmos. Em composição com variáveis, eles integram ferramental essencial para que comandos específicos sejam capazes de tomar decisões com base em certas condições. Para contemplar nossos objetivos imediatos, trataremos dos três operadores lógicos principais: e, ou e não. Com eles, você será capaz de compor expressões que basearão a lógica necessária para a resolução de problemas específicos. 3.2.1 Operador “e” O operador lógico “e” é parte fundamental dos algoritmos, pois desempenha um papel crucial na tomada de decisões com base em múltiplas condições. Ele é usado para combinar duas ou mais condições e determinar se todas elas são verdadeiras. A utilização deste operador implica que uma expressão resultará em verdadeira somente se todas as condições individuais que a compõem também forem verdadeiras. A boa notícia em favor do entendimento destas condições (e de outras que integrarão outros operadores lógicos) é que as utilizamos constantemente em nosso dia a dia. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 33 Tomemos como exemplo a seguinte situação: ao abrir o jornal, você se depara com uma vaga de emprego cujo preenchimento será feito por (a) pessoas regularmente matriculadas em curso relacionado à Tecnologia da Informação e (b) pessoas com 18 anos ou mais. Observe que o atendimento a apenas uma das condições nãotorna alguém apto para a vaga. É necessário que a condição (a) e a condição (b), ambas sejam verdadeiras para que tenhamos um candidato apto à vaga. Outros exemplos serão desenvolvidos quando estudarmos os comandos condicionais, em encontro futuro. Por ora, devemos entender que, dadas duas condições, ambas devem ser satisfeitas (ou seja, devem ser verdadeiras) para que a expressão toda que compõe uma expressão lógica criada com o operador “e” seja verdadeira. Você terá a oportunidade de constatar a grande utilidade deste operador em algoritmos que contenham tomadas de decisões com base em múltiplas variáveis ou condições. Ele permite que você crie algoritmos dotados de lógica complexa para resolver problemas do mundo real de maneira eficaz. 3.2.2 Operador “ou” O operador que abordaremos nesta seção é igualmente importante na construção de algoritmos, pois também desempenha papel fundamental na tomada de decisões baseadas em condições múltiplas. Nos algoritmos que construiremos, o operador “ou” será usado para combinar duas ou mais condições e determinar se pelo menos uma delas é verdadeira. Em outras palavras, a expressão resultará em verdadeira se pelo menos uma das condições individuais for verdadeira. Para ilustrar este conceito, devemos resgatar o exemplo usado no operador “e”: imagine que a descrição da vaga, desta vez, indique que quem (a) está matriculado em um curso de TI OU quem (b) tem 18 anos ou mais, estará apto a concorrer. Observe que bastará que uma das condições seja satisfeita para que o candidato possa concorrer à vaga. Neste ponto valem duas observações: a primeira é que o operador “ou”, de fato, é menos restritivo que o operador “e”, pois toda a expressão será verdadeira se apenas uma condição componente o for. A segunda observação será colocada em forma de pergunta: e se o candidato tiver 18 anos ou mais e estiver cursando TI? Neste caso, a aplicação do operador “ou” também retornará verdadeiro para a expressão. Em resumo, o operador “ou” é bastante útil para criar algoritmos flexíveis que podem lidar com diferentes cenários. Ele é frequentemente usado em situações em que você ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 34 deseja que pelo menos uma das condições seja satisfeita para prosseguir. Antes de avançarmos, vale o registro de que você poderá encontrar uma variação deste operador, conhecido com “ou exclusivo”. Neste caso, sua aplicação retornará verdadeiro quando uma das condições for verdadeira, mas retornará falso se ambas forem verdadeiras. 3.2.3 Operador “não” O operador lógico “não” serve para realizar a negação de uma condição lógica. Vamos explorar como esse operador funciona e sua importância nos algoritmos. Em outras palavras, ele é usado para inverter o valor de uma condição: se uma condição for verdadeira, o operador “não” a tornará falsa; se a condição for falsa, o operador “não” a tornará verdadeira. Este recurso será fundamental para criar parte da lógica em nossos algoritmos, permitindo a alteração do comportamento de variáveis e expressões com base em condições negativas. Ele é frequentemente usado em conjunto com os operadores “e” e “ou” para criar expressões lógicas mais elaboradas. 3.3 Tabela Verdade É comum que, ao tomarmos contato com um conhecimento novo, sintamos falta de algum artifício ou recurso que possa resumir toda a informação que acabamos de receber. Por sorte, o comportamento dos operadores lógicos que abordamos até aqui podem ser sintetizados e analisados, em um recurso chamado tabela verdade. Ela exerce papel bastante importante na lógica que permeia estes operadores e certamente irá nos ajudar a com as condições compostas que deveremos escrever em breve. Em uma tabela verdade teremos listadas todas as possíveis combinações de condições lógicas aplicadas aos operadores lógicos que conhecemos. Além de exibir as mencionadas condições, a tabela verdade exibirá o resultado das combinações formadas por essas condições. Utilizando os operadores “e”, “ou” e “não” para condições “a” e “b”, a tabela verdade assim será composta conforme exibido na tabela 3.1: a b a e b a ou b não a não b V V V V F F V F F V F V F V F V V F F F F F V V Tabela 3.1 – Tabela Verdade Fonte: o autor (2023). ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 35 Nesta tabela, “V” representa verdadeiro e “F” representa falso. Ela lista todas as combinações possíveis de valores para a e b e mostra o resultado das expressões “a e b”, “a ou b”, “não a” e “não b”, considerando todas as combinações possível. Observe que, com a utilização do operador “e”, a expressão é verdadeira apenas quando ambas as condições a e b são verdadeiras. No caso do operador “ou”, a expressão é verdadeira em todos os casos, exceto quando a e b são ambos falsos. Por fim, a aplicação do operador “não”, o valor lógico de a e de b são simplesmente invertidos. Em resumo, as tabelas verdade desempenham um papel importante na análise e no desenvolvimento de algoritmos, ajudando a garantir que as expressões lógicas funcionem conforme o esperado. No entanto, não só de operações lógicas serão construídos nossos algoritmos. Em muitas ocasiões precisaremos comparar valores entre variáveis ou entre uma variável e uma constante. Para isso, utilizaremos outro recurso muito importante: os operadores relacionais. Sigamos adiante! 3.4 Operações relacionais Durante a parte introdutória das operações lógicas, em seção anterior a esta, foi mencionado que elas compõem parte do conjunto de recursos que permitem tomadas de decisões durante a execução dos algoritmos. Com o estudo das operações relacionais introduziremos mais um recurso para tornar nossos algoritmos poderosas ferramentas de decisão. As operações relacionais são viabilizadas por seis operadores relacionais, conhecidos também como operadores de comparação. Não por acaso, estes recursos permitem comparar variáveis e expressões, com a obtenção de um resultado que varia entre verdadeiro e falso. Na sequência, estas ideias ganharão vida pela descrição dos operadores relacionais e pelos respectivos exemplos. 3.4.1 Operador “igual” Este operador, representado por um duplo igual ( == ) verifica se dois valores são iguais e retorna “verdadeiro” se for constatada a igualdade entre eles e “falso” se os valores forem diferentes. Vale a menção de que o termo “valores” remete ao conteúdo de uma variável ou a uma constante. Faz sentido, portanto, comparar uma variável com outra e uma variável com uma constante. Você jamais deve comparar uma constante com outra, seja qual for o operador usado. Imagine x e y como duas variáveis, cujos valores foram informados pelo usuário ou atribuídos por operação anterior. Em dado momento, elas contêm os valores de 4 e 5, ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 36 respectivamente. Assim, x contém o valor 4 e y contém o valor 5 e, ao aplicarmos o operador de igualdade, teremos que x == y retornará falso, pois os valores contidos nas variáveis sob comparação não são iguais. ANOTE ISSO No contexto dos algoritmos e da programação de computadores, o termo “retorna” indica a obtenção de um resultado por meio da aplicação de uma função ou de um operador específico. Já que consideramos a sequência “entrada > processamento > saída” como esquema geral dos algoritmos, podemos aproveitar essa ideia para representarmos também a atuação de um operador. A comparação de igualdade entre duas variáveis, por exemplo, toma as variáveis (entradas), realiza a comparação (processamento) e “retorna” verdadeiro ou falso (saída). 3.4.2 Operador “diferente” Este operador, representado pelo símbolo ( != ) verifica se dois valores são diferentes. O resultado, porém, segue comportamento contrário ao operador de igualdade. Aqui, a operação retornará “verdadeiro” seos valores forem diferentes e retornará “falso” se os valores forem iguais. Ainda considerando os valores de x e y do exemplo anterior, teremos que a aplicação de x != y retornará verdadeiro, pois os valores contidos nas variáveis sob comparação não são iguais. 3.4.3 Operador “maior que” Este operador, representado pelo sinal matemático de maior (>), retorna verdadeiro quando o valor da esquerda for maior que o valor da direita. Em operações que envolvam comparação, o valor da esquerda sempre será uma variável e o valor da direita poderá ser uma variável ou uma constante. Em uma situação em que o algoritmo deva apurar se uma idade lida é maior do que um certo valor, a expressão idade > 29 retornará verdadeiro se o conteúdo de idade for, de fato, maior que 29. 3.4.4 Operador “menor que” Este operador, representado pelo sinal matemático de menor (<), retorna verdadeiro quando o valor da esquerda for menor que o valor da direita. Em uma situação em que o algoritmo deva apurar se uma distância lida (ou calculada) é menor do que um ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 37 certo valor, a expressão distancia < 100 retornará verdadeiro se o conteúdo de distancia for, de fato, menor que 100. 3.4.5 Operador “maior ou igual” Este operador, representado pelo sinal de maior ou igual (>=), retorna verdadeiro quando o valor da esquerda for maior ou for igual ao valor da direita. É sempre necessário frisar que, em operações que envolvam comparação, o valor da esquerda sempre será uma variável e o valor da direita poderá ser uma variável ou uma constante. Em uma situação em que o algoritmo deva apurar se uma idade lida é maior ou igual a um certo valor, a expressão idade >= 29 retornará verdadeiro se o conteúdo de idade for maior que 29 ou for igual a 29. 3.4.6 Operador “menor ou igual” Este operador, representado pelo sinal de menor ou igual (<=), retorna verdadeiro quando o valor da esquerda for menor ou for igual ao valor da direita. Em uma situação em que o algoritmo deva apurar se uma distância lida (ou calculada) é menor ou igual a um certo valor, a expressão distancia <= 100 retornará verdadeiro se o conteúdo de distancia for menor ou for igual a 100. Os operadores relacionais serão amplamente utilizados em estruturas de controle condicionais, cujo conhecimento você logo adquirirá. Estes recursos também desempenharão papel importante na validação de entrada do usuário em nossos algoritmos. Entenda o que abordamos neste encontro como peças de um quebra- cabeça que logo se unirão a comandos e outras expressões para tornar seus algoritmos interessantes e úteis. Até a próxima! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 38 CAPÍTULO 4 INTRODUÇÃO À ALGORITMOS ESTRUTURADOS Até o momento, o caminho que trilhamos nos mostrou recursos fundamentais relacionados aos algoritmos, o que incluiu meios de inserirmos dados para serem processados e operadores que serão úteis para a construção do encadeamento lógico que resolverá o problema proposto. É bem verdade que nosso percurso ainda está no início, mas os elementos que estudaremos neste encontro comporão as bases de todos os algoritmos que você escreverá. Um algoritmo estruturado é aquele escrito de maneira clara e organizada, de modo a tornar-se facilmente compreensível e cuja manutenção é bastante simplificada. Esta abordagem é parte fundamental da nossa área e desempenha papel crítico na resolução de problemas por meio da criação de algoritmos e posterior uso de linguagem de programação. A clareza e organização associadas a algoritmos estruturados nascem da utilização apenas de estruturas sequenciais, de seleção e de repetição, justamente os temas que serão tratados de maneira introdutória neste nosso encontro. Esta abordagem é amplamente utilizada durante os passos iniciais do ensino de algoritmos, pois ela simplifica o rastreamento do fluxo de execução do algoritmo e a identificação de erros cometidos durante a escrita do código. Conforme você terá oportunidade de conferir a partir desta aula, os algoritmos estruturados podem ser considerados a pedra angular da programação de computadores através da escrita de algoritmos, pois promovem clareza, a eficiência e a facilidade de manutenção nos projetos de software, itens amplamente valorizados em ambiente acadêmico e profissional. Sigamos adiante, pois, com o estudo das estruturas sequenciais, de seleção e repetição. 4.1 Estruturas sequenciais Começamos nossa abordagem dos algoritmos estruturados pela mais elementar das três estruturas: a sequencial. Como a própria denominação sugere, ela se baseia na execução linear de um bloco de comandos, uma após o outro, seguindo uma sequência sem interrupção. De algum modo, todos os algoritmos que você criar usarão ao menos uma estrutura sequencial, o que significa que cada instrução (ou comando) será processada após a conclusão da anterior, criando um fluxo de execução claro ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 39 e previsível. Conforme já mencionado, essa abordagem é essencial para resolver problemas de forma organizada e eficaz. A estrutura sequencial é frequentemente usada para executar operações simples, incluindo entradas de dados feitas pelo usuário, execução de expressões aritméticas, processamentos triviais de dados e apresentação de resultados, nada diferente do que já aprendemos até o momento. Um exemplo possível para ilustrar essas ideias seria um algoritmo que calcula a média entre dois valores inteiros informados pelo usuário. Este algoritmo resolve o problema da seguinte maneira: 1. Receber o primeiro número. 2. Receber o segundo número. 3. Calcular a média entre os dois números. 4. Exibir a média. Embora seja bem simples, este algoritmo tem ao menos duas coisas a nos dizer: a) A forma como ele foi escrito difere ligeiramente da forma demonstrada em encontro anterior. Na verdade, não há uma maneira apenas de expressar nossos algoritmos e este será o tema central da nossa aula número 5. De qualquer forma, a sequência de passos, da forma como foi colocada, serve para que o problema do cálculo da média seja resolvido. Uma forma mais bem elaborada e mais próxima da execução por uma linguagem de programação é a que segue: 1 algoritmo calculo_de_media_aritmetica 2 //Este algoritmo recebe dois valores inteiros, calcula a média entre eles e retorna o resultado. 3 var 4 n1, n2: inteiro 5 media: flutuante 5 inicio 6 escreva (“Informe o primeiro valor: “) 7 leia (n1) 8 escreva (“Informe o segundo valor: “) 9 leia (n2) 10 media = n1 + n2 11 escreva (“A média é “, media) 12 fimAlgoritmo ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 40 Note que não há alteração na funcionalidade do algoritmo, apenas na forma de escrevê- lo. Conforme mencionado, em nosso próximo encontro trataremos detalhadamente das formas de se expressar um algoritmo. b) Este algoritmo é composto por uma única estrutura sequencial. Repare que esta estrutura se caracteriza por conter comandos que serão executados linearmente, do primeiro ao último, sem desvios condicionais ou repetições, recursos que serão tratados ainda nesta aula. A estrutura sequencial é a base sobre a qual outras estruturas de controle, como seleção e repetição, serão construídas. Portanto, dominar essa estrutura é fundamental neste contexto e atingiremos ótimo nível de entendimento com o desenvolvimento de exemplos. 4.2 Estruturas de repetição Uma estrutura de repetição é composta por comando (ou comandos) com capacidade para repetir um determinado trecho de algoritmo. Embora colocada em termos bem simples e objetivos, essa afirmação contém implicações importantes, que serão analisadas na sequência. a) Bloco de comandos: trata-se do conjunto de comandos que serão submetidosà repetição e que estarão subordinados ao comando de repetição escolhido para o processamento dos dados. Quando escrevermos nossos algoritmos, trataremos de delimitar nossos blocos de comandos com indicações de início e fim, nomeadas de acordo com o seguinte padrão: inicioNomeDoComando //neste espaço serão escritos os comandos a serem re- petidos fimNomeDoComando Observe a inclusão de um recuo na área em que os comandos do bloco serão escritos. Esta prática é largamente utilizada para fins de organização do código e leva o nome de indentação. Por fim, um apontamento necessário: em certas ocasiões poderemos dispensar a inclusão da indicação de início do bloco. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 41 b) Condição de parada: a existência de um trecho de algoritmo que contém repetição - da mais simples à mais complexa – implica na existência de um recurso algorítmico que interrompe essa repetição. A este recurso damos o nome de condição de parada, que geralmente é escrita como uma expressão de comparação colocada no comando de repetição. Caso esta expressão contenha um determinado defeito lógico, a repetição poderá nunca ser interrompida, fato que certamente causará problemas ao usuário do algoritmo. Além disso, a não interrupção da execução da repetição causará a negação da parte do conceito de algoritmos em que sua finitude é afirmada. c) Laço de repetição: também conhecido como loop ou iteração o laço de repetição refere-se à execução contínua do bloco definido para ser repetido enquanto a condição de parada assim o permitir. Um loop, portanto, é o modo como é conhecida uma repetição feita no bloco subordinado ao comando de repetição. Para que o criador do algoritmo possa escolher segundo a conveniência da ocasião, os comandos de repetição estão divididos em três modalidades, todas elas essenciais para implementação de tarefas repetitivas nos algoritmos. 4.2.1 Teste da condição de parada no início do bloco Esta modalidade de repetição apresenta a condição de parada no início do bloco que será repetido, o que significa que o bloco será executado somente se a condição de parada não tiver sido ainda satisfeita. Em outras palavras, a condição de parada é verificada no início do bloco e, somente se ainda não tiver sido satisfeita, o código dentro do bloco de comandos será executado. Vale o registro de que, durante suas pesquisas, você poderá encontrar a expressão “loop pré-testado” como denominação alternativa para esta modalidade. O comando que utilizaremos nesta modalidade terá o seguinte formato geral: Enquanto <aqui vai a condição de parada> faça <aqui são escritos os comandos que serão executados enquanto a condição de parada não for verdadeira> fimEnquanto ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 42 Além de implementar a condição de parada antes da entrada do fluxo de execução no bloco de comandos, esta modalidade de comando de repetição apresenta outra característica interessante: normalmente o criador do algoritmo não sabe de antemão quantas repetições serão dadas, dada a construção da condição de parada. Para ilustrar esta ideia, imagine a seguinte situação: o algoritmo deve permitir a entrada das notas dos alunos para fins de cálculo da média, enquanto não for digitado o valor -1. Em notação algorítmica, temos: leia (nota) Enquanto nota != -1 faça <aqui será feita a soma das notas lidas, a fim de que se possa calcular a média fora do loop.> fimEnquanto A satisfação da condição de parada está, portanto, atrelada à digitação do valor -1 por parte do usuário, o que pode ser feito logo na primeira digitação ou em qualquer outra oportunidade. Neste caso específico, a utilização do comando “Enquanto... faça,” implica que o algoritmo verificará no início de cada iteração se a comparação nota != -1 é verdadeira, ou seja, se o usuário não digitou -1. Enquanto essa condição estiver sendo atendida, as repetições continuarão. A definição correta da condição de parada é mandatória para garantir que o programa não entre em um loop infinito e que as ações repetidas sejam controladas. Este exemplo será desenvolvido por completo quando estudarmos em detalhes os comandos de repetição. Por ora, devemos avançar rumo à segunda modalidade de comando de repetição. 4.2.2 Teste da condição de parada no final do bloco Ao contrário da modalidade anterior, a estrutura de repetição com teste no final do bloco obrigatoriamente executará ao menos uma vez os comandos do bloco antes de realizar o teste expresso na condição de parada. Isso se dá por causa da colocação deste teste no fechamento do bloco. Esta modalidade de estrutura de repetição é implementada pelo comando “faça... enquanto” que, conforme já mencionado, é especialmente útil quando você precisa garantir que um bloco de código seja executado pelo menos uma vez, independentemente da condição. Posteriormente, a condição é ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 43 verificada, e se for verdadeira, o bloco é repetido. Caso contrário, a execução continua após o bloco. O comando que utilizaremos nesta modalidade terá o seguinte formato geral: Faça <aqui são escritos os comandos que serão executados enquanto a condição de parada não for satisfeita.> Enquanto <teste que expressa a condição de parada> Além de implementar a condição de parada após a execução no bloco de comandos, esta modalidade de comando de repetição é também utilizada em ocasiões em que o criador do algoritmo não tem como saber antecipadamente quantas repetições serão dadas. Recorreremos ao exemplo anterior para ilustrar esta ideia. Novamente, o algoritmo deve permitir a entrada das notas dos alunos para fins de cálculo da média, enquanto não for digitado o valor -1. Em notação algorítmica e usando o teste no final do bloco, temos: Faça leia (nota) <aqui será feita a soma das notas lidas, a fim de que se possa calcular a média fora do loop.> Enquanto nota != -1 Note que a leitura da nota no exemplo do item 4.2.1 é feita fora do bloco de repetição, ao passo que a leitura neste último exemplo é feita no interior do bloco. Para que a condição de parada no primeiro exemplo possa ser testada, um valor para a variável nota deveria estar carregado. Já para testes no final do bloco, a leitura pode ser feita no corpo de comandos que compõem o bloco de repetição. Embora extremamente úteis, estas duas modalidades de repetição não permitem que o criador do algoritmo conheça a quantidade de repetições que serão feitas, exceto pela implementação de uma variável de controle. Há, no entanto, uma terceira modalidade, cuja característica principal é a de realizar, nativamente, o controle da quantidade de repetições a serem feitas. Vamos conhecê-la? ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 44 4.2.3 Quantidade definida de repetições É comum que parte do problema a ser resolvido pelo algoritmo inclua uma quantidade fixa de repetições de um certo trecho de código. Nestes casos, portanto, o criador do algoritmo saberá antecipadamente a quantidade de repetições a serem feitas. O cálculo da média entre 10 notas, a busca pelo maior entre 5 números e a soma de 4 distâncias compõem algumas situações para este caso, cuja implementação se dá por meio da estrutura “Para...Faça”. Este comando será usado, então, para criarmos loops com um número fixo de iterações e, para que isto seja possível, o próprio comando permitirá que especifiquemos um valor inicial, a condição de parada e o valor final da variável de controle. O comando que utilizaremos nesta modalidade terá o seguinte formato geral: Para i de 1 até 8 faça <aqui são escritos os comandos que serão executados enquanto a variável de controle i não tiver atingido o valor 8> fimPara ISTO ACONTECE NA PRÁTICAConforme já mencionamos, o nome de uma variável é escolha do criador do algoritmo, observadas as regras de composição deste nome. No formato geral do comando “Para”, portanto, a variável colocada logo após o início do comando deveria ser nomeada genericamente, como indicação de que a prerrogativa para a atribuição de um nome é do criador do algoritmo. Por qual motivo, então, decidimos colocar explicita e especificamente a variável i neste comando? Na prática, o nome de variável i é tão utilizada neste comando que se tornou quase um padrão. Note que a escolha do nome desta variável de controle ainda é sua, mas certamente seu algoritmo será mais facilmente entendido (e mantido) com a observância desta regra não declarada. Um exemplo possível para a utilização desta estrutura será desenvolvido na sequência e envolve a leitura de 6 notas, com a apresentação da soma entre elas. Observe que, neste caso, o problema especifica a leitura de exatamente 6 notas, o que atrela a condição de parada ao atingimento deste valor na variável de controle i. O usuário ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 45 do algoritmo não poderá, portanto, informar qualquer quantidade de notas diferente de seis, pois o código não dará a ele meios para isso. Observe o trecho que segue. Para i de 1 até 6 faça leia (nota) s = s + n fimPara imprima (s) O funcionamento completo deste comando e a abordagem detalhada da condição de parada serão tratados em encontro futuro. Por ora, devemos fixar que o paradigma da programação estruturada oferece meios para repetirmos blocos de código em três maneiras ligeiramente distintas entre si, e que se mostrarão mais ou menos adequadas para a diversidade de situações que nos serão apresentadas. Avancemos, pois, rumo a última estrutura deste encontro. 4.3 Estruturas de seleção ou condicionais Em composição com as estruturas de repetição, as tomadas de decisão serão peças fundamentais na construção dos nossos algoritmos. De acordo com determinada condição expressa no código, o algoritmo poderá realizar um ou outro cálculo, exibir uma ou outra mensagem e, de modo genérico, apresentar um ou outro resultado. Pela própria maneira como esta última sentença foi construída, é possível entender que um comando condicional permite ao fluxo de execução tomar um caminho entre dois (ou mais) possíveis, implementando desta forma uma decisão no código. Embora existam três principais modalidades de implementação de condicionais, abordaremos aqui apenas duas delas, resumidamente. 4.3.1 Estrutura condicional simples O comando que implementa esta estrutura realiza o teste em uma expressão lógica. No caso de este teste retornar verdadeiro, um ou mais comandos serão executados. O formato geral do comando que implementa a estrutura condicional simples é o que segue: Se (expressão_lógica) então <bloco que será executado se expressão_lógica retornar verdadeiro> FimSe ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 46 Sobre este comando temos duas observações importantes por ora: a primeira é que “expressão_lógica” nada mais é do que uma expressão que compara uma variável com outra ou uma variável com uma constante, usando um dos seis operadores lógicos que estudamos em encontro anterior. Estes operadores são ==, >=, <=, >, < e !=. A segunda observação remete ao bloco de comandos que será executado no caso de a expressão lógica retornar verdadeiro. Ele pode ser formado por um ou vários comandos, incluindo outros comandos “se”. O algoritmo que segue ilustra uma possível utilização deste comando: 1 Algoritmo ExemploCondicionalSimples 2 Var 3 numero: Inteiro 4 Inicio 5 Escreva(“Informe um número inteiro: “) 6 Leia(numero) 7 Se (numero % 2 = 0) Então 8 Escreva(“O número “, numero, “ é par.”) 9 FimSe 10 Fim Este código permite a entrada de um número inteiro e a avaliação se este número é par, por meio da utilização do operador que retorna o resto da divisão entre dois inteiros. Observe que o bloco de comandos que será executado em caso de retorno verdadeiro da expressão lógica é composto por apenas um comando, algo perfeitamente válido na construção do algoritmo. Embora estejamos diante de um comando completo e funcional, ainda há algo não respondido sobre ele: se a expressão lógica retornar falso, o que será executado? Neste caso, o retorno falso do teste fará com que o comando seguinte ao bloco seja executado. Ocorre, no entanto, que o criador do algoritmo frequentemente deseja criar um fluxo alternativo quando a expressão retornar falso, algo que esta modalidade de condicional não permite. A solução será descrita no próximo item. 4.3.1 Estrutura condicional composta Esta modalidade de comando condicional prevê a execução de um ou mais comandos no caso de a expressão lógica colocada no comando retornar falso. O formato geral do comando fica assim: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 47 Se (expressão_lógica) então <bloco que será executado se expressão_lógica retornar verdadeiro> Senão <bloco que será executado se expressão_lógica retornar falso> FimSe Na prática, a palavra “senão” e o respectivo bloco foram acrescentados ao comando, o que o torna mais completo ainda. Observe exemplo anteriormente desenvolvido, agora com o comando condicional composto: Algoritmo ExemploCondicionalComposto Var numero: Inteiro Inicio Escreva(“Digite um número: “) Leia(numero) Se (numero % 2 = 0) Então Escreva(“O número “, numero, “ é par.”) Senão Escreva(“O número “, numero, “ é ímpar.”) FimSe Fim Ficamos por aqui neste encontro. Em aulas futuras trataremos destas estruturas em detalhes com muitos exemplos. Até a próxima! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 48 CAPÍTULO 5 FORMAS DE REPRESENTAÇÕES DOS ALGORITMOS Aqui estamos em nosso quinto encontro e é chegada a hora de conversarmos sobre as principais formas de representarmos os algoritmos. Como você bem se lembra, um algoritmo é uma sequência de passos finitos e ordenados, que levam a resolução de determinado problema. Para que isso seja possível, o algoritmo deve ser capaz de receber entradas, realizar processamentos e oferecer resultados. Embora este conceito não estabeleça um meio único e padronizado de escrita dos algoritmos, as boas práticas trataram de consagrar alguns formatos que hoje são universalmente aceitos e sobre eles colocaremos nossa atenção nas próximas seções. Por isso, apesar de já termos escrito os nossos primeiros algoritmos, será a partir deste encontro que você poderá contar com outros meios para descrever como um problema deverá ser resolvido. Sigamos adiante! 5.1 Linguagem natural A representação em linguagem natural é a forma mais simples e intuitiva de descrever um algoritmo. Ela usa palavras e frases escritas em um idioma humano para explicar passo a passo a resolução de um problema por algoritmo. Embora seja fácil de compreender, a linguagem natural apresenta um problema que poderá desencorajar sua utilização, especialmente em algoritmos mais complexos: ao descrever uma sequência de passos em linguagem natural, você estará submetido a pouca (ou nenhuma) regra para composição desses passos, o que certamente resultará em comandos ambíguos ou formulados sem um padrão previamente determinado. Apesar deste inconveniente, o uso da linguagem natural para descrever um algoritmo ainda constitui um meio eficaz de comunicação para que profissionais de diferentes áreas possam entender e colaborar em projetos que envolvam algoritmos. Afinal, nesta forma de representação, o rigor na escrita dos comandos e expressões é deixado em segundo plano. Quando descrevemos algoritmos em linguagem natural estamos traduzindo conceitos muitas vezes abstratos e cheios de jargõestécnicos em termos ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 49 mais acessíveis. Isso torna a compreensão dos algoritmos mais fácil para um público diversificado, incluindo pessoas que não têm conhecimento em programação. Um exemplo de algoritmo descrito em linguagem natural poderá esclarecer mais. Nossa missão será a de calcular e exibir a média entre quatro valores inteiros dados como entrada. Uma descrição viável dos passos é a que segue: 1. Inicialize duas variáveis: soma e quantidade com o valor 0. A variável soma será usada para acumular a soma dos números, e a variável quantidade para contar quantos números estão no conjunto. 2. Para cada número na lista: a. Adicione o número à variável soma. b. Incremente a variável quantidade em 1. 3. Após processar todos os números informados pelo usuário, divida a variável soma pelo valor de quantidade. Isso resultará na média. 4. A média calculada no passo 3 é a saída do algoritmo. Retorne esse valor. Embora a sequência seja compreensível e os passos sejam efetivos no cálculo da média, este mesmo algoritmo poderia ser escrito de muitas outras formas, mesmo com mínimas variações entre elas. Apesar de um algoritmo ser, por excelência, uma ferramenta objetiva, há espaço para subjetividades no que acabamos de escrever. Note também que a variável que contém a média não foi “declarada” antes do seu uso, fato que pode passar despercebido para a maioria. Em uma escrita formal, esta falha teria sido mais facilmente detectável. Que tal, então, incluirmos algumas regras nesta linguagem e torná-la mais objetiva? 5.2 Pseudocódigo Antes de tratarmos especificamente desta modalidade de representação de um algoritmo, é necessária uma ressalva ao termo que a identifica. O prefixo “pseudo” aplicado neste contexto nos informa que, embora estejamos diante de uma codificação apta a descrever uma sequência de passos, ela não poderá ser usada para implementar nossos algoritmos para serem executados por um computador. Apesar desta questão, a utilidade desta modalidade está absolutamente resguardada. O pseudocódigo é, portanto, uma linguagem intermediária que combina elementos da linguagem natural ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 50 e da programação. Ele usa instruções simplificadas que se assemelham a código real, mas são mais fáceis de entender. O pseudocódigo é amplamente usado para planejar algoritmos antes que o código em linguagem de programação seja efetivamente escrito e será por meio desta modalidade que expressaremos as resoluções dos problemas que se apresentarão. Sabedores das vantagens da utilização do pseudocódigo, alguns desenvolvedores criaram ferramentas que permitem a execução de um algoritmo escrito em código que, a rigor, não seria executável. Uma dessas ferramentas se chama VisulAlg e ela será nosso ponto de apoio para a criação dos algoritmos. ISTO ESTÁ NA REDE A versão atual da ferramenta VisuAlg pode ser baixada gratuitamente no site disponível em https://visualg3.com.br/. Além de obter o aplicativo, neste site você poderá também informações a respeito dele. Em determinado trecho do texto explicativo, o autor refere-se ao pseudocódigo implementado pela ferramenta como “Portugol”, palavra que junta “Português” com “Algoritmo”. Além disso, o autor explica se tratar de “um programa de livre uso e distribuição GRÁTIS, e DOMÍNIO PÚBLICO, usado para o ensino de lógica de programação em várias escolas e universidades no Brasil e no exterior”. Faça o download e instale-o em seu computador. Como usaremos pseudocódigo (ou Portugol) na construção de nossos algoritmos, deixaremos a formalização das regras de criação de um código para oportunidades em que estivermos desenvolvendo exemplos práticos. Vale desde já, no entanto, a menção de que os principais comandos, expressões, operadores, variáveis, estruturas de dados e outros recursos disponíveis em uma linguagem de programação devem contar com seu correspondente no conjunto de recursos da pseudolinguagem. Na sequência serão mencionados alguns dos principais recursos de construção de algoritmos disponibilizados pelo VisuAlg. 1. Início e fim de algoritmo: Algoritmo e FimAlgoritmo são os marcadores para início e fim de um algoritmo. https://visualg3.com.br/ ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 51 2. Variáveis: a área de declaração de variáveis começa com a palavra Var, seguida do nome_da_variavel : tipo_da_variavel. 3. Entrada e saída de dados: são funções viabilizadas pelos comandos Leia() e Escreva(), respectivamente. 4. Comando condicional: uma das estruturas condicionais (normalmente a mais utilizada de todas) é implementada pelo comando Se..Então..Senão. 5. Comandos de repetição: o comando Enquanto..Faça implementa o laço de repetição com teste no início. Já o comando Faça..Enquanto implementa o laço de repetição com teste no final. Por fim, o comando Para..De..Até implementa laços com quantidade determinada de repetições. Antes de avançarmos rumo à próxima representação, vale uma provocação: será que só de texto vivem nossos algoritmos? Não haveria, talvez, um meio gráfico de representação de uma sequência de passos? 5.3 Fluxograma Embora você possa encontrar essa modalidade com os nomes de diagrama de fluxo e diagrama de blocos, trataremos todos eles como sinônimos. Para os nossos objetivos, eventuais pequenas diferenças conceituais entre essas modalidades serão absorvidas. De qualquer maneira, um fluxograma representa a lógica presente em um algoritmo usando símbolos gráficos. De forma simplificada, comandos e ações presentes nos algoritmos são representados por símbolos padronizados. Um retângulo, por exemplo, representa genericamente uma ação; um losango é usado para representar decisões e setas servem para mostrar a sequência de execução. Observe a tabela 5.1. Ela contém o nome e a respectiva aplicação de cada símbolo padronizado de um fluxograma. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 52 Símbolo Nome Aplicação Terminador Representa o início e o fim do algoritmo. Processamento Representar qualquer tipo de processo, incluindo o processamento de uma expressão aritmética, por exemplo. Linha Representa o fluxo do dado no algoritmo. Entrada manual Representa a entrada de um ou mais dados pelo teclado. Exibição Representa a exibição de um resultado em tela. Decisão Representa uma decisão. A depender do resultado do teste descrito no interior da figura, o fluxo do algoritmo seguirá um ou outro caminho. Tabela 5.1 – Principais símbolos usados em fluxogramas Fonte: adaptado de Souza (2013, p. 88) Esta modalidade tende a facilitar a compreensão da lógica implementada no algoritmo, já que se utiliza de símbolos padronizados para representar a sequência de passos que irá resolver o problema proposto. Para melhor ilustrar a utilidade deste recurso, tomemos como exemplo um algoritmo desenvolvido em nosso encontro anterior. O código, que nos é reapresentado logo abaixo, verifica se um número inteiro informado pelo usuário é par ou ímpar. Algoritmo ExemploCondicionalComposto Var numero: Inteiro Inicio Escreva (“Digite um número: “) Leia (numero) Se (numero % 2 == 0) Então Escreva (“O número “, numero, “ é par.”) Senão Escreva (“O número “, numero, “ é ímpar.”) FimSe Fim Usando a notação de um fluxograma, o mesmo algoritmo é descrito como se observa na figura 5.1: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 53 Figura 5.1: Fluxograma de verificação de número par ou ímpar Fonte: o autor. Embora haja quem possa argumentar que o espaço ocupado pelas duas representações é o mesmo, a proposta do fluxograma nos parece mais simples neste caso. O texto inserido no interior dossímbolos tende a ser breve, já que o próprio símbolo que o contém representa o comando. Observe, por exemplo, o segundo símbolo do fluxograma: pelo fato de representar uma entrada de dado, não será necessário (e tampouco correto) escrever o comando Leia() em seu interior. O mesmo ocorre com o losango, que representa um comando condicional. A simplicidade de um fluxograma frequentemente funciona como aliado no entendimento do problema, fato que tentaremos demonstrar também neste segundo exemplo. O problema agora inclui a leitura de quatro notas, o cálculo da média entre elas e a indicação de aprovação ou não do aluno, segundo o critério de que notas maiores ou iguais a 7,0 significam aprovação. Vejamos a resolução em pseudocódigo: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 54 Algoritmo CalculoMedia Var nota1, nota2, nota3, nota4, media: flutuante Inicio Escreva (“Digite a primeira nota: “) Leia (nota1) Escreva (“Digite a segunda nota: “) Leia (nota2) Escreva (“Digite a terceira nota: “) Leia (nota3) Escreva (“Digite a quarta nota: “) Leia (nota4) media = (nota1 + nota2 + nota3 + nota4) / 4 Se (media >=7) Escreva(“Aluno aprovado.”) Senao Escreva(“Aluno não aprovado.”) Fim Nesta forma de representação, as declarações de variáveis e mensagens que antecedem as entradas de dados são explicitamente colocadas no código, bem como os comandos em cada linha. Na figura 5.2 é possível observar o código representado por um fluxograma. Detalhes relacionados a mensagens de entrada e declaração de variáveis são omitidos, o que tende a simplificar a representação da resolução do problema. Assim como no exemplo anterior, o algoritmo é finalizado após a exibição de uma das mensagens dirigidas ao usuário. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 55 Figura 5.2: Fluxograma de apuração de aprovação de um aluno. Fonte: o autor. Embora a utilidade e a importância do fluxograma sejam evidentes, essa modalidade de representação pode não dar ao criador do algoritmo o suporte necessário e imediato para transformar uma solução em código apto a ser executado por um computador, justamente pela supressão de detalhes que caracteriza um fluxograma. Nosso próximo item aborda, em linhas gerais, as linguagens de programação como meio de representação de um algoritmo. Mais até do que representá-lo, uma linguagem de programação torna-o real e factível, ao custo da observância de muitas regras de composição de código. 5.4 Linguagem de programação Uma linguagem de programação é capaz de fornecer comandos, variáveis, expressões e todos os recursos necessários para que um algoritmo possa ser implementado e ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 56 executado pela máquina. Em outras palavras, linguagens de programação, como C, Python e Java, são capazes de fornecer representação diretamente por código aos algoritmos. Da mesma forma descrita para algoritmos, a programação estruturada usa estruturas sequenciais, condicionais e de repetição para descrever e implementar a lógica do algoritmo. Esta é, portanto, a forma mais direta de representação de um algoritmo. Embora o objeto deste encontro não seja o de abordar em detalhes uma linguagem de programação, vale o investimento do nosso tempo na breve descrição de algumas das linguagens mais usadas em nosso tempo e na caracterização de elementos que compõem uma linguagem. De acordo com Melo e Silva (2014), uma linguagem de programação é um conjunto de recursos que podem ser compostos para construir programas específicos, mais um conjunto de regras de composição que garantem que todos os programas podem ser implementados em computadores. Ainda segundo os autores, para atingir seus objetivos, os elementos de uma linguagem devem ter significados únicos, o que nos leva a mais duas definições: Sintática: como é escrito cada um dos elementos da linguagem Semântica: o que significa cada um dos elementos da linguagem. Estes conceitos nos remetem ao rigor necessário para que um programa seja escrito corretamente e execute as funcionalidades conforme pretendido. O criador do programa – frequentemente chamado desenvolvedor – deve respeitar a sintática da linguagem ao escrever os comandos e demais elementos que compõem o código. Uma palavra escrita incorretamente simplesmente impedirá que o programa seja executado. Por outro lado, mesmo que um comando seja escrito com todo rigor sintático, ele falhará em executar determinada função se houver engano em relação a sua funcionalidade. Em outras palavras, o desenvolvedor imagina um resultado e obtém outro, justamente por desatenção à semântica. Como não poderia deixar de ser, as principais linguagens de programação atuais refletem as demandas da indústria de tecnologia e a evolução das necessidades de desenvolvimento de software. Aqui estão quatro das linguagens mais relevantes no cenário atual: 1. Python: trata-se de uma das linguagens de programação mais populares e versáteis. É conhecida por sua legibilidade e simplicidade, o que a torna uma ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 57 escolha bem interessante para iniciantes. Python é amplamente utilizado em desenvolvimento web, análise de dados, aprendizado de máquina e automações. De acordo com o site oficial (Python, 2023), trata-se de uma linguagem de fácil aprendizagem, tanto para programadores iniciantes como para desenvolvedores com experiência em outras linguagens. 2. Java: linguagem de programação conhecida por sua portabilidade e ampla adoção entre desenvolvedores. É usada em aplicativos móveis (Android), desenvolvimento de servidores e aplicações empresariais. 3. C++: trata-se de uma extensão da linguagem C e é amplamente utilizada em programação de sistemas, jogos e desenvolvimento de software de alto desempenho. Quando você precisar criar uma aplicação que demande rapidez, o C++ será uma escolha quase imediata. 4. C#: conhecida como “C Sharp”, esta linguagem foi desenvolvida pela Microsoft. Ela é amplamente usada para desenvolver aplicativos Windows, jogos com a Unity e aplicativos empresariais. Ela se destaca pela integração com a plataforma .NET. Vale a ressalva de que estas são apenas algumas das principais linguagens de programação atuais, e a escolha dependerá do contexto, dos requisitos do projeto e da familiaridade dos desenvolvedores envolvidos. Este foi o conteúdo que preparamos para este encontro. É importante mencionar que cada uma das formas de representação que abordamos tem suas vantagens e é escolhida de acordo com o contexto em que o algoritmo estará inserido, incluindo o público que deverá compreendê-lo. A escolha da representação adequada é fundamental para garantir que o algoritmo seja compreendido e implementado corretamente. Até a próxima! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 58 CAPÍTULO 6 ESTRUTURAS CONDICIONAIS Você já refletiu sobre os recursos que utiliza para tomar uma decisão? Sua experiência de vida, seus hábitos e as questões circunstanciais específicas certamente exercem efeito em seus julgamentos e decisões, o que torna o acerto ou o erro mais dependente de influências subjetivas do que objetivas. Em outras palavras, na maioria dos casos, acertar ou errar numa decisão não é questão exata para nós humanos. Contudo, os algoritmos não contam com os mesmos “recursos mentais” dos quais nós dispomos para tomar um caminho ou outro. Ao contrário disso, são os resultados de expressões condicionais que darão base para as decisões que o criador do algoritmo implementar. Neste capítulo abordaremos as várias modalidades de comandos condicionais, que constituem as portas de entrada para tomadas de decisão em um algoritmo. Reforçamos, pois, que asestruturas condicionais são fundamentais na criação de um algoritmo, pois permitem a tomada de decisões com base em condições específicas. Com isso, estas estruturas desempenham papel crítico na lógica de controle de fluxo de um algoritmo e suas modalidades conferem flexibilidade e poder ao criador do algoritmo. Por isso, trataremos dos comandos condicionais aqui em profundidade maior do que fizemos em encontro anterior, o que inclui o desenvolvimento de novos exemplos. 6.1 Estrutura condicional simples Como o próprio nome sugere, esta estrutura contempla o comando mais básico de todos. Objetivamente, ela avalia uma condição e executa um bloco de código somente se a condição avaliada for verdadeira. Conforme já introduzido em encontro anterior, o formato geral do comando “Se” em pseudolinguagem para este caso é o que segue: Se (teste_condicional) Então <comandos que serão executados caso teste_condicional retorne verdadeiro> FimSe ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 59 Conforme nos ensinam Souza et al. (2013), esta estrutura é representada por um comando que avalia uma expressão lógica, resultando um valor que pode ser verdadeiro ou falso. A depender do resultado desta expressão, o fluxo do algoritmo seguirá por um entre dois caminhos: em caso de retorno verdadeiro, será executado o bloco subordinado ao comando “Se”. Em caso de retorno falso, será efetuado um desvio sem comando algum. Os mesmos autores nos oferecem o formato geral do comando em um fluxograma: Figura 6.1: Estrutura condicional Se..então Fonte: Souza et al. 2013, p. 127. Como exemplo de aplicação imediata, imagine um jogo em que o competidor ganhará determinado prêmio apenas se atingir uma certa pontuação. A utilização da estrutura condicional simples, implementada pelo comando se..então.., é bem adequada para a situação. Se a pontuação for atingida, o prêmio é concedido. Nenhum comando específico será executado, no entanto, se a pontuação não for atingida. Considerando que a pontuação necessária para a conquista do prêmio seja 1000, o trecho de código que implementa essa ideia é o que segue: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 60 Se (ponto >=1000) Então conceder_premio FimSe Embora estejamos diante de um trecho simples, duas observações devem ser feitas: a) Não haverá ação no corpo do comando caso o teste condicional retornar falso. Em outras palavras, nenhum outro comando subordinado ao “Se” será executado caso a pontuação não seja maior ou igual a 1000. Esta, aliás, é a característica mais forte desta estrutura. b) “conceder_premio” remete a um conjunto de comandos escritos pelo criador do algoritmo. Neste caso específico, a expressão apenas representa os comandos que efetivarão a concessão do prêmio. Estes comandos não nos interessam por ora. O desenvolvimento de um exemplo completo em pseudocódigo será útil para a compreensão definitiva do comando. O algoritmo que segue permite a leitura de três números inteiros e retorna quantos deles são divisíveis por 3. Algoritmo ExemploCondicionalSimples Var n, c=0: inteiro // a variável n receberá inteiros // a variável c será usada como contador Inicio Escreva (“Informe o primeiro valor: “) Leia (n) Se (n % 3 == 0) Então c=c+1 FimSe Escreva (“Informe o segundo valor: “) Leia (n) Se (n % 3 == 0) Então c=c+1 FimSe Escreva (“Informe o terceiro valor: “) Leia (n) Se (n % 3 == 0) Então c=c+1 FimSe Escreva (“Foram informados “, c, “ valores divisíveis por 3”) Fim ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 61 Note que a variável n recebe, por meio de leitura, 3 valores. O primeiro valor é lido e, na sequência, submetido a um teste lógico para que sua divisibilidade por 3 seja apurada, por meio da aplicação do operador módulo. Se o teste retornar verdadeiro (ou seja, se o número lido for divisível por 3), então a variável c é incrementada em 1. No entanto, se o teste retornar falso, a variável c não passa pelo incremento e o comando “Se..então” é encerrado sem nenhuma outra ação. Como são necessárias três leituras, o processo é repetido por três vezes. Note que não foram declaradas - tampouco usadas - três variáveis para as leituras. Ao invés disso, apenas uma variável recebe o valor informado pelo usuário, por três vezes. Essa implementação é possível por ser desnecessário exibir os valores lidos. A cada nova leitura, o valor que ocupa a variável é substituído por outro. Contudo, antes que isso ocorra, a apuração da divisibilidade e eventual incremento no contador acontecem. ISTO ACONTECE NA PRÁTICA O algoritmo que acabamos de desenvolver poderia ser escrito de modo diferente e, mesmo assim, fornecer solução para o problema? Neste caso específico, a resposta é sim. A critério do criador do algoritmo, três variáveis distintas poderiam ser lidas e, com os valores carregados, passarem individualmente pelos testes lógicos no comando “Se..então”. Ainda tratando especificamente deste algoritmo, e considerando sua implementação em uma linguagem de programação, a diferença de utilização de recursos computacionais entre os dois códigos seria absolutamente imperceptível. No entanto, desenvolvedores de grandes sistemas empenham-se em tornar o algoritmo o mais eficiente possível, a fim de tornar seus programas mais eficientes e rápidos. Neste contexto, qualquer utilização dispensável de variável ou de recurso algorítmico pode ser decisivo. Embora útil, esta estrutura tem utilização relativamente restrita. No caso concreto, é mais comum que o criador do algoritmo esteja diante de um problema que demande também a execução de comandos no caso de a expressão lógica (ou teste lógico) retornar falso. Para esta situação, claramente mais frequente, temos disponível o comando Se..então..senão. Vamos a ele, pois! 6.2 Estrutura condicional composta A chamada estrutura condicional composta é efetivada pelo comando Se..então.. senão. Ele permite que o algoritmo execute um bloco de código se o teste da condição ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 62 retornar verdadeiro e outro bloco se retornar falso. Isso é útil para cenários em que deve ser implementada alternativa ao retorno verdadeiro. De fato, por meio desta estrutura o algoritmo escolherá um caminho entre dois possíveis. A exemplo da estrutura anterior, o formato geral deste comando também já foi introduzido, mas será novamente abordado. Vejamos: Se (teste_condicional) Então <comandos que serão executados caso teste_condicional retorne verdadeiro> Senão <comandos que serão executados caso teste_condicional retorne falso> FimSe Este formato geral nos indica que, se o retorno do teste lógico for verdadeiro, os comandos dispostos logo após o Então serão executados. No entanto, se o retorno do teste for falso, os comandos colocados logo após o Senão é que serão executados. Em absolutamente nenhum caso os dois blocos serão executados na mesma execução do algoritmo. A ideia da estrutura condicional composta também pode ser representada em fluxograma, conforme segue: Figura 6.2: Estrutura condicional Se..então..senão Fonte: Souza et al. 2013, p. 128. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 63 O desenvolvimento de um exemplo deverá revelar na prática o funcionamento da estrutura. O trecho de código que segue realiza teste e processamento na nota de um aluno. Inicio Escreva (“Informe a nota do aluno: “) Leia (nota) Se (nota >= 7.0) Então inicio Escreva (“Aluno aprovado.”) contador = contador + 1 somador = somador + nota1 fim Senão Escreva(“Aluno reprovado.”) FimSe Fim Sobre este trecho cabe uma análise mais detalhada: o código lê a nota doaluno e, se o valor lido for maior ou igual a 7, o contador de notas maiores que sete (implementado na variável contador) será incrementado em 1. Na sequência, o somador de notas maiores ou iguais a 7 será incrementado pelo valor da nota, o que configura uma expressão que acumula valores por meio de somas. Embora este trecho fizesse mais sentido se inserido em um comando de repetição, ele ainda assim nos revela dois fatos importantes: A) Há três comandos que serão executados caso o teste nota>=7.0 retorne verdadeiro. Estes três comandos constituem um bloco, que foi delimitado por inicio e fim no código. Em havendo retorno verdadeiro, os três comandos serão executados, necessariamente. B) Há apenas um comando no bloco do “Senão”, o que faz cair a obrigatoriedade da delimitação de bloco. Bem, neste ponto nos parece claro a forma e a função de uma estrutura condicional, seja ela simples ou composta. No entanto, uma pergunta ainda permanece: e se o criador do algoritmo tiver que implementar uma estrutura em que deva escolhido um caminho entre vários possíveis? Há recurso para isso? Continue por aqui. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 64 6.3 Estrutura condicional aninhada A estrutura condicional aninhada é uma técnica de criação de algoritmos que permite a criação de condicionais dentro de outras condicionais. O termo “aninhada” nos remete a algo abrigado em um ninho ou uma coisa recolhida no interior de outra. A ideia, portanto, é que um comando condicional seja colocado no interior de outro e tal recurso é útil quando precisamos avaliar várias condições diferentes em um algoritmo. Em atendimento à questão feita antes do início desta seção, trata-se do recurso que irá permitir a escolha de um entre vários caminhos possíveis, ou seja, quando as decisões do programa dependem de múltiplas variáveis ou situações. Em termos práticos, a estrutura condicional aninhada é composta por uma série de blocos condicionais e efetivada pelo uso de comandos “Se”, “Senão Se” e “Senão”, que determinam o fluxo do programa com base em várias condições. Usando pseudocódigo, um formato possível para esta modalidade de estrutura condicional é o que segue: Se (condição1) Então // Bloco de código a ser executado se a condição1 for verdadeira Senão Se(condição2) Então // Bloco de código a ser executado se a condição2 for verdadeira Senão // Bloco de código a ser executado se a condição2 for falsa FimSe FimSe O bloco condicional mais externo (ou seja, o escrito em primeiro lugar) verifica a “condição1”. Se essa condição retornar verdadeiro, o bloco de código dentro do primeiro “Se...Então” é executado. Aninhado a este primeiro bloco encontra-se outro comando condicional “Se...Então...Senão”, que verifica a “condição2”. Se a condição2 também for verdadeira, o bloco de código dentro desse segundo comando condicional é executado. Caso contrário, o bloco de código no “Senão” desse segundo comando condicional é executado. No final é incluído um bloco de código no “Senão” do primeiro comando condicional externo, a fim de especificar os comandos que serão executados se a “condição1” não for verdadeira. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 65 Este formato permite criar estruturas de decisão aninhadas, onde as ações a serem executadas dependem de várias condições diferentes. Trata-se de uma ferramenta poderosa para lidar com casos mais complexos em algoritmos. Na notação de fluxograma, temos: Figura 6.3: Estrutura condicional Se..então..senão aninhada. Fonte: o autor. Imagine que tenha sido confiada a você a missão de criar um algoritmo que permita calcular o valor do frete para entrega de produtos com base na distância percorrida e no peso dos produtos. A própria natureza deste problema nos remete a múltiplas combinações entre distância percorrida e peso transportado, o que sugere a utilização de estrutura condicional que permita a escolha de uma entre várias opções. Observe o código que segue: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 66 Algoritmo CondicionalAninhada Var distancia, peso, frete: flutuante Inicio Escreva (“Informe a distância (em km): “) Leia (distancia) Escreva(“Informe o peso (em kg): “) Leia (peso) Se (distancia <= 100) Então Se (peso <= 200) Então frete = 10 + 2 * distancia Senão frete = 10 + 3 * distancia FimSe Senão Se (peso <= 200) Então frete = 20 + 1.5 * distancia Senão frete = 20 + 2.5 * distancia FimSe FimSe Escreva(“O valor do frete é: R$”, Frete) Fimalgoritmo Neste exemplo, o algoritmo começa solicitando ao usuário que informe a distância percorrida e o peso do produto a ser entregue. Em seguida, ele utiliza estruturas condicionais compostas aninhadas para calcular o valor do frete com base nos valores informados. Observe que tais valores serão armazenados em variáveis não inteiras, já que as grandezas representadas acomodam valores com casas decimais. A primeira estrutura condicional verifica se a distância é menor ou igual a 100 km. Em caso de retorno verdadeiro, outra condicional é aninhada para avaliação do peso da mercadoria. No primeiro comando “Se..então”, a leitura que devemos fazer é a seguinte: se a distância a ser percorrida for menor ou igual a 100, então o peso será avaliado. Neste caso, se o peso for menor ou igual a 200, então o frete será calculado pela fórmula frete = 10 + 2 * distancia. Se o peso, no entanto, for maior do que 200, o valor do frete será mais alto, e calculado pela fórmula frete = 10 + 3 * distancia. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 67 No entanto, a distância a ser percorrida poderá ser maior do que 100, hipótese em que o teste lógico do primeiro comando “Se..então” retornará falso. Neste caso, o peso novamente será avaliado e, em sendo menor ou igual a 200, frete = 20 + 1.5 * distancia. Com um peso maior do que 200, frete = 20 + 2.5 * distancia. Os critérios de peso, distância e fórmula para cálculo do frete são determinados pelo enunciado do exercício ou, em um caso concreto, pela política da empresa. Para este problema específico, a estrutura condicional aninhada nos pareceu funcionar muito bem. Com relativamente poucos caminhos, as opções são cobertas com necessidade mínima de aninhamento entre comandos. No entanto, o criador do algoritmo poderá se deparar com problemas em que um caminho deverá ser escolhido entre muitos, o que tornará o desenvolvimento do código propenso a erros. Nossa quarta e última estrutura condicional nos oferece solução para este caso, sob determinadas condições. 6.4 Escolha A estrutura condicional “escolha” – muitas vezes referenciada também como “caso” – é parte importante da criação de algoritmos, já que é usada para tomar decisões com base em diferentes valores ou condições. Ela permite que o criador do algoritmo especifique uma variável que será avaliada e, com base no valor dessa variável, o algoritmo executará um bloco de código correspondente a um caso específico. Este recurso é muito conveniente quando a escolha de um caminho entre vários depende da avaliação de um número inteiro e esta característica da estrutura se tornará clara com o desenvolvimento de exemplos. Observe o formato geral do comando em pseudolinguagem: escolha opcao caso <valor_inteiro1> <comando> caso <valor_inteiro2> <comando> caso <valor_inteiro3> <comando> senao escreva (“Opção inválida.”) fimescolha ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 68 A variável opcao conterá um valor inteiro, normalmente informado pelo usuário. O nome da variável é escolha do criador do algoritmoe, para fins apenas de composição do formato geral, escolhemos o nome “opção”. Através da avaliação do valor contido nesta variável, o comando executará um – e apenas um – dos blocos indicados por <comando>. Se o valor da variável opção não for encontrada em nenhum dos “casos”, o algoritmo enviará a mensagem de “Opção inválida” para a tela ou, de modo geral, executará comando que trate uma opção não prevista. O formato geral da estrutura, expresso em um fluxograma, é assim constituído: Figura 6.4: Estrutura de decisão caso Fonte: Souza et al. 2013, p. 129. Observe que esta estrutura evita uso excessivo de aninhamentos, o que certamente será muito conveniente em caso de muitos caminhos a serem avaliados para a escolha de apenas um deles. Normalmente será possível substituir a estrutura “escolha” por ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 69 estrutura condicional aninhada, o que normalmente não será vantajoso para fins de entendimento e manutenção do código. No entanto, Um exemplo apropriado para o caso Algoritmo CondicionalEscolha Var opcao: inteiro Inicio escreva(“Escolha uma opção (1, 2 ou 3): “) leia(opcao) escolha opcao caso 1 escreva(“Você escolheu a opção 1.”) caso 2 escreva(“Você escolheu a opção 2.”) caso 3 escreva(“Você escolheu a opção 3.”) senao escreva(“Opção inválida.”) fimescolha Fimalgoritmo ANOTE ISSO A escolha da estrutura condicional depende da situação. A clareza do código e a eficiência são fatores a serem considerados. A utilização excessiva de aninhamento de estruturas condicionais deve ser evitada, pois pode tornar o código difícil de ler e manter. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 70 CAPÍTULO 7 ESTRUTURA DE REPETIÇÃO COM TESTE NO INÍCIO Iniciamos neste encontro uma sequência de capítulos dedicados às três modalidades de comandos de repetição. Vale sempre a lembrança de que a programação estruturada nos reservou estruturas sequenciais, condicionais e de repetição como recursos para desenvolvermos nossos algoritmos e, na condição de componentes importantes destes recursos, cada uma das três modalidades será abordada em profundidade e de modo particularizado. Em encontro anterior a este tivemos a oportunidade de introduzir o funcionamento de uma repetição de trecho de código, ressaltando as características de cada modalidade e as possíveis utilizações de cada uma. Naquela oportunidade, o conceito e a aplicação da condição de parada de uma repetição também foram abordados. No presente capítulo teremos a oportunidade de investigar as particularidades da estrutura em que a condição de parada é colocada antes dos comandos que serão repetidos. Sigamos adiante! 7.1 Conceitos e aplicações A efetivação de um teste lógico no início do comando de repetição justifica o apelido de “loop com teste no início” para esta estrutura. Em termos objetivos, a estrutura de repetição com teste no início funciona assim: antes de executar o bloco de código dentro do loop (ou do laço), é feito um teste. Se ele retornar verdadeiro, o código contido no bloco é executado; caso contrário, o fluxo é desviado para fora do bloco, especificamente para o primeiro comando depois dele. Uma ilustração possível para este mecanismo inclui a contagem de votos para representante discente no colegiado de um determinado curso e a elaboração da solução deste problema será feita em etapas, de modo que refinamentos sucessivos sejam obtidos em cada uma delas. Através desta eleição, apenas um entre três candidatos será o escolhido e, embora a quantidade de alunos aptos a votarem seja conhecida, não se sabe quantos alunos efetivamente exercerão o direito de voto. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 71 Esta característica do pleito sugere ao criador do algoritmo a utilização de um comando de repetição apropriado para situações em que não se sabe, de antemão, quantas vezes o bloco de comandos será repetido. Em termos mais específicos, não se sabe quantas vezes o algoritmo deverá ler um voto e atribui-lo a um dos candidatos, justamente por não se saber quantos alunos efetivamente exercerão o direito de voto. ANOTE ISSO Embora estejamos ainda analisando mais detalhadamente nossa primeira estrutura de repetição, um registro precisa ser feito: a escolha de uma entre as três modalidades de repetição requer conhecimento das características de cada uma e atenção a detalhes que o problema nos informa. Neste exemplo, o criador do algoritmo não conta com a informação de quantos alunos votarão, fato que o impede de utilizar um comando com quantidade fixa de repetições. A primeira parte da solução nos remete à definição de uma condição de parada de repetições. A opção mais imediata seria a interrupção do laço por atingimento de determinado número de votos, mas essa opção se mostrou inaplicável para o caso. Assim sendo o criador do algoritmo deve dar ao usuário a possibilidade de interromper a contagem de votos quando for necessário. Para a viabilização deste artifício, consideraremos que cada um dos três candidatos será identificado por um número de 1 a 3. Ao entender que a votação terminou, o usuário encarregado do processo digitará -1 e o algoritmo se incumbirá de exibir o total de votos de cada candidato. Antes de prosseguirmos, valem algumas considerações a respeito desta condição de parada. Como se trata de uma convenção estabelecida pelo criador do algoritmo, o valor -1 poderia ser substituído por qualquer outro valor inteiro, desde que diferente de 1, 2 e 3. Além disso, na condição de controlador da interrupção do laço, o valor -1 não pode ser computado como voto de nenhum dos candidatos, fato que demandará nossa atenção durante a escrita do código. O segundo passo em direção à solução se dará por meio da descrição dos passos do algoritmo em linguagem natural estruturada. Mesmo que sujeita a pequenas alterações de desenvolvedor para desenvolvedor, uma descrição possível desses passos é a que segue: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 72 Leia um voto Enquanto voto diferente de -1 faça Inicio_enquanto Se voto diferente de -1 entao Caso voto seja 1, some 1 a voto1 voto seja 2, some 1 a voto2 voto seja 3, some 1 a voto2 Leia um voto Fim_enquanto Escreva a quantidade de votos para o candidato 1 Escreva a quantidade de votos para o candidato 2 Escreva a quantidade de votos para o candidato 3 Observe que, embora conte com uma estrutura lógica adequada ao entendimento dos passos, essa descrição ainda não contempla muitos elementos necessários a uma descrição por pseudolinguagem, incluindo declaração e uso de variáveis. Esta descrição em linguagem natural estruturada fornece base para a nossa próxima etapa do refinamento sucessivo, que é a elaboração do pseudocódigo deste problema, conforme segue: 1 algoritmo “ContagemDeVotos” 2 var 3 voto, cont1, cont2, cont3: inteiro 4 inicio 5 cont1 <- 0 6 cont2 <- 0 7 cont3 <- 0 8 escreva(“Digite o voto (de 1 a 3, ou -1 para encerrar): “) 9 leia(voto) 10 enquanto voto != -1 faca 11 escolha voto 12 caso 1: 13 cont1 = cont1 + 1 14 caso 2: 15 cont2 = cont2 + 1 ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 73 16. caso 3 17 cont3 = cont3 + 1 18 caso contrario 19 escreva(“Voto inválido. “) 20 fimescolha 21 escreva(“Digite o próximo voto (ou -1 para encerrar): “) 22. leia(voto) 23 fimenquanto 24 escreva(“Resultado da votação:”) 25 escreva(“Candidato 1: “, cont1, “ votos”) 26 escreva(“Candidato 2: “, cont2, “ votos”) 27 escreva(“Candidato 3: “, cont3, “ votos”) 28 fimalgoritmo O entendimento completo destealgoritmo passa pela análise do seu código. Na linha 1 temos a indicação do início do algoritmo, com a atribuição de um nome que o identifica. Na linha 3, as variáveis que compõem a solução são declaradas: a variável voto servirá para receber e armazenar o voto dado pelo usuário, que deverá ser expresso com valores entre 1 e 3. Já as variáveis cont1, cont2 e cont3 atuarão como contadores dos votos dados ao candidato 1, candidato 2 e candidato 3, respectivamente. Da linha 5 até a linha 7, os contadores recebem o valor inicial 0, a fim de garantir que a contagem em que atuarão seja exata. O primeiro processamento efetivo do algoritmo se dá na linha 8, e é por ela que o usuário do algoritmo é informado que deverá escolher um número de 1 a 3 para votar. Na linha seguinte, a opção do usuário é efetivamente lida, ou seja, o valor que usuário informar será armazenado na variável voto. Vale a menção de que, ao digitar o valor 1, o usuário atribuirá o voto ao candidato 1; ao digitar o valor 2, o usuário atribuirá o voto ao candidato 2 e, ao digitar o valor 3, o usuário atribuirá o voto ao candidato 3. O comando enquanto..faça na linha 10 e a marcação de fim de bloco da linha 23 (fimenquanto) demarcam o escopo do laço de repetição. Os comandos contidos entre essas duas linhas serão repetidos enquanto o valor informado para o voto seja diferente de -1. Neste ponto, vale a lembrança de que a definição da digitação do valor -1 como condição de parada foi convenção adotada pelo criador do algoritmo, já que ele não dispõe do número exato de votos que serão dados. Observe que a primeira execução do teste lógico voto != -1 é feita com base na leitura executada na linha 9 e, portanto, antes do bloco de repetição. É necessário, ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 74 portanto, que a variável voto contenha um valor válido, a fim de que ele possa ser comparado com a constante -1. O próximo comando após o cabeçalho do comando de repetição nos remete ao tratamento necessário ao conteúdo da variável voto. A fim de executar a contagem dos votos foi usado o comando escolha..caso. Por meio desta estrutura condicional, o algoritmo direcionará a contagem do voto para cont1, cont2 ou cont3 de modo organizado e facilmente legível para um outro eventual criador de algoritmo. O uso de um comando condicional garante que um único voto jamais será atribuído a dois candidatos distintos. Após o fim do comando escolha..caso – e do consequente incremento de 1 em um dos contadores – o algoritmo faz nova leitura do voto, desta vez no interior do laço de repetição. Ao atingir a linha 23, o fluxo do algoritmo é desviado novamente para a linha 10, que executará novamente a comparação entre o valor recém-lido e a constante -1, para fins de efetivação ou não da interrupção do comando de repetição. Quando o usuário informar o valor -1 na leitura do voto, o fluxo do algoritmo se encaminhará para fora do laço de repetição, especificamente para o primeiro comando após o delimitador fimenquanto. Neste ponto, as variáveis cont1, cont2 e cont3 conterão as quantidades de votos atribuídos a cada um dos candidatos e bastará informá-los em tela, o que é feito nas linhas 25, 26 e 27. Embora o desconhecimento prévio da quantidade de repetições seja uma característica marcante dos problemas cuja solução contemplam o comando enquanto..faça, é possível ao criador do algoritmo a determinação da quantidade de repetições do bloco, independentemente da intervenção do usuário. Isso se dá através da utilização de uma condição de parada em que a comparação é feita entre uma variável do tipo contador com uma constante. Observe o exemplo simples que segue: Algoritmo “Enquanto..Faca” var contador: inteiro inicio contador = 1 enquanto contador <= 5 faça escreva(“Contador: “, contador) contador = contador + 1 fimenquanto fimalgoritmo ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 75 Por meio da utilização de uma variável controlada pelo criador do algoritmo na condição de parada, o comando enquanto..faça irá executar exatamente 5 repetições, independentemente da intervenção do usuário. A execução de uma quantidade fixa de repetições é mais convenientemente codificada através de outro comando de repetição, que teremos oportunidade de abordar em encontro futuro. Ficamos por aqui neste capítulo. Até a próxima! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 76 CAPÍTULO 8 ESTRUTURA DE REPETIÇÃO COM TESTE NO FIM Continuamos neste encontro a sequência de três capítulos dedicados às modalidades de comandos de repetição. Conforme já mencionado, a programação estruturada nos reservou estruturas sequenciais, condicionais e de repetição como recursos para desenvolvermos nossos algoritmos e, na condição de componentes importantes destes recursos, cada uma das três modalidades será abordada em profundidade e de modo particularizado. No encontro anterior a este tivemos a oportunidade de esmiuçar o comando de repetição com teste lógico no início do bloco e, para isso, desenvolvemos dois exemplos de sua utilização. Naquela oportunidade, o conceito e a aplicação da condição de parada de uma repetição também foram abordados. No presente capítulo teremos a oportunidade de investigar as particularidades da estrutura em que a condição de parada é colocada no fim do bloco de comandos que serão repetidos. Sigamos adiante! 8.1 Conceitos e aplicações Na estrutura de repetição com teste no final do bloco, o teste lógico é feito no fechamento do bloco de comandos que serão repetidos. Essa particularidade garante que, ao menos uma vez, todos os comandos do bloco serão executados. Em termos objetivos, a estrutura de repetição com teste no final funciona assim: depois de ser executado o bloco de código dentro do loop (ou do laço), é realizado um teste lógico. Se ele retornar verdadeiro, o fluxo do algoritmo é desviado para o início do bloco, que será executado novamente. Se o teste retornar falso, o fluxo de execução é desviado para fora do bloco, especificamente para o primeiro comando depois dele. Assim como fizemos no encontro passado, desenvolveremos um exemplo completo para ilustrar o funcionamento da estrutura que compõe o objeto principal do capítulo. O exemplo começa assim: imagine que você deve criar um algoritmo que ofereça a um professor a possibilidade de, ao informar as notas das provas de seus alunos, obter a média entre elas. Novamente, a quantidade de entradas (ou, especificamente, de ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 77 notas) é desconhecida, o que demanda a determinação de uma condição de parada que não esteja atrelada ao atingimento de uma quantidade de repetições. Esta característica do processo também sugere ao criador do algoritmo a utilização de um comando de repetição apropriado para situações em que não se sabe, de antemão, quantas vezes o bloco de comandos será repetido. Em termos mais específicos, não se sabe quantas vezes o algoritmo deverá ler uma nota e acumulá-la com a soma das anteriores, justamente por não se saber quantos notas efetivamente serão processadas. ANOTE ISSO Estamos analisando mais detalhadamente nossa segunda estrutura de repetição e novamente um registro precisa ser feito: a escolha de uma entre as três modalidades de repetição requer conhecimento das características de cada uma e atenção a detalhes que o problema nos informa. Neste exemplo, o criador do algoritmo não conta com a informação de quantas notas serão lidas, fato que o impede de utilizar um comando com quantidade fixa de repetições. A primeira parte da solução nos remete à definição de uma condição de parada de repetições. A opção mais imediata seria a interrupção do laço por atingimento de determinado númerode votos, mas essa opção se mostrou inaplicável para o caso. Assim sendo, o criador do algoritmo deve dar ao usuário a possibilidade de interromper a contagem de votos quando for da vontade dele. Para a viabilização deste artifício, consideraremos que uma nota jamais será expressa com valor negativo. Ao entender que a entrada de notas terminou, o usuário encarregado do processo digitará -1 e o algoritmo se incumbirá de calcular e mostrar a média entre as notas. Antes de prosseguirmos, valem algumas considerações a respeito desta condição de parada. Como se trata de uma convenção estabelecida pelo criador do algoritmo, o valor -1 poderia ser substituído por qualquer outro valor inteiro, desde que diferente de valores de 1 a 10, já que estes são valores possíveis para composição de uma nota. Além disso, na condição de controlador da interrupção do laço, o valor -1 não pode ser computado como nota, fato que demandará nossa atenção durante a escrita do código. O segundo passo em direção à solução se dará por meio da descrição dos passos do algoritmo em linguagem natural estruturada. Mesmo que sujeita a pequenas alterações de desenvolvedor para desenvolvedor, uma descrição possível desses passos é a que segue: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 78 Faça Leia nota Se a nota for diferente de –1, então Some 1 na quantidade de notas Acumule notas Enquanto a nota digitada for diferente de -1 Calcule a média das notas; Informe a médias das notas Observe que, embora conte com uma estrutura lógica adequada ao entendimento dos passos, essa descrição ainda não contempla muitos elementos necessários a uma descrição por pseudolinguagem, incluindo declaração e uso de variáveis. Esta descrição em linguagem natural estruturada fornece base para a nossa próxima etapa do refinamento sucessivo, que é a elaboração do pseudocódigo deste problema, conforme segue: 1. algoritmo “CalculaMedia” 2. var 3. nota, soma, media: flutuante 4. contador: inteiro 5. inicio 6. soma = 0 7. contador = 0 8. faca 9. escreva(“Digite a nota (ou -1 para encerrar): “) 10. leia(nota) 11. //Verifica nota é válida antes de incluir na média 12. se (nota != -1) entao 13. soma = soma + nota 14. contador = contador + 1 15. fimse 16. enquanto (nota != -1) 17. // Calcula a média 18. se (contador > 0) entao 19. media = soma / contador 20. escreva(“A média das notas é: “, media) 21. senao 22. escreva(“Nenhuma nota válida foi inserida.”) 23. fimse 24. fimalgoritmo ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 79 O entendimento completo deste algoritmo passa pela análise do seu código. Na linha 1 temos a indicação do início do algoritmo, com a atribuição de um nome que o identifica. Nas linhas 3 e 4, as variáveis que compõem a solução do problema são declaradas: a variável nota servirá para receber e armazenar a nota informada pelo professor, que deverá ser expressa com valores entre 0.0 e 10.0. A variável soma atuará como acumulador de somas das notas e, por fim, a variável contador terá a função de contar a quantidade de notas inseridas, para que seja possível extrair delas a média aritmética, expressa na variável media. Nas linhas 6 e 7 o contador e o acumulador recebem o valor inicial 0, a fim de garantir que a contagem em que atuarão seja exata. Conforme já mencionado, a variável soma atuará como um acumulador de notas e foi declarada como sendo do tipo flutuante, ou seja, do tipo que aceita valores com casas decimais. Ao planejar as variáveis que comporão o algoritmo, você deve entender que tipo de valor ela processará. Neste caso específico, ela armazenará somas de notas, que também serão usadas para armazenar dados do tipo flutuante. Afinal de contas, uma nota pode receber valores fracionários. A linha 8 marca o início do bloco de comandos que serão repetidos enquanto determinada condição estiver sendo satisfeita e a linha 16 marca o fim deste bloco. Observe que o comando agora tem uma estrutura diferente: o início do bloco de repetição é marcado pela palavra faça e nada mais. Isso significa que não haverá teste lógico antes da entrada do fluxo no bloco, fato que distingue este comando daquele que estudamos em nosso encontro anterior. Outro desdobramento desta construção do comando é a execução, ao menos uma vez, do bloco de comandos. Na prática, esta característica desobriga o criador do algoritmo a fazer, por exemplo, uma leitura de variável antes da entrada no bloco, já que o teste lógico será feito apenas no final do laço e, antes dele, essa variável será lida. Confirmando esta característica, temos a leitura da primeira nota na linha 10, ou seja, já no interior do bloco. O fato de nenhuma comparação do valor informado pelo usuário com o valor -1 ter sido feita até aquela linha, obriga o criador do algoritmo a tomar uma precaução: na linha 12 foi incluído uma condicional que verifica se o valor recém-digitado não é -1. Caso o valor digitado não seja -1, então o algoritmo acumulará nota e somará 1 em contador. Processadas estas duas expressões, o bloco de repetição se encerra na linha 16, com o teste lógico que determinará a parada ou prosseguimento do loop. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 80 ANOTE ISSO O conceito de algoritmo nos indica que a ordem dos comandos importa, já que se trata de uma sequência de passos ordenada e finita. Isto posto, convidamos você a observar novamente a descrição do algoritmo em linguagem natural e compará-la com o algoritmo desenvolvido em pseudolinguagem. Um olhar mais atento revelará que, na primeira descrição, o comando de “somar um ao contador” aparece antes do comando para “acumular notas”. Na descrição do algoritmo em pseudolinguagem, no entanto, esta ordem aparece trocada. Neste caso específico, a ordem de execução destes dois comandos não terá impacto na correta execução do algoritmo. No entanto, a necessidade de se cuidar da ordem dos comandos permanece como regra geral dos algoritmos. Na linha 18 – já fora do bloco – o algoritmo avalia se a variável contador não contém o valor 0 antes de efetivar a divisão da linha 19. Vale o registro de que o cálculo da média deve ser feito apenas uma vez durante todo o algoritmo, fato que impede que a expressão seja colocada no interior do bloco de repetição. Vale também a menção de que o valor de contador permanecerá 0 se nenhuma nota for informada pelo professor. Feita essa avaliação, o valor da média é informado ao usuário ou, no caso de não ter sido digitado nota alguma, uma mensagem correspondente é exibida através do comando da linha 22. 8.1 Teste de mesa A natureza e funcionalidades próprias do algoritmo que acabamos de desenvolver nos oferece a oportunidade para apresentarmos o que convencionamos chamar de “teste de mesa”. Há muito tempo, numa é época em que os computadores eram raros e o acesso a eles era restrito, um desenvolvedor não dispunha de todo o tempo que quisesse para criar seu algoritmo. Neste cenário, ele precisava garantir que seu código seria editado, compilado e executado no menor tempo possível, ao se certificar de que ele continha a menor quantidade possível de erros. Para isso, o desenvolvedor daquela época simulava a execução do código no papel e sobre sua mesa de trabalho. O teste de mesa consiste, pois, em simular a execução do algoritmo criado, sem executá-lo de verdade em um computador. O primeiro passo nesta direção é dado pela identificação das variáveis que compõem o código e pela inclusão dessas variáveis em uma tabela. No caso específico do nosso exemplo, a construção desta tabela é a que segue: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 81 nota soma contador media Eventualmente uma ou outra expressãoque apareça no algoritmo também poderá ganhar uma coluna na tabela, mas não adotaremos essa prática neste exemplo. Uma vez criada a tabela, bastará simular a execução do algoritmo e, a cada comando, atualizar o valor das variáveis. Naturalmente que o criador do algoritmo deverá escolher um valor aleatório e válido quando houver uma leitura no código. A fim de que a execução do nosso teste de mesa seja facilitada, será reproduzido aqui o código do algoritmo que desenvolvemos na seção 8.1. 1. algoritmo “CalculaMedia” 2. var 3. nota, soma, media: flutuante 4. contador: inteiro 5. inicio 6. soma = 0 7. contador = 0 8. faca 9. escreva(“Digite a nota (ou -1 para encerrar): “) 10. leia(nota) 11. //Verifica nota é válida antes de incluir na média 12. se (nota != -1) entao 13. soma = soma + nota 14. contador = contador + 1 15. fimse 16. enquanto (nota != -1) 17. // Calcula a média 18. se (contador > 0) entao 19. media = soma / contador 20. escreva(“A média das notas é: “, media) 21. senao 22. escreva(“Nenhuma nota válida foi inserida.”) 23. fimse 24. fimalgoritmo ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 82 A segunda ação a ser tomada é a atribuição do valor 0 às variáveis soma e contador, conforme feito nas linhas 6 e 7. A variável nota receberá aleatoriamente o valor 6.5, o professor tivesse informado esta nota ao usar o algoritmo na linha 10. Como a variável media não foi atualizada e não recebeu valor inicial algum, nós a manteremos inalterada. A tabela, então, ficará como segue. nota soma contador media 6.5 0 0 Executamos mentalmente o teste da linha 12 - se (nota != -1) então – e comparamos o valor de nota, que é 6.5, com a constante -1. Como o teste retorna verdadeiro, prosseguimos para as linhas 13 e 14, nas quais a soma e a contagem de notas são feitas. Considerando o valor que temos para a nota e os comandos executados nas linhas 13 e 14, a tabela ficará assim: nota soma contador media 6.5 0 0 6.5 1 A variável soma assumiu o valor 6.5 por ter sido submetida à expressão soma = soma + nota. Como o valor inicial de soma era 0, ele foi somado a 6.5, e o resultado foi atribuído à própria variável soma. Importante também mencionar que, se achar conveniente, você poderá riscar o valor anterior da variável, justamente para mostrar que ele foi atualizado depois do processamento da expressão. Esse expediente foi adotado na primeira linha da tabela, apenas para fins de exemplificação. Ao atingir a linha 16, o fluxo do algoritmo será direcionado novamente para linha 8, ou seja, para o início do comando de repetição. Considerando leituras sucessivas da variável nota com os valores 7.0, 3.9, 8.5 e -1, teremos as seguintes atualizações na tabela: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 83 nota soma contador media 6.5 0 0 7.0 6.5 1 3.9 13.5 2 8.5 17.4 3 -1 25.9 4 25.9 4 Findado o laço de repetição, a média de 6,47 será calculada e o objetivo do algoritmo terá sido atingido. O formato final da tabela ficará como segue: nota soma contador media 6.5 0 0 7.0 6.5 1 3.9 13.5 2 8.5 17.4 3 -1 25.9 4 25.9 4 6.47 A utilização deste recurso será bastante útil a você, considerando o aumento do entendimento a respeito do código e a checagem do resultado atingido pelo algoritmo. Ficamos por aqui neste capítulo. Até a próxima! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 84 CAPÍTULO 9 ESTRUTURA DE REPETIÇÃO COM NÚMERO DEFINIDO DE REPETIÇÕES Continuamos neste encontro a sequência de três capítulos dedicados às modalidades de comandos de repetição. Conforme já mencionado, a programação estruturada nos reservou estruturas sequenciais, condicionais e de repetição como recursos para desenvolvermos nossos algoritmos e, na condição de componentes importantes destes recursos, cada uma das três modalidades será abordada em profundidade e de modo particularizado. Na terceira e última modalidade, abordaremos o comando de repetição usado em ocasiões em que a natureza do problema permite que o criador do algoritmo conheça antecipadamente a quantidade de loops a serem executados no bloco. No encontro anterior a este tivemos a oportunidade de esmiuçar o comando de repetição com teste lógico no final do bloco e, para isso, desenvolvemos um exemplo completo de sua utilização. Naquela oportunidade, tivemos a chance de conhecer o teste de mesa, meio pelo qual você pode simular a execução do código sem que um computador seja demandado. No presente capítulo investigaremos as particularidades da estrutura em que a condição de parada é acionada pelo atingimento de uma determinada quantidade de repetições. Sigamos adiante! 9.1 Conceitos e aplicações Na estrutura com quantidade conhecida de repetições, o próprio comando implementa a condição de parada, cujo acionamento se dá pelo atingimento de uma determinada quantidade de repetições. Antes de prosseguirmos, vale a pena lembrarmos o formato geral do comando que será o objeto principal de estudo neste encontro. Para i de 1 até n faça <aqui são escritos os comandos que serão executados enquanto a variável de controle i não tiver atingido o valor n.> fimPara ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 85 O funcionamento do comando para..faça estabelece que uma variável de controle – comumente declarada como i e sempre do tipo inteiro – servirá como parâmetro para a contagem de repetições a serem executadas. O comando se inicia com a palavra Para, que é uma tradução possível para o comando universalmente denominado for, palavra comum no idioma inglês. Na sequência, o comando apresenta a variável que controlará a quantidade de repetições e que terá seu valor incrementado a cada iteração. Conforme já mencionado, utilizaremos a variável i para este fim, embora o nome seja de livre escolha do criador do algoritmo. Esta variável, no entanto, deverá ser sempre do tipo inteiro. O comando continua com o intervalo numérico que definirá a quantidade de iterações. Embora comumente seja utilizado o valor 1 como iniciador da contagem, nada impede que outro valor – seja ele expresso numericamente ou por meio de uma variável que contenha um valor inteiro – seja implementado como marco inicial da contagem. O comando termina com a palavra faça, como indicativo de que um bloco de comandos a ser repetido será implementado na sequência. Conforme mencionado em nosso quarto encontro (confira no Capítulo 4), este comando deve ser usado nas situações em que o criador do algoritmo sabe antecipadamente a quantidade de repetições a serem feitas. Embora o enunciado do problema normalmente não aponte que um ou outro comando deva ser usado, você deve ficar atento às indicações de quantidades definidas existentes neste enunciado. O cálculo da média entre 10 notas, a busca pelo maior entre 5 números e a soma de 4 distâncias compõem algumas situações que se resolvem pela implementação do comando “Para..Faça”. Em termos práticos, o funcionamento do comando com quantidade definida de repetições segue estes passos: • Ao ser executado pela primeira vez, o comando atribui à variável de controle i o valor que marca o início da contagem de repetições. Na grande maioria dos casos, esse valor é 1, conforme se observa no formato geral do comando. • Na sequência, o fluxo do algoritmo alcança o bloco de comandos que serão repetidos. A colocação no plural da palavra “comando” constitui apenas uma conveniência de estilo, já que pode haver apenas um comando no bloco. Os comandos então são executados até o atingimento do delimitador de fim de bloco. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 86 • Neste momento, o fluxo é desviado para o início do comando,especificamente para a linha do para..faça, e o valor da variável de controle (i) é incrementado automaticamente em 1. ANOTE ISSO O incremento da variável i, quando não indicado, é de uma unidade. No entanto, o comando para..faça prevê a possibilidade de um incremento diferente para esta variável, por meio do complemento passo colocado no comando. - O comando, então, compara o novo valor da variável de controle com o valor que marca o final das repetições. Se a variável de controle for menor ou igual do que este valor, um novo loop é iniciado. Assim como fizemos nos dois últimos encontros, desenvolveremos um exemplo completo para ilustrar o funcionamento da estrutura que compõe o objeto principal do capítulo. Contudo, o argumento principal desta vez será uma operação matemática, cuja solução deverá requerer multiplicações sucessivas processadas em um bloco de repetição. O enunciado para o problema é bem simples: elaboração de um algoritmo que calcule o fatorial de um número dado pelo usuário. Acrescentaremos a isso a restrição de que o valor fornecido pelo usuário deverá estar entre 0 e 8. Antes de nos aventurarmos pela solução computacional do problema, convém darmos uma olhada em sua essência matemática. O fatorial de um número, denotado por “n!”, é o produto de todos os números inteiros positivos de 1 até n. Em outras palavras, n!=n×(n−1)×(n−2)×…×2×1. O 5!, por exemplo, é calculado como 5×4×3×2×1=120. O fatorial é frequentemente utilizado em combinatória, probabilidade e outras áreas da matemática para calcular o número de maneiras diferentes de organizar ou escolher elementos de um conjunto. O primeiro passo em direção à solução é dado pela definição da condição de parada. Ao contrário dos dois comandos de repetição anteriores, o comando para.. faça controla automaticamente a quantidade de repetições e tem, implicitamente, a condição de parada atrelada a esta quantidade. Resta-nos, então, definir a quantidade de repetições que deverão ser executadas no bloco para termos uma condição de parada, certo? ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 87 Embora a resposta para esta pergunta seja “sim”, o problema está em entender quantas repetições são necessárias, considerando as características do cálculo. Note que o fatorial de 5 é calculado por uma sequência de 5 números multiplicados em ordem descendente. Por sua vez, o fatorial de 6 será calculado por uma sequência de 6 números multiplicados em ordem descendente e assim por diante. Esta característica do cálculo nos indica, portanto, a quantidade de repetições do comando. Há, contudo, uma última particularidade a ser abordada: de qual número se deseja calcular o fatorial? Conforme poderemos constatar, a resposta estará nas mãos do usuário. O segundo passo em direção à solução se dará por meio da descrição dos passos do algoritmo em linguagem natural estruturada. Mesmo que sujeita a pequenas alterações de desenvolvedor para desenvolvedor, uma descrição possível desses passos é a que segue: Leia um número Se esse número for positivo, Então calcule o fatorial através de multiplicações sucessivas Exiba o resultado. Embora conte com uma estrutura lógica adequada ao entendimento dos passos, essa descrição ainda não contempla muitos elementos necessários a uma descrição por pseudolinguagem, incluindo declaração e uso de variáveis. Falta também a esta descrição o detalhamento do comando contido na sentença “...calcule o fatorial através de multiplicações sucessivas”. Sem este detalhamento, o entendimento da solução ficaria prejudicado. Esta descrição em linguagem natural estruturada fornece base para a nossa próxima etapa do refinamento sucessivo, que é a elaboração do pseudocódigo deste problema, conforme segue: 1. Algoritmo “Fatorial” 2. Var 3. numero, fatorial, i: inteiro 4. Inicio 5. Escreva(“Digite o numero para calcular o fatorial: “) 6. Leia(numero) 7. // Verifica se o número é negativo (fatorial não está definido para números negativos) 8. Se numero < 0 Então ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 88 9. Escreva(“O fatorial não está definido para núme- ros negativos.”) 10. Senão 11. fatorial := 1 12. Para i de 1 ate numero faca 13. fatorial := fatorial * i 14. FimPara 15. Escreva(“O fatorial de “, numero, “ é: “, fatorial) 16. FimSe 17. FimAlgoritmo Novamente, o entendimento completo do exemplo passa pela análise do seu código. Na linha 1 temos a indicação do início do algoritmo, com a atribuição de um nome que o identifica. A atribuição do nome “Fatorial” para este algoritmo é bastante conveniente, já que mostra ao observador a funcionalidade principal dele. Na linha 3, as variáveis que compõem a solução do problema são declaradas. Como são todas do mesmo tipo, é possível declará-las todas na mesma linha. A variável numero servirá para receber e armazenar o número inteiro informado pelo usuário, e do qual se deseja extrair o fatorial. A variável fatorial, também do tipo inteiro, será aquela que, ao final das repetições, conterá o resultado esperado do algoritmo. Por fim, a variável i controlará a quantidade de interações dadas pelo comando para..ate. As linhas 5 e 6 marcam o primeiro processamento efetivo do código e contêm os comandos que estabelecerão interação com o usuário. Respectivamente, eles servem para enviar uma mensagem à tela e receber um valor informado pelo usuário, justamente aquele do qual se deseja calcular o fatorial. Na linha 8, o comando condicional “se” é usado para prevenir a tentativa de se calcular o fatorial de um número negativo, o que não é viável matematicamente. Em havendo sido digitado um número negativo, o fluxo do algoritmo é desviado para o fim. Caso o número informado não seja negativo, o fluxo é desviado para o bloco de comandos contido entre as linhas 10 e 16. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 89 ANOTE ISSO Idealmente, toda entrada feita pelo usuário deve ser validada pelo algoritmo, o que implica na criação de mecanismo que ofereça nova possibilidade de informar um dado no caso de a entrada ter sido feita em desacordo com os requisitos do problema. Logo teremos oportunidade de criar código de validação de entrada do usuário. Outro registro necessário neste ponto é a diferença entre os termos iteração e interação, ambas mencionadas neste capítulo. Embora parecidas, essas palavras são usadas em contextos diferentes. Iteração nos remete à repetição de algo. Por sua vez, interação significa ação mútua ou comunicação e geralmente é usada quando o texto se refere às ações do usuário durante a execução do algoritmo. Na linha 11 há um comando que atribui o valor 1 à variável fatorial, o que significa a atribuição do valor neutro da multiplicação à variável que conterá o resultado das multiplicações sucessivas. Esta ação equivale a, por exemplo, “zerar” o contador antes de incluí-lo na expressão. Finalmente, as linhas 12 a 14 implementam as ações demandadas para a efetiva solução do problema. Note que, por ação controlada pelo comando para..ate, a variável i assumirá valores de 1 até o número informado pelo usuário na leitura da linha 6. Conforme já mencionado, a quantidade de multiplicações executadas nesta operação coincide com o valor do qual se deseja o fatorial. Como já conhecemos a técnica do teste de mesa, nós a usaremos para descrever as iterações do comando para..ate. Como primeira providência, será criada tabela contendo variáveis e expressão que integram os comandos das linhas 11 a 14. Note que a expressão fatorial * i também foi incluída, para melhor acompanhamento da evolução da variável fatorial. Consideraremos que o usuário informou o valor 4 na entrada do dado. Além disso, os valores iniciais das variáveis e da expressãojá serão colocados na primeira linha da tabela. Note que a expressão fatorial * i foi valorada na primeira iteração feita pelo comando. numero i fatorial * i fatorial 4 1 1 1 Ao final da primeira iteração, o comando atinge o fim de bloco na linha 14 e, ato contínuo, desvia o fluxo do algoritmo de volta para a linha 12. Neste momento, a variável ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 90 i é incrementada em uma unidade e um teste de atingimento do número de iterações é feito. Como a variável i é menor do que 4 (valor informado pelo usuário), então o comando prossegue e a tabela do teste de mesa fica com a seguinte configuração: numero i fatorial * i fatorial 4 1 1 1 4 2 2 2 Como a leitura do número do qual se quer o fatorial é feita fora do laço de repetição, o valor lido não irá se alterar. Na terceira iteração, os processos se repetem: o valor da variável i é incrementado em 1 e comparado com o conteúdo da variável numero, que permanecem em 4. Como a comparação retorna falso, a multiplicação de fatorial com i é novamente feita e o resultado é atribuído à própria variável fatorial. Com valores atualizados, nosso teste de mesa fica assim: numero i fatorial * i fatorial 4 1 1 1 4 2 2 2 4 3 6 6 Na quarta iteração, os valores das variáveis i e numero serão iguais, o que significa que a iteração será executada novamente. A parada se dará quando o valor da variável de controle exceder o valor indicado após o “até”, seja ele expresso em uma constante ou variável. Com valores atualizados pela execução do incremento aplicado em i e pela execução da expressão fatorial := fatorial * i, a tabela do nosso teste de mesa assim ficará: numero i fatorial * i fatorial 4 1 1 1 4 2 2 2 4 3 6 6 4 4 24 24 O resultado que queríamos era 4! (4x3x2x1) e, ao final do processamento, obtivemos o valor 24. Observe que, na notação matemática, a ordem das multiplicações é do maior número para o menor. No processamento do algoritmo, a ordem é do menor número (1) até o maior que, no caso, é 4. Esse fato, no entanto, não altera o resultado da operação. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 91 9.1 Validação de entrada Independentemente da natureza do algoritmo e das estruturas nele aplicadas, sempre será necessário que todo valor ou texto informado pelo usuário passe pelo que chamamos de validação de entrada. É muito comum – e necessário - que o criador do algoritmo oriente o usuário sobre o tipo de entrada que deve ser dado, com mensagens do tipo “Informe um número inteiro entre 1 e 10” ou “Informe sua nota ou -1 para terminar”. Ocorre, no entanto, nada impede que usuário não siga a orientação e, ao invés de informar o que se pede, digite algo totalmente incoerente com o propósito do algoritmo. Haverá necessidade, portanto, de se prevenir essas entradas inconsistentes. A funcionalidade que permeia uma validação inclui a comparação da entrada dada com os critérios estabelecidos pela lógica do algoritmo. Em havendo discrepância entre ambos, deverá ser dada ao usuário a chance de digitar novamente aquele dado. Uma análise descuidada das soluções que poderiam ser implementadas nessa situação apontaria na direção do comando se..então..senão. Esse caminho, no entanto, será totalmente ineficaz se considerarmos que o usuário poderá ser incoerente na entrada uma segunda, terceira ou quarta vez – e assim por diante. O comando se..então..senão serviria para validação se ao usuário fosse dada apenas uma chance de errar a entrada. Um segundo erro, nesta hipótese, levaria ao fim do algoritmo. Na prática, o que os criadores de algoritmos utilizam nas validações de entrada são os comandos de repetição e a lógica embutida nesta solução é a seguinte: “enquanto o usuário não informar corretamente a entrada, permita que ele a informe novamente”. O uso de um comando de repetição, portanto, será necessário em uma validação de entrada. Tomemos como exemplo o código que acabamos de desenvolver para o cálculo do fatorial de um número inteiro. Apenas para facilitar nossas referências, o trecho entre as linhas 5 e 14 serão reproduzidos na sequência. 5. Escreva(“Digite o numero para calcular o fatorial: “) 6. Leia(numero) 7. // Verifica se o número é negativo (fatorial não está definido para números negativos) 8. Se numero < 0 Então 9. Escreva(“O fatorial não está definido para números negativos.”) 10. Senão 11. fatorial := 1 12. Para i de 1 ate numero faca 13. fatorial := fatorial * i 14. FimPara ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 92 Observe que a leitura da linha 6 é feita sem que uma restrição de entrada seja colocada. Será, portanto, neste trecho aplicaremos uma validação, levando em conta que números abaixo de zero e acima de 8 não serão permitidos. A primeira restrição foi estabelecida para prevenir a digitação de números negativos, faixa em que não se pode calcular fatorial. A segunda restrição foi colocada para que valores excessivamente altos não precisem ser calculados pelo algoritmo. Nossa primeira providência então será “cercar” o comando de leitura com um comando que a repetirá toda vez que o valor informado estiver em desacordo com os critérios estabelecidos. Não faremos a reescrita completa do algoritmo do fatorial para demonstrar um meio de validação. Ao invés disso, focaremos nossa atenção no trecho em que ela deverá ser efetivada. Em nosso próximo encontro teremos oportunidade de desenvolver mais validações de entrada. Observe: faça Escreva(“Informe um numero entre 0 e 8 para calcular o fatorial: “) Leia(numero) Enquanto (numero < 0) ou (numero > 8) O trecho de leitura está “envolto” em um comando faça..enquanto. Da forma como foi estruturado, este comando garante que será oferecida ao usuário uma nova possibilidade de leitura toda vez que o valor lido estiver abaixo de 0 e acima de 8, conforme as restrições impostas pelo problema. Note que foi utilizado o operador lógico ou para compor a condição de parada da repetição. Caso a entrada for menor que zero OU maior do que 8, o trecho deverá ser repetido. Fica colocado o desafio para que você refaça o algoritmo do fatorial utilizando a validação de entrada proposta. Terminamos por aqui mais um encontro. Até logo! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 93 CAPÍTULO 10 ESTRUTURA DE DADOS HOMOGÊNEA UNIDIMENSIONAL: VETORES Chegamos ao nosso décimo encontro e, com ele, vem a certeza de que uma afirmação poderá ser feita com segurança: já conhecemos todos estruturas lógicas oferecidas pelo paradigma da programação estruturada e, com elas, poderemos desenvolver algoritmos completos e com capacidade para resolver os problemas que se apresentarem. Naturalmente, esta afirmação se baseia no conhecimento que adquirimos até aqui acerca de estruturas sequenciais, condicionais e de repetição e dos seus respectivos comandos. Há, no entanto, algumas questões a serem respondidas: será que já conhecemos todos os recursos com os quais um algoritmo poderá contar para estruturar e tratar seus dados? Será que a variável, do modo que a conhecemos, é o único meio de se guardar um dado? Como a resposta para essas perguntas é “não”, iniciamos neste capítulo uma sequência de aulas nas quais abordaremos as estruturas que nos darão a possibilidade de agrupar dados e tratá-los de forma conjunta, em oposição à singularidade implícita em uma variável. Sigamos adiante! 10.1 Conceito de vetor Uma boa maneira de iniciar a composição de um determinado conceito é pelo entendimento dos termos que compõem o nome do recurso. A expressão “estrutura de dados homogênea unidimensional” – que pode ser considerado como o nome completo do vetor - nos reserva uma série de descobertas sobre sua natureza e funcionalidade.A expressão “estrutura de dados” nos remete a um arranjo ou forma de organização em que mais do que um dado poderá ser armazenado. Uma variável, em sua forma original, compõe um recurso em que um dado – e apenas um – pode ser armazenado por vez. Se um outro valor é atribuído à uma variável, o valor anterior se perde. Por ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 94 definição, uma estrutura de dados é um meio do qual o criador do algoritmo dispõe para armazenar mais do que um valor por vez. Outro componente do nome atribuído ao vetor é o termo “homogênea”. Ao buscarmos no dicionário o significado deste termo, veremos que ele indica coisas do mesmo tipo ou com semelhança em sua composição. Colocada no feminino por se referir diretamente à estrutura, esta palavra nos indica que, embora um vetor suporte vários dados, eles deverão ser todos do mesmo tipo. Por exemplo, um vetor declarado sob o tipo inteiro, só suportará dados inteiros. Por fim, o termo “unidimensional” nos revela que a estrutura possui apenas uma dimensão, ou seja, podemos imaginar o vetor como sendo uma fileira de dados que representam apenas a largura de uma tabela. Colocadas estas observações, é chegado o momento de você conhecer a representação visual mais conhecida para um vetor. É necessário mencionar que a forma com que os dados são de fato armazenados na memória do computador não é a mesma forma que os representaremos aqui. A figura 10.1 exibe a representação de um vetor e a indicação de seus componentes. 5 7 -6 21 10 14 1 3 1 2 3 4 5 6 7 8 Figura 10.1: Representação de um vetor Fonte: elaborada pelo próprio autor. Desta representação podemos extrair o seguinte: • os dados colocados nos retângulos são chamados elementos do vetor e devem ser todos do mesmo tipo. Neste exemplo, os dados são todos numéricos e do tipo inteiro. • os números posicionados abaixo dos elementos do vetor são chamados de índices e a função deles é a de apontar e individualizar a posição de um determinado elemento no vetor. Os índices começam em 1 e são formados por valores inteiros sequenciais. Embora um vetor seja uma estrutura única, seus elementos são tratados individualmente como variáveis, algo obtido pela implementação do índice. Supondo que o vetor da figura 10.1 tenha sido declarado como v (logo trataremos da declaração de um vetor), a individualização dos elementos se dará como segue: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 95 o elemento v[1] tem o valor de 5; o elemento v[2] tem o valor de 7; o elemento v[3] tem o valor de -6; o elemento v[4] tem o valor de 21; o elemento v[5] tem o valor de 10; o elemento v[6] tem o valor de 14; o elemento v[7] tem o valor de 1 e o elemento v[8] tem o valor de 3. ANOTE ISSO Algumas linguagens de programação implementam o índice com início em 0 ao invés de 1. O VisualG, que constitui nossa ferramenta atual para criação de algoritmos, inicia a contagem dos índices por 1 e esta prática será adotada em nossos encontros. Observe que o índice é escrito entre abre e fecha colchetes ( [ ] ), que é a notação universal para este recurso. Na sequência, trataremos das especificidades contidas na declaração de um vetor. 10.2 Declaração de um vetor A definição do tamanho de um vetor (ou seja, da quantidade de elementos que ele suporta) faz parte do conjunto de decisões que o criador algoritmo deve tomar. Esse tamanho, além do nome e o tipo do vetor, são as informações que compõem a declaração do vetor. Tomando como exemplo o vetor da figura 10.1, a declaração em VisualG ficaria como segue: v: vetor [1..8] de inteiro A primeira parte da declaração é constituída pelo nome do vetor. A este respeito, devemos nos lembrar das regras de composição de um identificador e do bom senso ao escolher um nome que combine com a função do vetor. Depois do nome e do sinal de dois pontos ( : ), o que segue é o termo “vetor”, que indica a estrutura que está sendo declarada. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 96 Na sequência é posicionada a dimensão do vetor, sinalizada pelo número inicial 1 e pelo valor final 8. O número 8 indica a quantidade de valores suportados pelo vetor. Por fim, o tipo do vetor é explicitado. Devemos imaginar que, a partir da declaração do vetor, ele passará a existir na memória do computador e poderá ser utilizado conforme a demanda da solução do problema. 10.3 Aplicações de vetores Os vetores são utilizados quando a solução do problema demanda que um conjunto de dados do mesmo tipo esteja ligado a uma estrutura única. Imagine que o algoritmo que você está construindo deva manipular 10 notas individualmente. Sem a utilização de um vetor, você seria obrigado a declarar 10 variáveis do tipo flutuante, o que poderia ser desconfortável e improdutivo, para dizer o mínimo. Em termos gerais, os vetores desempenham um papel fundamental na criação de algoritmos, pois oferecem uma estrutura eficiente e organizada para o armazenamento e manipulação de um conjunto de dados. Sua utilidade vai muito além de uma simples coleção de elementos, pois eles proporcionam uma abordagem estruturada que facilita a implementação de algoritmos complexos e a resolução de uma variedade de problemas computacionais. Na sequência apresentamos duas características dos vetores que tornam bastante vantajosa sua aplicação em determinadas situações: • Armazenamento e manipulação eficientes: vetores permitem armazenar elementos do mesmo tipo de forma contígua na memória, facilitando o acesso direto a eles através de índices. Isso resulta em operações de leitura e gravação mais eficientes em comparação com estruturas de dados mais dispersas. Além disso, a capacidade de acessar elementos por meio de índices torna a manipulação de dados mais direta e rápida. Esse acesso direto é particularmente valioso em algoritmos que requerem iteração e processamento sequencial de elementos. • Organização e estruturação dos dados: a utilização de vetores proporciona uma maneira organizada de lidar com conjuntos de dados. A ordenação linear dos elementos facilita a implementação de algoritmos de busca, classificação e outras operações que dependem da organização dos dados. Isto posto, resta-nos aplicar este conteúdo em prol da construção de um exemplo que o justifique. Antes, porém, algumas particularidades do processamento de um vetor devem ser abordadas. Sigamos adiante! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 97 10.4 Vetor na prática Conforme já mencionado, o índice relacionado a um vetor serve para individualizar cada elemento deste vetor. Uma rápida observação nestes índices vai nos mostrar que eles são representados por números inteiros dispostos sequencialmente, que se estendem de 1 até o tamanho do vetor e pela variação do índice é possível percorrer, um a um, todos os elementos do vetor. Esta particularidade estabelece uma conveniência muito íntima entre o uso do vetor e o uso do comando de repetição para..faça, abordado em nosso último encontro. Como você bem se lembra, este comando faz a variável de controle – comumente declarada como i – variar de 1 até o valor apontado como parte do comando. Ao utilizarmos o para..faça no processamento do vetor, poderemos atribuir à variável i a missão atuar como índice dele. Observe o trecho de algoritmo que segue: para i de 1 ate 5 faca Escreva(“Digite o valor “, i, “: “) Leia(vetor[i]) fimPara Embora não seja um código completo, ele serve para nos mostrar um procedimento importante e bastante comum neste contexto: ele carrega um vetor com dados informados pelo usuário. O comando para..faça faz a variável i assumir valores entre 1 e 5, o que permite que a utilizemos como índice do vetor. A cada iteração, uma mensagem é enviada ao usuário paraque ele digite o valor 1 (ou primeiro valor), o valor 2 e assim por diante, até o valor 5. Ao final desta sequência, teremos um vetor todo lido. Considerando a escolha aleatória de valores informados pelo usuário, uma ilustração possível do vetor carregado é a mostrada na figura 10.2: 8 12 0 6 10 1 2 3 4 5 Figura 10.2: Vetor com dados informados pelo usuário Fonte: elaborada pelo próprio autor. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 98 ISTO ACONTECE NA PRÁTICA Na prática, à toda leitura de um valor em um algoritmo deve corresponder a devida validação de entrada. Embora o usuário deva ser instruído a respeito do tipo de dado a ser digitado, o criador do algoritmo deve criar meio de prevenção de digitação incorreta, a exemplo do que fizemos em nosso encontro passado. Outro aspecto prático a ser destacado neste ponto é impossibilidade de se realizar leitura de uma quantidade menor de elementos do que aquela estipulada no comando para..faça. Em outras palavras, em um laço de 5 repetições, o usuário não poderá escolher inserir, por exemplo, 3 valores apenas. Em um programa em execução, a interrupção do laço implicaria na própria interrupção da execução do programa. Para que a ilustração do uso de um vetor fique completa, desenvolveremos um exemplo em, após lido, o vetor de inteiros de tamanho 5 deverá ter seu primeiro e último elementos trocados entre si. Por fim, o vetor resultante deverá ser exibido. Ao colocarmos em prática nossa abordagem de evolução incremental do código, criaremos uma sequência de passos em alto grau de abstração como primeira providência rumo à solução. Ler um vetor de 5 elementos inteiros Executar a troca entre o primeiro e o sexto elementos Exibir o vetor resultante Embora a entrada e a saída de dados estejam relativamente bem definidas, a descrição do processamento soa bastante inconclusiva. Para que o caminho rumo ao algoritmo em pseudocódigo seja mais bem preparado, trataremos de detalhar e desdobrar a segunda linha daquele código. Vejamos: Ler um vetor de 5 elementos inteiros Atribuir à variável auxiliar o elemento v[1] Atribuir ao elemento v[1] o elemento v[5] Atribuir ao elemento v[5] a variável auxiliar Exibir o vetor resultante ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 99 Por meio desta descrição entendemos que a inversão dos elementos (o primeiro elemento vai para a quinta posição e o quinto elemento vai para a primeira posição) requer a introdução de uma variável auxiliar, que armazenará temporariamente um dos elementos. Assim como acontecem em uma variável simples, a atribuição de um novo valor implica no desaparecimento do valor atual, o que não poderia acontecer nesta solução. Com a declaração da estrutura de dados e das variáveis e com a utilização dos comandos de um pseudocódigo em VisualG, teremos o seguinte algoritmo: 1. Algoritmo “TrocaElementosNoVetor” 2. var 3. vetor: vetor [1..5] de inteiro 4. auxiliar, i: inteiro 5. inicio // Preenche o vetor com valores fornecidos pelo usuário 6. para i de 1 ate 5 faca 7. Escreva(“Digite o valor “, i, “: “) 8. Leia(vetor[i]) 9. fimpara // Troca o primeiro elemento com o quinto usando uma variável auxiliar 10. auxiliar = vetor[1] 11. vetor[1] = vetor[5] 12. vetor[5] = auxiliar // Exibe o vetor resultante 13. Escreva(“Vetor resultante: “) 14. para i de 1 ate 5 faca 15. Escreva(vetor[i], “ “) 16. fimPara 17. FimAlgoritmo O melhor entendimento deste código passa pela análise individual das suas linhas mais relevantes. Conforme costume, iniciamos o algoritmo dando nome a ele. Na sequência, na linha 3, declaramos o vetor de tamanho 5 e de elementos inteiros. Na linha 4, as variáveis i (controle do laço para..faca e índice do vetor) e auxiliar são declaradas como inteiras. As linhas 6 a 9 implementam a leitura do vetor, por meio da aplicação de um comando com quantidade definida de repetições. Na sequência, das linhas 10 a 12, a troca dos elementos é feita por meio de atribuições sucessivas ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 100 entre posições do vetor e variável auxiliar. Por fim, as linhas 14 e 16 imprimem o vetor resultante. As aplicações possíveis para um vetor certamente serão muito variadas e numerosas. Nos próximos encontros teremos oportunidade de usarmos essas estruturas como elementos auxiliares na manipulação conjunta de dados, o que nos dará mais familiaridade ainda com este recurso. Este é o fim deste capítulo. Até o próximo! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 101 CAPÍTULO 11 ESTRUTURA DE DADOS HOMOGÊNEA BIDIMENSIONAL: MATRIZES Em nosso último encontro tivemos a oportunidade de conhecer um recurso absolutamente útil ao armazenamento de um conjunto de dados do mesmo tipo. O vetor - nome pelo qual identificamos uma estrutura de dado homogênea unidimensional – é capaz de armazenar uma quantidade de elementos igual ao tamanho estabelecido no momento da declaração. Com a utilização do comando para..faça, um vetor pode ser percorrido para realização da leitura, do processamento e da exibição de seus dados em tela, contando com a conveniência da quantidade conhecida de repetições do comando que com ele atua. O capítulo que aqui se inicia abordará também uma estrutura que armazena dados de modo conjunto e do mesmo tipo, mas com uma particularidade: ao invés de uma dimensão, esta estrutura conta com duas para executar sua função, fato que a fez ficar conhecida como matriz. Quem sabe até já lhe seja familiar o formato desta estrutura. Nossa abordagem incluirá o necessário embasamento teórico – cujo caminho já foi bem pavimentado pelos vetores – e o desenvolvimento prático a fim de esclarecer a utilização de uma matriz. Sigamos adiante! 11.1 Conceito de matriz Embora já tenhamos visitado cada termo da expressão que identifica o vetor e, na ocasião, esclarecido boa parte do que também estudaremos aqui, faremos o mesmo com a matriz. A expressão “estrutura de dados homogênea bidimensional” – que pode ser considerado como o nome completo de matriz - nos reserva uma série de descobertas sobre a natureza e funcionalidade deste recurso. A expressão “estrutura de dados” nos remete a um arranjo ou forma de organização em que mais do que um dado poderá ser armazenado. Por definição, uma estrutura de dados é um meio do qual o criador do algoritmo dispõe para armazenar mais do que um valor por vez. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 102 Outro componente do nome atribuído ao vetor é o termo “homogênea”. Ao buscarmos no dicionário o significado deste termo, veremos que ele indica coisas do mesmo tipo ou com semelhança em sua composição. Colocada no feminino por se referir diretamente à estrutura, esta palavra nos indica que, embora uma matriz suporte vários dados, eles deverão ser todos do mesmo tipo. Por exemplo, uma matriz declarada sob o tipo inteiro, só suportará dados inteiros. Até agora, sem novidades. O termo “bidimensional” é o que estabelecerá a primeira diferença conceitual entre um vetor e uma matriz. Este termo nos revela que a estrutura possui duas dimensões, ou seja, podemos imaginar uma matriz como sendo uma estrutura composta por linhas e colunas, à semelhança de uma tabela, conforme ilustrado na figura 11.1. É prudente mencionar novamente que a forma com que os dados são armazenados na memória do computador não é a mesma forma aqui representada. 1 7 -6 9 10 2 12 1 0 8 3 4 3 7 1 4 6 9 3 2 1 2 3 4 Figura 11.1: Representação de uma matriz. Fonte: elaborada pelo próprio autor. Desta representação podemos extrair o seguinte: • os dados colocados na tabela são chamados elementos da matriz e devem ser todos do mesmo tipo, já que a estruturaé homogênea. Neste exemplo, os dados são todos numéricos e do tipo inteiro. • os números posicionados abaixo e à esquerda da matriz são também chamados de índices da matriz e a função deles é a de apontar e individualizar a posição de um determinado elemento. Os índices começam em 1 e são formados por valores inteiros sequenciais. • esta matriz tem dimensão 4x4 (lê-se quatro por quatro), já que é formada por quatro linhas e quatro colunas. Nada impede que o número de colunas seja diferente do número de linhas. Uma matriz formada por apenas uma linha será chamada de vetor e seu processamento será feito conforme abordamos em nosso encontro passado. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 103 Embora uma matriz seja uma estrutura única (ou unitária), seus elementos são tratados individualmente como variáveis, algo obtido pela implementação dos índices. Para referenciar um elemento de forma individualizada, o processamento feito em uma matriz necessita de dois índices (no vetor apenas um índice era necessário), fato que requererá alguma adaptação neste processamento. Estabeleceremos que o primeiro índice fará referência à linha e o segundo índice fará referência à coluna da matriz. Supondo que a matriz da figura 11.1 tenha sido declarado com o nome de m, a individualização dos elementos daquela matriz será feita como segue: o elemento m[1,1] tem o valor de 7; o elemento m[1,2] tem o valor de -6; o elemento m[1,3] tem o valor de 9; o elemento m[1,4] tem o valor de 10; o elemento m[2,1] tem o valor de 12; o elemento m[2,2] tem o valor de 1; o elemento m[2,3] tem o valor de 0; o elemento m[2,4] tem o valor de 8; o elemento m[3,1] tem o valor de 4; o elemento m[3,2] tem o valor de 3; o elemento m[3,3] tem o valor de 7; o elemento m[3,4] tem o valor de 1; o elemento m[4,1] tem o valor de 6; o elemento m[4,2] tem o valor de 9; o elemento m[4,3] tem o valor de 3 e o elemento m[4,4] tem o valor de 2. ANOTE ISSO Algumas linguagens de programação implementam o índice com início em 0 ao invés de 1. O VisualG, que constitui nossa ferramenta atual para criação de algoritmos, inicia a contagem dos índices por 1 e esta prática será adotada em nossos encontros. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 104 Observe que o índice é escrito entre abre e fecha colchetes ( [ ] ), que é a notação universal para este recurso. É também necessário reforçar que os índices colocados entre os colchetes são separados por vírgula e se referem à linha e coluna, nesta ordem. Na sequência, trataremos das especificidades contidas na declaração de uma matriz. 11.2 Declaração de uma matriz A definição do tamanho de uma matriz (ou seja, da quantidade de elementos que ela suportará em linhas e colunas) faz parte do conjunto de decisões que o criador algoritmo deve tomar. Esse tamanho, além do nome e o tipo da matriz, são as informações que compõem a referida declaração. Tomando como exemplo a matriz da figura 11.1, a declaração em VisualG ficaria como segue: m: vetor [1..4, 1..4] de inteiro A primeira parte da declaração é constituída pelo nome da matriz. A este respeito, devemos nos lembrar das regras de composição de um identificador e do bom senso ao escolher um nome que combine com a função da estrutura de dados. Depois do nome e do sinal de dois pontos ( : ), o que segue é o termo “vetor”, que indica a estrutura que está sendo declarada. Note que, embora estejamos declarando uma matriz, o termo “vetor” ainda se faz presente. No entanto, agora ele vem acompanhado de especificação de quantidade de linhas e de colunas, o que indica a declaração de uma estrutura bidimensional, ou seja, uma matriz. Na sequência é posicionada a quantidade de elementos da linha da matriz, sinalizada pelo número inicial 1 e pelo valor final 4. O número 4, portanto, indica a quantidade de valores suportados na dimensão altura. Após a vírgula, a indicação se repete, desta vez relacionada à quantidade de colunas, o que nos revela a largura da matriz. Por fim, o tipo da matriz é explicitado. Devemos imaginar que, a partir da declaração, a matriz passará a existir na memória do computador e poderá ser utilizada conforme a demanda da solução do problema. 11.3 Aplicações de matrizes As matrizes são utilizadas quando a solução do problema demanda que um conjunto de dados do mesmo tipo esteja ligado a uma estrutura única, dotada de linhas e de colunas. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 105 Imagine que o algoritmo que você está construindo deva manipular, individualmente, 16 valores que necessitem ser dispostos em formato tabular. Sem a utilização de uma matriz, você seria obrigado a declarar 16 variáveis de um determinado tipo, o que poderia dificultar o desenvolvimento do algoritmo. As matrizes desempenham um papel fundamental em uma ampla gama de algoritmos e aplicações computacionais, já que nos oferecem uma estrutura eficiente e organizada para manipular conjuntos de dados em mais de uma dimensão. As principais aplicações e benefícios do uso das matrizes nos algoritmos incluem: Representação de dados tabulares: matrizes são frequentemente usadas para representar dados tabulares, como planilhas e dados estatísticos. Cada linha da matriz pode representar uma entrada de dado, enquanto as colunas podem representar diferentes atributos ou características associadas a esses dados. Processamento de imagem e vídeo: neste caso, as matrizes são amplamente utilizadas para representar pixels. Operações como filtragem, convolução e transformações podem ser aplicadas eficientemente usando operações matriciais, permitindo o processamento rápido e eficaz de grandes conjuntos de dados visuais. No contexto do processamento de imagem e vídeo, a convolução é uma operação matemática fundamental realizada entre uma matriz de pixels da imagem original e uma matriz conhecida como “filtro” ou “kernel”. A convolução é frequentemente utilizada para realizar diversas transformações e operações em imagens, como realce de bordas, desfoque, detecção de características e outras manipulações. Transformações Geométricas: em gráficos computacionais e simulações, as matrizes são utilizadas para realizar transformações geométricas, como rotações, translações e escalonamentos. Isso é vital na criação de efeitos visuais e animações. Ao utilizar matriz, o criador do algoritmo se beneficiará da eficiência computacional associada a ela. Operações matriciais são altamente otimizadas e podem ser executadas de maneira eficiente em hardware moderno, proporcionando ganhos significativos de desempenho. Além disso, as matrizes fornecem uma estrutura organizada para armazenar e manipular dados, facilitando o acesso e a manipulação de informações em algoritmos complexos. As matrizes, portanto, desempenham um papel essencial em uma variedade de domínios computacionais, desde a representação eficiente de dados até a resolução de problemas complexos em diversas disciplinas. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 106 Isto posto, resta-nos aplicar este conteúdo em prol da construção de um exemplo que o justifique. Antes, porém, algumas particularidades do processamento de uma matriz devem ser abordadas. Sigamos adiante! 11.4 Matriz na prática Em nosso último encontro, mencionamos que o índice relacionado a um vetor serve para individualizar cada elemento deste vetor, já que, variando seu valor de um em um, seria possível percorrer toda a estrutura, elemento a elemento. A adaptação deste recurso à realidade de uma matriz requer um único acréscimo: ao invés de um índice, precisaremos de dois, sendo um para percorrer os elementos da linha e outro para percorrer os elementos da coluna. À semelhança do índice de um vetor, os índices de uma matriz são representadospor números inteiros dispostos sequencialmente, que se estendem de 1 até a dimensão da linha e de 1 até a dimensão da coluna. As variações dos índices serão possibilitadas, de modo muito conveniente, pela utilização do comando de repetição para..faça. No entanto, precisaremos aqui de dois comandos para..faça: um para variar linha e outro para variar coluna. Neste ponto convém introduzir o conceito de comandos de repetição aninhados. Para esta implementação, devemos considerar a existência de um comando para.. faça interno e outro externo. O comando de “dentro” (ou interno) será responsável por fazer variar coluna por coluna da matriz. Quando a leitura, escrita ou qualquer outro processamento atingir a última coluna, o comando para..faça externo será executado e fará seu índice variar a linha em uma unidade, o que equivalerá a “pular de linha”. Como primeiro exemplo, desenvolveremos apenas o trecho de leitura de uma matriz de 3 linhas por 4 colunas. Observe: 1. para i de 1 ate 3 faca 2. para j de 1 ate 4 faca 3. escreva(“Informe o elemento “, i, “x”, j, “: “) 4. leia(matriz[i, j]) 5. fimpara 6. fimpara ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 107 Embora não estejamos diante de um código completo, ele servirá para nos mostrar um procedimento importante e bastante comum neste contexto: a carga de uma matriz com dados informados pelo usuário. O comando para..faça externo (de fora) faz a variável i assumir valores entre 1 e 3, o que permitirá o percurso das três linhas da matriz. Já o comando para..faça interno fará o índice j assumir valores entre 1 e 4, o que permitirá que todas as colunas da matriz sejam visitadas. Utilizaremos novamente o recurso do teste de mesa para entendermos como uma matriz é percorrida. Ao atingir o primeiro comando do trecho, o fluxo do algoritmo atribuirá o valor 1 à variável i e, logo em seguida, atribuirá também o valor 1 à variável j, pela ação direta dos comandos para..faça. Após a execução pela primeira vez dos dois comandos para..faça e da leitura do primeiro elemento – aleatoriamente escolhido como 7 –, teremos a seguinte configuração em nossa tabela: i j matriz [i,j] 1 1 7 O comando interno, que se estende da linha 2 até a linha 5, deverá ser resolvido antes que o fluxo do algoritmo seja deslocado para o comando externo. Em outras palavras, o índice i permanecerá em 1 enquanto o índice j assumirá valores entre 1 e 4. Após a quarta iteração do comando interno, o comando externo será novamente executado, conforme ilustrado na continuidade do nosso teste de mesa. i j matriz [i,j] 1 1 7 2 5 3 6 4 2 A figura 11.2 exibe representação da matriz após a execução do para..faça interno. 1 7 5 6 2 2 3 1 2 3 4 Figura 11.2: Representação da matriz após a carga da primeira linha. Fonte: elaborada pelo próprio autor. Uma vez resolvido o comando interno, o comando externo é novamente executado. Com o índice i assumindo do valor 2, haverá o equivalente a um salto de linha. Uma nova execução do comando interno fará novamente o índice j assumir o valor 1, o que ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 108 posicionará a leitura na posição [2,1] da matriz. O que vem na sequência é novamente a variação do índice j até o percurso das quatro colunas da matriz. ISTO ACONTECE NA PRÁTICA Novamente é necessário alertar para o fato de que, devido à construção do comando para..faça, não é possível interromper a entrada de dados em uma matriz antes do atingimento do número de elementos estipulado para ela na declaração. Caso você declare uma matriz 5x5, deverá realizar 25 leituras – nem uma a mais, nem uma a menos – de elementos para que o procedimento seja encerrado. Para que a ilustração do uso de uma matriz seja mais efetiva, desenvolveremos um exemplo completo, com declaração de variáveis e exibição de resultado. Nossa missão agora é a de ler uma matriz 4x4 e exibir apenas os elementos da diagonal principal e a explicação que segue substituirá a etapa de descrição do problema em linguagem natural estruturada. Em uma matriz em que a quantidade de linhas é igual à quantidade de colunas é definida o que chamamos de diagonal principal. A figura 11.3 ilustra esta ideia. 1 7 -6 9 10 2 12 1 0 8 3 4 3 7 1 4 6 9 3 2 1 2 3 4 Figura 11.3: Ilustração da diagonal principal de uma matriz Fonte: elaborada pelo próprio autor. Embora a indicação visual da diagonal principal seja um tanto óbvia, ainda precisamos definir como indicá-la por meio de uma expressão. Para isso, basta observarmos como se comportam os índices nos elementos em destaque. Uma rápida análise nos mostra que, para compor a diagonal principal, o elemento precisa que o índice da linha seja igual ao índice da coluna. Feita essa avaliação, descobriremos que o elemento só será exibido quando i = j. Observe o código completo. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 109 1. algoritmo “DiagonalPrincipal” 2. var 3. matriz: vetor [1..4, 1..4] de inteiro 4. i, j: inteiro 5. inicio 6. // Leitura da matriz 7. para i de 1 ate 4 faca 8. para j de 1 ate 4 faca 9. escreva(“Informe o elemento “, i, “x”, j, “: “) 10. leia(matriz[i, j]) 11. fimpara 12. fimpara 13. // Exibição dos elementos da diagonal principal 14. escreva(“Elementos da diagonal principal:”) 15. para i de 1 ate 4 faca 16. para j de 1 ate 4 faca 17. se (i=j) escreva(matriz[i, j], “ “) 18. fimpara 19. fimpara 20. fimalgoritmo O melhor entendimento deste código passa pela análise individual das suas linhas mais relevantes. Conforme costume, iniciamos o algoritmo dando nome a ele. Na sequência, na linha 3, declaramos uma matriz 4x4 de elementos inteiros. Na linha 4, as variáveis i e j são declaradas como inteiras, pois atuarão como índices da matriz. As linhas 7 a 12 implementam a leitura da matriz, por meio da aplicação de comandos aninhados com quantidade definida de repetições. Na sequência, das linhas 15 a 19 os elementos da diagonal são enviados à tela. As aplicações possíveis para uma matriz certamente serão muito variadas e numerosas. Procure desenvolver exercícios que manipulem tanto vetores quanto matrizes, a fim de que você adquira familiaridade com a utilização de índices. Ficamos por aqui neste encontro. Até o próximo e bons estudos! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 110 CAPÍTULO 12 ESTRUTURA DE DADOS HETEROGÊNEA: REGISTROS A abordagem de vetores e matrizes em nossos dois últimos encontros foi realmente providencial. Com o conhecimento adquirido, abandonamos a ideia de que poderíamos processar apenas um dado por vez e passamos a vislumbrar muitas possibilidades de manipulação de dados em conjunto. As estruturas homogêneas unidimensionais e bidimensionais nos mostraram ser possível agrupar dados que mantêm afinidade entre si em uma única estrutura, o que facilita a leitura, o processamento e a exibição do dado, além de tornar o código que as implementa mais facilmente compreensível. No entanto, havia uma restrição imposta aos vetores e matrizes. Por mais úteis que sejam ao criador do algoritmo, essas estruturas são capazes de armazenar apenas dados do mesmo tipo, o que limita sua utilização. No mundo real, os dados que compõem uma entidade tendem a ser de tipos distintos entre si, o que torna vetores e matrizes soluções frequentemente inaptas para estes casos. Que tal, então, se dispuséssemos de estruturas capazes de armazenar dados heterogêneos? Pois é exatamente deste assunto que trata este capítulo. Sigamos adiante! 12.1 Conceito e aplicações A estrutura de dados heterogênea é capaz de armazenar, de forma unitária, uma coleção de elementos de diferentes tipos. Diferentemente das estruturas homogêneas, que lidam com elementosdo mesmo tipo, as estruturas heterogêneas permitem armazenar dados variados, como inteiros, caracteres, booleanos e até mesmo outras estruturas de dados. Em quais ocasiões, afinal, é possível utilizar estas estruturas? Imagine que aquele tênis que você gostaria de ter entrou finalmente em promoção. A loja virtual que implementou a oferta, no entanto, nunca fora visitada anteriormente por você e, por isso, antes de poder chamar o tênis de seu, você precisará inserir seus dados em um formulário oferecido pelo site, como forma de ter seu cadastro efetivado. Uma rápida análise no formulário revelará a diversidade de tipos de dados ali contidos. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 111 Considerando alguns dos campos mais comuns de um formulário de cadastramento em uma loja virtual, teremos: Nome: tipo cadeia de caracteres (ou String ou Texto). Endereço: tipo cadeia de caracteres (ou String ou Texto). Senha: tipo numérico (pode incluir requisitos específicos, como comprimento mínimo e presença de caracteres, entre outros). Data de nascimento: tipo data. Quantidade de pessoas que residem na casa do cliente: tipo inteiro. ANOTE ISSO O conjunto de dados formado pelos campos do formulário – ou de qualquer outro agrupamento viabilizado por uma estrutura de dados heterogênea – levará o nome de registro. Então, quando você terminar de se cadastrar numa loja física (ou em qualquer outra entidade) por meio eletrônico, você terá acabado de se tornar um registro nos arquivos desta loja. As particularidades de armazenamento e de recuperação de um registro por meio de um Sistema Gerenciador de Banco de Dados (SGBD) não compõem o escopo do nosso estudo. Embora esta quantidade de campos esteja longe de ser o que, de fato, é implementado em um formulário deste tipo, ainda assim podemos observar a diversidade de tipos de dados aqui presente. Um deles – o tipo data - nem foi nosso objeto de estudo, inclusive. Essa variedade deixa claro que um vetor ou uma matriz não seriam capazes de oferecer solução para armazenamento deste conjunto de dados, já que suportam apenas dados do mesmo tipo. A solução, então, passa pela utilização de uma estrutura de dados heterogênea, já que elas oferecem flexibilidade ao acomodar dados diversos em uma única unidade organizada. Isso significa que, em vez de limitar o conjunto de dados a um único formato, as estruturas heterogêneas podem lidar com uma gama mais ampla de informações. Dentre as aplicações práticas destas estruturas, destacam-se: Representação de entidades complexas: estruturas heterogêneas são ideais para modelar entidades complexas que possuem propriedades diversificadas. Por exemplo, em sistemas de gerenciamento de banco de dados, onde uma entidade pode ter atributos como nome, idade e endereço, uma estrutura heterogênea pode acomodar ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 112 eficientemente esses dados variados. Fizemos esta menção, inclusive, na seção #Anote isso#. Manipulação de dados multidimensionais: em algoritmos que lidam com dados multidimensionais, como imagens ou modelos tridimensionais, as estruturas heterogêneas permitem representar e acessar diferentes tipos de informações em cada dimensão. Isso é crucial em áreas como processamento de imagem, realidade virtual e simulações. Armazenamento eficiente de configurações: sistemas que exigem a representação de configurações com parâmetros variáveis podem se beneficiar significativamente de estruturas de dados heterogêneas. Isso é evidente em configurações de software, onde cada componente pode ter diferentes tipos e quantidades de parâmetros. Integração de dados em redes sociais: em aplicações que lidam com dados provenientes de redes sociais, onde os perfis dos usuários podem conter uma diversidade de informações, desde texto e imagens até preferências e conexões, estruturas heterogêneas são essenciais para representar essas informações de forma coesa. De fato, são muitas possibilidades de uso para uma estrutura de dados heterogênea. Nas próximas seções trataremos de aspectos mais práticas relacionados ao nosso objeto de estudo. 12.2 Declaração de um registro Antes de tratarmos especificamente da declaração de um registro, convém resgatarmos algo importante a respeito da ferramenta que temos utilizado até o momento para implementar nossos algoritmos. O VisualG é uma ferramenta de criação de algoritmos voltada para implementações simples e apropriadas ao aprendizado de iniciantes na área. Sua capacidade de manipulação de estruturas de dados, portanto, é limitada quando comparada com linguagens de programação reais. O VisualG não oferece um meio para representarmos um registro, mas esta limitação não nos impedirá de prosseguir. A declaração e utilização dos registros serão feitos ainda por meio de pseudocódigo, mas que não necessariamente poderá ser executado. O formato geral da declaração de um registro é o que segue: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 113 tipo nomedoTipoRegistro = registro inicio campo1: tipo campo2: tipo ... campoN: tipo fim A descrição de cada parte do formato geral será feita por meio do desenvolvimento de um caso específico de criação e utilização de um registro. Desta forma, para a nossa primeira declaração, tomaremos como exemplo uma ficha qualquer de cadastramento, que contém os seguintes campos: nome, endereço, telefone e CEP. Em nosso pseudocódigo – e seguindo o formato geral recém-indicado – , a declaração fica assim: tipo ficha = registro início nome_completo: caractere endereco: caractere cidade: caractere cep: inteiro telefone: inteiro fim A primeira observação a ser feita atinge a palavra reservada tipo. Ela estabelece que um tipo de dado específico está sendo criado pelo desenvolvedor do algoritmo. Quando usamos os tipos inteiro ou caractere, por exemplo, estamos na verdade utilizando algo previamente estabelecido pela linguagem ou pela ferramenta de desenvolvimento de algoritmo. Sobre este tipo pré-estabelecido temos um controle bastante restrito e nos resta apenas declarar as variáveis destes tipos conforme as regras que já conhecemos. No caso da declaração de um registro, no entanto, temos a possibilidade de criar uma estrutura própria e específica para nossos propósitos. A palavra ficha indica o nome dado ao tipo que se está criando. Trata-se, portanto, de um identificador, e a atribuição de um nome a ele segue as regras que já conhecemos. Em seguida vem a palavra-chave registro, como indicação de que a declaração se relaciona a uma estrutura heterogênea. Por fim, os campos do registro são declarados com seus respectivos tipos. Para que seja mantida a maior compatibilidade possível com o VisualG, faremos a declaração da cadeia de caracteres (string) com o tipo caractere. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 114 ANOTE ISSO No contexto das linguagens de programação e dos algoritmos, uma palavra reservada é aquela que tem um significado específico e reservado na linguagem em que está sendo utilizada. Essas palavras têm funções pré-definidas na linguagem e não podem ser usadas para outros fins, como identificadores de variáveis ou de funções. Para que cada um dos campos do registro possa ser individualmente acessado, será necessária a criação de uma variável do tipo recém-declarado. Ao invés de declarar uma variável de um tipo pré-definido qualquer, o criador do algoritmo deverá declarar uma variável do tipo registro, aqui chamado ficha. ficha: ficha_minha A declaração desta variável, portanto, será feita como de costume, com a diferença de que o tipo será aquele definido pelo criador do algoritmo. Uma outra particularidade acerca dos registros é a possibilidade de inserirmos um registro na declaração deoutro registro, de acordo com a ideia de criação de recursos aninhados. Imagine que o criador do algoritmo deseje inserir, neste registro que temos, o campo de data de nascimento da pessoa a ser cadastrada. Embora uma linguagem de programação ou um Sistema Gerenciador de Banco de Dados (SGBD) conte com recursos avançados para guardar, processar e recuperar datas, nós aqui a trataremos da forma mais conveniente para a fixação do conceito e da utilização de registros. Considerando a declaração já feita do registro que chamamos de ficha, incluiremos uma outra em nossa área de declarações e a chamaremos de data_de_nascimento. Este segundo registro será assim declarado: tipo data_de_nascimento = registro início dia: inteiro mes: inteiro ano: inteiro fim ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 115 Basta agora inserir, no registro ficha, um campo do tipo data_de_nascimento, e a relação entre os dois registros estará estabelecido. Com esta providência, o tipo ficha ficará como segue: tipo ficha = registro início nome_completo: caractere endereco: caractere cidade: caractere cep: inteiro telefone: inteiro nascimento: data_de_nascimento fim Muito conveniente, não é mesmo? O tipo registro ficha tornou-se uma estrutura heterogênea aninhada, composta por outro tipo registro data_de_nascimento. Nas próximas páginas serão abordados aspectos da utilização de registros. 12.3 Utilização de um registro Uma vez declarada, a estrutura heterogênea poderá ser usada no código. O acesso aos campos do registro se dá pela composição da variável declarada com o nome do campo. Em nosso caso específico, o acesso será feito como segue: ficha_minha.nome_completo ficha_minha.endereco ficha_minha.cidade ficha_minha.cep ficha_minha.telefone Note que há um ponto ( . ) separando a variável do campo. É necessário salientar também que a expressão “acesso aos campos” remete à possibilidade de atribuir um valor, realizar uma leitura, exibir em tela ou até mesmo inseri-los em uma expressão aritmética, quando o tipo do campo assim o permitir. O acesso ao campo dia, declarado no registro data_de_nascimento, ficaria assim: ficha_minha.nascimento.dia. O desenvolvimento de um exemplo completo do uso de registro abordará a representação sobre alunos, incluindo nomes, idades e notas em duas disciplinas ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 116 diferentes. O pseudocódigo que implementa a leitura e exibição destes dados é o que segue: 1. Algoritmo ExemploEstruturaHeterogenea 2. // Definindo o registro para representar um aluno 3. tipo aluno = registro 4. inicio 5. nome: caractere 6. idade: inteiro 7. notaMatematica: real 8. notaPortugues: real 9. fim 10. //Declarando variáveis do tipo Aluno 11. var 12. aluno1: aluno 13. aluno2: aluno 14. // Atribuindo valores os dados dos alunos 15. aluno1.nome = “João” 16. aluno1.idade = 18 17. aluno1.notaMatematica = 8.5 18. aluno1.notaPortugues = 7.0 19. aluno2.nome = “Maria” 20. aluno2.idade = 17 21. aluno2.notaMatematica = 9.0 22. aluno2.notaPortugues = 8.5 23. // Exibindo informações dos alunos 24. escreva(“Dados do aluno 1:”) 25. escreva(“Nome: “, aluno1.nome) 26. escreva(“Idade: “, aluno1.idade) 27. escreva(“Nota de Matemática: “, aluno1. notaMatematica) 28. escreva(“Nota de Português: “, aluno1. NotaPortugues) 29. escreva(“Dados do Aluno 2:”) 30. escreva(“Nome: “, aluno2.nome) 31. escreva(“Idade: “, aluno2.idade) 32. escreva(“Nota de Matemática: “, aluno2. notaMatematica) 33. escreva(“Nota de Português: “, aluno2. notaPortugues) 34. fimalgoritmo ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 117 É sempre bom lembrar que estamos diante de uma estrutura heterogênea, já que aluno contém diferentes tipos de dados, como String para o nome, inteiro para a idade e real para as notas. Cada instância da estrutura aluno (aluno1 e aluno2) armazena dados específicos sobre um aluno, e os exibe antes do término do algoritmo. Das linhas 15 a 22, o algoritmo atribui valores aos campos, o que não é uma prática tão comum. Normalmente seria dada ao usuário a possibilidade de inserir esses dados via digitação, através do comando de leitura. Embora este algoritmo tenha sua utilidade, ele ainda fica nos devendo uma certa flexibilidade na quantidade de alunos com a qual é possível trabalhar. Imagine se, ao invés de dois alunos, tivéssemos a necessidade de realizar algum processamento sobre 100 alunos. Seria muito improdutivo reproduzir mais 98 vezes as entradas e saídas conforme realizadas no algoritmo de exemplo. Para esta tarefa, no entanto, podemos contar com um recurso capaz de armazenar dados do mesmo tipo sob uma mesma estrutura. Sim, podemos usar vetor para manipular vários registros de alunos de forma conveniente e organizada. Observe o algoritmo que segue: 1. Algoritmo armazenarAlunos 2. //Definindo o registro para representar um aluno 3. tipo aluno = registro 4. inicio 5. nome: caractere 6. idade: inteiro 7. notaMatematica: real 8. notaPortugues: real 9. fim 10. //Declarando um vetor de alunos com tamanho 10 11. var 12. vetorAlunos: vetor[1..10] de aluno 13. i: inteiro 14. //Inicializando os dados dos alunos usando um loop 15. para i de 1 até 10 faça 16. escreva(“Informe o nome do aluno “, i, “: “) 17. leia(vetorAlunos[i].nome) 18. escreva(“Digite a idade do aluno “, i, “: “) 19. leia(vetorAlunos[i].idade) 20. escreva(“Informe a nota de Matemática do aluno “, i, “: “) ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 118 21. leia(vetorAlunos[i].notaMatematica) 22. escreva(“Informe a nota de Português do aluno “, i, “: “) 23. leia(vetorAlunos[i].notaPortugues) 24. fimpara 25. // Exibindo informações dos alunos usando um loop 26. para i de 1 até 10 faça 27. escreva(“Dados do aluno “, i, “:”) 28. escreva(“Nome: “, vetorAlunos[i].nome) 29. escreva(“Idade: “, vetorAlunos[i].idade) 20. escreva(“Nota de Matemática: “, vetorAlunos[i]. notaMatematica) 31. escreva(“Nota de Português: “, vetorAlunos[i]. notaPortugues) 32. fimpara 33. fimalgoritmo Observe que, na linha 12, um vetor de registros de alunos é criado, o que significa que temos a liberdade de criarmos vetores de qualquer tipo, inclusive aqueles definidos pelo desenvolvedor. A leitura e a exibição de elementos do vetor se dão de modo semelhante àquele verificado em vetores de tipos pré-definidos, com o acréscimo do ponto ( . ) que separa o nome do vetor do nome do campo. Terminamos por aqui mais este encontro, com a recomendação de que você se aprofunde no assunto através de outras leituras e aprimore sua habilidade prática com exercícios. Até a próxima! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 119 CAPÍTULO 13 CONCEITO E UTILIZAÇÃO DE STRINGS Nem só de números são compostos os algoritmos. Embora os comandos e expressões que abordamos até o momento tivessem atuação em elementos numéricos, há certos tipos de problemas que demandam a utilização de recursos avançados aplicados em sequência de caracteres, ou strings. Os mesmos comandos que implementam nossas estruturas sequências, condicionais e de repetição são usados para a manipulação de strings, mas teremos a oportunidade de introduzir neste capítulo algumas funções que viabilizam o tratamento mais detalhado de uma sequência de caracteres, o que inclui acesso a um elemento específico, concatenações e comparações, entre outras. Esperamos que você tenha ótimo aproveitamento de mais este encontro! 13.1 Manipulação de strings através de funções A possibilidade de manusear strings por meio de funções previamente definidas compõe parte essencial da criação de algoritmos, seja em pseudocódigo,seja em uma linguagem de programação. No VisualG, a manipulação de strings é realizada através de funções específicas que permitem a execução de diversas operações, incluindo concatenação e extração de substrings. Antes de abordarmos estas funções, vale repassarmos pelos procedimentos de declaração e atribuição associados a cadeias de caracteres. Em VisualG, a declaração se dá como segue: var nome: caractere Observa-se, portanto, que o tipo pré-definido chamado caractere está associado a uma cadeia de caracteres, conforme introduzido em nosso último encontro. A atribuição de caracteres a variáveis é feita com o operador <- e o sentido da atribuição é aquele ao qual estamos acostumados: do lado esquerdo fica a variável à qual está sendo associado um valor e, à direita da expressão, fica o valor literal, conforme exemplo: nome_do_cliente <- “Carlos Alberto da Costa” ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 120 Nos itens seguintes serão apresentadas as principais operações que você poderá executar com strings em VisualG: 13.1.1 Concatenação Caso tenha curiosidade, você encontrará no dicionário os termos “ligar” e “juntar” associados à palavra concatenar. Em termos objetivos, é exatamente isso que este recurso realiza entre cadeia de caracteres: através do sinal de “ + “, é possível juntar duas strings em uma só. Imagine que uma variável chamada nome, do tipo caractere, tenha sido declarada e que você deseja juntar ao nome nela contido a expressão “Olá”. A variável saudacao receberá o resultado desta junção entre strings, conforme segue: saudacao <- “Olá, “ + nome 13.1.2 Comprimento O comprimento de uma cadeia de caracteres é dado pelo número de caracteres que a compõe. Embora as linguagens de programação implementem funções identificadas com termos na língua inglesa, nós manteremos a compatibilidade com o VisualG, na medida do possível. A função, portanto, que retorna o comprimento de uma string é compr(), nome derivado do termo comprimento. Observe o trecho que segue: len_texto <- compr(“Oi, mundo!”) escreva (“O comprimento da string é: “, len_texto) Este código atribui a quantidade de caracteres à variável len_texto e, em seguida, imprime esta quantidade em tela. 13.1.3 Acesso a parte de uma string É comum que o problema a ser resolvido pelo algoritmo demande que o desenvolvedor tenha acesso a apenas um trecho da cadeia de caracteres. Para isso, a função copia() foi criada. Ela permite extrair parte de uma string (ou uma substring) com base em uma posição inicial e um comprimento. Observe o código que segue: texto <- “Nome e Sobrenome” sobrenome <- copia(texto, 8, 9) Escreva(“Sobrenome: “, sobrenome) ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 121 A função copia(texto, posição_inicial, posição_final) recupera a trecho contido entre o valor de posição_inicial e o valor de posição_final, na cadeia de caracteres contida em texto. Um exemplo que ilustra essa funcionalidade é mostrado logo abaixo. Algoritmo “SeparaData” var data, dia, mes, ano: caractere inicio escreval (“Informe uma data no formato dd/mm/aaaa: “) leia (data) dia <- copia(data;1;2) mes <- copia(data;4;2) ano <- copia(data;7;4) escreval(“Dia: “ + dia) escreval(“Mês: “ + mes) escreval(“Ano: “ + ano) fimalgoritmo Este algoritmo solicita que o usuário informe uma data no formato dd/mm/aaaa e, após a leitura da string, passa a selecionar trechos dela para atribuí-los às variáveis dia, mes e ano. Note que a primeira função copia seleciona o trecho que vai do primeiro caractere da string data até o segundo caractere desta mesma string. Com isso, é possível atribuir o dia à variável dia. O mesmo procedimento é executado em relação a mês e ano, com o cuidado de não selecionar a barra separadora. Por último, preste atenção nos comandos de escrita do algoritmo. A letra “l” colocada após o escreva não constitui erro de digitação. Na verdade, ele informa que, após a exibição em tela da mensagem, haverá um salto para a próxima linha. 13.1.4 Localização Os recursos criados para manipulação de strings incluem funcionalidade para que o desenvolvedor consiga localizar uma sequência de caracteres em uma determinada sequência. Com este recurso é possível, por exemplo, encontrar uma palavra dentro de uma cadeia maior de caracteres. O nome que usaremos para esta função é pos(sequencia, palavra), que remete a posição. Os dois argumentos inseridos na função indicam, respectivamente, a sequência que se deseja buscar e a string na ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 122 qual será feita a busca. O retorno será 0 (zero) caso a substring não esteja contida na string. Observe o algoritmo que segue. O objetivo dele é o de retornar, por meio de uma variável do tipo inteiro, a posição de um caractere numa sequência. Algoritmo “Posição” var palavra: caractere sequencia: caractere posicao: inteiro inicio escreval(“Informe uma palavra qualquer:”) leia(palavra) escreval(“Informe um caracter ou sequência de caracteres que deve ser buscada na palavra informada:”) leia(sequencia) posicao <- Pos(sequencia, palavra) escreval(“O caractere foi encontrado na posição “, posicao) fimalgoritmo Tomadas de modo isolado, cada uma destas funções tem sua importância resguardada no contexto da manipulação de strings. No entanto, a utilidade destes recursos aumenta quando usados em conjunto, de modo colaborativo. O código que segue utiliza três funções para atingir o objetivo de separar o nome do sobrenome, conforme análise que será feita na sequência. Observe: 1. Algoritmo “Sobrenome” 2. var 3. nome, sobrenome: caractere 4. quantidade, espaco: inteiro 5. inicio 6. escreva(“Informe seu nome e seu sobrenome, separados por espaço: “) 7. leia (nome) 8. quantidade <- compr (nome) 9. espaço <- pos (“ “, nome) 10. sobrenome <- copia (nome, espaço + 1, quantidade) 11. escreva (“Seu sobrenome é “, sobrenome, “ “, quantidade) 12. fimalgoritmo ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 123 Nas linhas 3 e 4 são declaradas as variáveis que serão utilizadas posteriormente no processamento das sequências, com destaque para quantidade e espaço. A primeira conterá o tamanho do nome informado, incluindo o espaço entre nome e sobrenome. Já a variável espaço armazenará a posição absoluta do espaço no nome digitado. Essa informação será essencial para que o nome possa ser separado do sobrenome. Nas linhas 6 e 7 temos a orientação ao usuário e a leitura do nome. Já na linha 8, a função compr() se incumbe de obter o comprimento do nome completo informado pelo usuário e o valor obtido é armazenado na variável quantidade, do tipo inteiro. Na sequência, a função pos() é usada para obter a posição em que o espaço se encontra na string nome. Por fim, na linha 10, o resultado do processamento das funções até agora utilizadas servirá para viabilizar a extração do sobrenome, que está localizado da posição espaco + 1 até o último caractere da string. A linha 11 contém o comando que exibe o sobrenome. 13.1.5 Outras funções O conjunto de recursos específicos para manipulação de sequências de caracteres ainda incluem outras funções, não menos importantes do que aquelas que acabamos de abordar. Mencionaremos duas delas: maiusc (c: caractere): retorna um valor do tipo caractere contendo a sequência c em letras maiúsculas. minusc (c: caractere): retorna um valor do tipo caractere contendo a sequência c em letras minúsculas. Isto posto, estamos aptos a desenvolveremos na sequência um algoritmo que utiliza funções para manipulação de strings. 13.2 Exemplo de algoritmo de manipulação de strings Nada melhor do que desenvolvermos mais um exemplo de utilização dasfunções relacionadas a strings para fixarmos como utilizá-las. Neste exemplo, vamos pedir ao usuário para digitar uma frase, e o algoritmo irá realizar algumas operações básicas, quais sejam exibir o comprimento da string, extrair uma parte dela e concatenar duas sequências de caracteres. Observe o código: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 124 1. Algoritmo “ExemploManipulacaoDeStrings” 2. // Este algoritmo solicita ao usuário que informe uma 3. // frase, realiza com ela operações de strings 4. // e exibe os resultados. 5. var 6. frase, parte, novaFrase: caractere 7. comprimento: inteiro 8. inicio 9. // Solicita ao usuário que digite uma frase 10. escreva(“Digite uma frase: “) 11. leia(frase) 12. // Obtém e exibe o comprimento da string 13. comprimento <- compr(frase) 14. escreval (“O comprimento da frase é: “, comprimento) 15. // Extrai e exibe uma parte da string 16. parte <- copia(frase, 2, 5) 17. escreval(“Parte da frase: “, parte) 18. // Concatena uma nova string à frase original 19. escreva(“Digite uma nova parte para concatenar: “) 20. leia(novaFrase) 21. frase <- frase + “ “ + novaFrase 22. // Exibe a frase resultante após a concatenação 23. Escreva(“Frase após a concatenação: “, frase) 24.fimalgoritmo Era isso que gostaríamos que você conhecesse como início da sua caminhada no mundo das sequências de caracteres. Continue buscando por leituras adicionais e não deixe de praticar com exercícios. Até a próxima! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 125 CAPÍTULO 14 MODULARIZAÇÃO O processo pelo qual adquirimos conhecimento e habilidades em algoritmos é marcado, em suas etapas iniciais, pela abordagem de problemas relativamente simples e que demandam a criação de código com poucas linhas. À medida que adquirimos mais conhecimento, nos tornamos capazes de combinar várias estruturas e comandos em um só código, de modo a resolver problemas de complexidade crescente. Neste aspecto, a aprendizagem de algoritmos em nada difere da aquisição de conhecimento em outras áreas. Durante a atuação profissional de um desenvolvedor, no entanto, ele será chamado a resolver problemas de grande complexidade, e normalmente atuará como membro de uma equipe de profissionais. O conteúdo deste nosso encontro abordará justamente um dos recursos disponíveis para a divisão da complexidade de um algoritmo em partes menores e mais facilmente gerenciáveis. Começamos aqui, portanto, nossa caminhada pela modularização de código. Continue conosco! 14.1 O conceito e a importância da modularização Um dos vários significados da palavra módulo nos remete a ideia de parte de algo. Em sentido amplo, modularizar é entendido como “quebrar em partes”, com a consequente formação de módulos distintos e mutuamente relacionados. Em nosso contexto, modularizar significa criar subalgoritmos que tratam, idealmente, de uma única tarefa e que mantenham a menor dependência possível entre si. Para ilustrar essa ideia, tomaremos como referência um sistema acadêmico. Como você deve imaginar, trata-se de um problema complexo a ser resolvido, já que inclui muitas situações, manipula muitos dados e oferece ao usuário muitas opções de interação com o algoritmo. Ao se deparar com a missão de abordar um problema deste porte, o profissional de Tecnologia da Informação - que não necessariamente será o desenvolvedor – deverá identificar as grandes tarefas que o sistema irá resolver. Ao assumirmos a ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 126 posição deste profissional, identificamos alguns módulos que o futuro sistema deverá contemplar: • Cadastramento de usuários • Matrícula de alunos • Gestão de notas e frequências • Gestão financeira Naturalmente que, a depender da proposta do sistema, muitos outros módulos seriam acrescentados a estes. No entanto, com uma breve análise, já podemos inferir que o tratamento das funcionalidades que indicamos não poderá se dar a partir de um algoritmo “monolítico” e que aborde todos os problemas com uma solução unitária. Ao contrário disso, o problema deverá ser tratado em partes. É comum, inclusive, que grupos de desenvolvedores fiquem responsáveis pela criação de módulos específicos, o que possibilita otimização do trabalho A modularização, portanto, constitui um recurso indispensável na construção de algoritmos eficientes e compreensíveis. Através dela, é possível de dividir um programa complexo em partes menores e mais gerenciáveis, que realizam funções específicas e idealmente independentes, o que facilita o desenvolvimento, a manutenção e a compreensão do código. Separamos algumas razões para implementação da modularização nos algoritmos: Desenvolvimento colaborativo: conforme já mencionamos, em projetos de software que envolvem equipes de desenvolvedores, a modularização permitirá divisão clara de tarefas. Cada membro da equipe pode trabalhar em módulos específicos sem interferir no trabalho dos outros, promovendo um desenvolvimento mais eficiente e organizado. Melhor legibilidade e compreensão: ao dividir um algoritmo em módulos, o código tende a se tornar mais legível. Cada módulo é responsável por uma tarefa específica, facilitando a compreensão do propósito e da lógica de cada parte do algoritmo. Isso é essencial para colaboração entre programadores e para a manutenção a longo prazo do código. Reusabilidade: módulos independentes são reutilizáveis em diferentes partes do programa ou em projetos distintos. Se uma função específica é necessária em vários ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 127 contextos, o módulo correspondente pode ser facilmente incorporado, economizando tempo e esforço de desenvolvimento. Manutenção Simplificada: quando um algoritmo é modularizado, a manutenção do código torna-se mais fácil. As alterações ou correções podem ser feitas em módulos específicos sem afetar o funcionamento de outras partes do programa. Isso reduz a propagação de erros e facilita a identificação e solução de problemas. Facilidade de Testes: módulos independentes são mais fáceis de testar, pois suas entradas e saídas podem ser isoladas. Isso simplifica o processo de identificação e correção de bugs, já que é possível focar nos módulos específicos que apresentam problemas. Escalabilidade: a modularização facilita a escalabilidade do software. Novos recursos ou funcionalidades podem ser adicionados como módulos independentes, sem a necessidade de reescrever o código existente. Isso torna o sistema mais flexível e adaptável a mudanças futuras. Não faltam motivos para construirmos algoritmos em módulos, não é mesmo? A modularização é uma prática essencial na construção de algoritmos eficientes e de fácil manutenção. Ao dividirem um programa em partes menores e independentes, os desenvolvedores podem melhorar a legibilidade, a reusabilidade e a escalabilidade do código, facilitando o processo de desenvolvimento e a manutenção contínua do software. Feita esta contextualização, avançamos rumo à implementação da ideia de modularização. Daqui em diante, utilizaremos os termos subprograma e/ou subrotina como sinônimos de módulos e, como de costume, usaremos os recursos oferecidos pelo VisualG para colocá-los em prática. 14.2 Chamada de subprogramas Embora o termo subprograma possa ser entendido como equivalente ao termo módulo, ele nos remete à temas mais específicos dos algoritmos. Podemos, inclusive, definir subprograma como um programa que, de forma subordinada ao programa principal, realiza uma determinada subtarefa. Este trecho conceitual nos revela a existência, portanto, a existência de um programa principal e a subordinação dos subprogramas a ele. Em termos práticos, os subprogramas são chamados (ou invocados) apartir do corpo do programa principal, assim como costumamos invocar os comandos pré-definidos. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 128 Quando a execução do subprograma é terminada, o fluxo do algoritmo é desviado para o comando do programa principal situado imediatamente após a chamada do comando. Embora haja um desvio no fluxo sem que, necessariamente, uma estrutura condicional tenha sido usada, não estaremos diante de um desvio incondicional, algo vetado na programação estruturada. A chamada de um subprograma gera um desvio provisório no fluxo de execução, justificada pela necessidade de se executar um trecho de código distante do ponto da chamada. A figura 14.1 exibe o esquema geral de uma chamada de subprograma. Programa Principal (PP) Subprograma 1 Comando1 1 Comando1 2 Comando2 2 Comando2 3 Comando3 3 Comando3 4 Chamada_sub 4 Fim 5 Comando5 n Comandon Figura 14.1: esquema geral de uma chamada de subprograma. Fonte: o autor Ao ser encontrado o término do subprograma, o fluxo é novamente desviado para o programa principal, que poderá conter outras chamadas. Na sequência abordaremos a ideia de retorno de valor ao programa principal, após o processamento do subprograma. 14.3 Retorno de valor Como sabemos, a chamada feita a um subprograma desvia o fluxo do algoritmo para outro trecho de código que será executado até seu final ser atingido. Com base na natureza do problema, o desenvolvedor deverá decidir se o subprograma retornará – ou não – um determinado resultado ao programa principal, fato que definirá o tipo de procedimento que será criado. Antes de abordarmos os dois tipos de procedimentos que poderemos utilizar em nossos algoritmos, vale a pena tratarmos do retorno de um resultado ao programa principal. Imaginemos a seguinte situação: você está criando um algoritmo que demandará a exibição em tela de uma mensagem por 20 vezes durante a execução do algoritmo. Entre uma exibição e outra, apenas um determinado nome será alterado entre as mensagens. Embora seja possível a inclusão de 20 comandos escreva(), essa ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 129 solução não seria inteligente e, por isso, você optou pela criação de um subprograma que executará a exibição da mensagem sempre que for chamado. A respeito de como fazer com que este subprograma exiba um nome diferente a cada execução, não se preocupe: abordaremos esta questão antes do final do nosso conteúdo. A segunda situação apresentará uma característica distinta da primeira, e que nos ajudará no entendimento de ambas. Imagine agora que a solução a ser criada pelo seu algoritmo inclua uma operação de soma entre dois números, repetida também 20 vezes. Com o recurso que já conhece, você poderá codificar essa soma em um subprograma e chamá-lo 20 vezes, informando valores diferentes a cada chamada. O papel do subprograma é o de realizar a soma e retornar o resultado ao programa principal. A diferença entre a primeira situação e a segunda é justamente esta: a mera exibição da mensagem não requer nenhum retorno de resultado ao programa principal, ao passo que a execução da soma demanda que o resultado desta operação seja retornado ao mesmo programa principal. A identificação – feita pelo desenvolvedor - destas situações de retorno ou não retorno de um resultado determinará o tipo de subprograma a ser utilizado e este será nosso próximo assunto. 14.4 Implementação de subprogramas Nas próximas linhas trataremos dos dois tipos de subprogramas com os quais poderemos contar na construção de nossos algoritmos. Para muitas linguagens, sobretudo as mais novas, a diferenciação entre estes dois tipos se dá de modo bastante sutil e muitas vezes sequer é mencionada a existência de duas modalidades de subprogramas pelo tutorial da linguagem. No entanto, em nosso contexto, será importante conhecermos as especificidades de cada tipo, em detalhes. 14.4.1 Procedimento Um procedimento é um subprograma que não retorna valor algum ao programa principal. A sua execução, por si só, realiza o processamento planejado pelo criador do algoritmo, sem a necessidade de geração de um resultado em formato numérico textual que deva ser devolvido a programa chamador. Considerando novamente o VisualG como ferramenta para criação de subprogramas, declararemos assim um procedimento: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 130 procedimento <nome-de-procedimento> (parâmetros>) // variáveis locais inicio // comandos fimprocedimento Pelas regras do VisualG, esta declaração deve ser posicionada entre o final da declaração de variáveis e a linha início do programa principal, conforme teremos possibilidade de visualizar. Note que um procedimento tem um nome e, por meio dele, teremos como chamá-lo no programa principal. Além disso, ele conta também com uma área de declaração de variáveis próprias do procedimento e com comandos, que em nada diferem dos que já aprendemos. Para ilustrar estas ideias, utilizaremos um exemplo simples. Observe: algoritmo “ImprimeMediaProcedimento” var nota1, nota2, nota3, media: real procedimento imprimeMedia() escreva (“A média é: “, media) fimProcedimento inicio escreva(“Digite a primeira nota: “) leia(nota1) escreva(“Digite a segunda nota: “) leia(nota2) escreva(“Digite a terceira nota: “) leia(nota3) media <- (nota1 + nota2 + nota3) / 3 // Chamando o procedimento para imprimir a média imprimeMedia() fimalgoritmo Neste exemplo, o algoritmo solicita ao usuário que digite três notas, calcula a média e chama o procedimento imprimeMedia() para exibir a média na tela. Observe a separação entre programa principal e procedimento: o código tem início com o nome do algoritmo e com a declaração de variáveis, conforme estamos acostumados a fazer. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 131 Depois disso, o procedimento é codificado, segundo o formato geral anteriormente especificado. Por fim, a palavra reservada inicio marca o começo da área de código. É desta forma, objetivamente, que o módulo aparece destacado do programa principal. Antes de abordarmos a segunda modalidade de subprograma, valem duas menções importantes: 1) As variáveis declaradas no corpo do programa principal recebem o nome de variáveis globais e são visíveis no programa todo, inclusive nos subprogramas subordinados a ele. Quando declaradas no corpo do procedimento, as variáveis são chamadas de locais e são visíveis apenas no corpo do procedimento. Neste contexto, ser “visível” significa poder ser acessada e alterada. 2) Na prática, a solução para este problema não demandaria a criação de um procedimento. Afinal, ao modularizarmos o sistema, não obtivemos economia de código, nem aumentamos a facilidade de manutenção. No entanto, através deste código simples, poderemos expandir a utilização de procedimentos para casos mais complexos e que, de fato, os demandem. 14.4.2 Função A função é um subprograma que retorna um valor ao programa que a chamou (ou programa principal). Assim como um procedimento, ela deve ser posicionada entre o final da declaração das variáveis e a linha de início do programa. O formato geral de uma função é exibido na sequência: funcao <nome-de-função> : tipo-de-dado // variáveis locais inicio // comandos retorne fimfuncao O <nome-de-função> obedece às mesmas regras de nomenclatura das variáveis. A distinção entre procedimento e função começa no tipo de dado que é retornado para a função e explicitado no cabeçalho dela. Quando o desenvolvedor inicia uma função com, por exemplo, função soma : inteiro, ele indica que o valor a ser retornado pela função será do tipo inteiro. Antes de encontrar o fimfuncao, o fluxo ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINONETO FACULDADE CATÓLICA PAULISTA | 132 do algoritmo passa pelo comando retorne, que enviará ao programa principal o resultado calculado pela função. A fixação destes conceitos será promovida por meio do desenvolvimento do exemplo que segue. 1. algoritmo “FuncaoRetornaInteiro” 2. var 3. numero, resultadoFinal: inteiro 4. funcao inteiro calculaQuadrado(numero: inteiro):inteiro 5. var 6. resultado: inteiro 7. inicio 8. resultado <- numero * numero 9. retorne resultado 10.fimfuncao 11. inicio 12. escreva(“Informe o valor do qual se deseja calcular o quadrado: “) 13. leia(numero) 14. // Chamando a função e armazenando o resultado retornado 15. resultadoFinal <- calculaQuadrado(numero) 16. escreva(“O quadrado de “, numero, “ é: “, resultadoFinal) 17. fimalgoritmo Na linha 1 é nomeado o algoritmo e, logo na linha 3 as variáveis globais (ou seja, aquelas que podem ser acessadas de qualquer lugar do programa, incluindo o interior da função). Na linha 11 é iniciado, de fato, o processamento do algoritmo. Na linha 13, o valor do qual se deseja o quadrado é lido pelo teclado. Logo na sequência, a função calculaQuadrado() é chamada em formato de atribuição, o que significa que o fluxo do algoritmo será desviado para a função e o resultado nela apurado será atribuído à variável resultadoFinal, que deve ser do mesmo tipo da função. Na sequência, o resultado da operação é exibido em tela. As linhas de 4 a 10 compõem a função e, em seu corpo, é possível verificar a declaração da variável resultado, que poderá ser utilizada apenas nos limites da função. Objetivamente, a função apenas multiplica um valor por ele mesmo e retorna ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 133 o resultado na variável resultado. O número que é multiplicado por ele próprio para a apuração do seu quadrado chega até a função através de passagem de parâmetro, assunto a ser abordado em nosso próximo encontro. Antes de terminarmos este capítulo, vale a pena resgatarmos o primeiro item da nossa argumentação em favor da modularização: a implementação do sistema acadêmico. Com o conhecimento que adquirimos durante este encontro, fica facilitada a tarefa de compreender as formas de implementação de um sistema complexo. Embora nossa abordagem do problema seja propositalmente simplificada, podemos imaginar um programa principal que ofereça ao usuário a possibilidade de escolher uma entre 5 opções, por meio de um menu semelhante ao que segue: 1 – Cadastramento de usuários 2 – Matrícula de alunos 3 – Gestão de notas e frequências 4 – Gestão financeira 5 – Sair do programa Informe um número entre [1 – 4] ou 5 para sair. Utilizando a estrutura de seleção múltipla escolha..caso, será possível direcionar as chamadas das funções de cadastramento, matrícula, gestão de notas, gestão financeira ou saída, a depender da escolha do usuário. Que tal fazer a implementação desta solução? Boa sorte e até nosso próximo encontro! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 134 CAPÍTULO 15 PARÂMETROS E ARGUMENTOS EM SUBPROGRAMAS O conhecimento relacionado a utilização de subprogramas em nossos algoritmos certamente nos apresentou novos e eficientes meios de solucionar os problemas com os quais nos deparamos em nossa jornada. Ter a possibilidade de abordar partes do problema em módulos específicos tornou possível a solução de situações mais complexas do que aquelas cuja resolução se baseava em apenas ou outra estrutura. A utilização de subprogramas, portanto, será a chave para que nossa capacidade de abstrair problemas mais complexos atinja ótimos níveis. Embora o conteúdo apresentado e desenvolvido em nosso último encontro tenha sido adequado para introduzir o assunto, ainda precisamos entender como se dá a interação entre o programa principal e os subprogramas e isto se efetivará através do aprofundamento em variáveis globais e locais, bem como por meio do estudo de parâmetros e argumentos que transitam entre os módulos do algoritmo. Neste nosso último encontro, esperamos que os conhecimentos adquiridos preparem você para alçar grandes voos em desenvolvimento de soluções computacionais. 15.1 Conceito de parâmetros e argumentos Grande parte das vantagens obtidas pela adoção dos subprogramas em algoritmos está baseada na interação entre os módulos, viabilizada pela transição de variáveis. Por exemplo, a reusabilidade do código - que se traduz na capacidade de se utilizar determinado código em várias partes do programa – é potencializada pela passagem destas variáveis entre os componentes do programa. Quando você tiver a missão de criar subprogramas flexíveis e reutilizáveis, o uso eficiente de parâmetros e argumentos desempenhará papel central no atingimento deste objetivo. A possibilidade da realização da troca de dados entre subprogramas e programa principal será essencial para municiar uma função ou procedimento, fato que permitirá a criação de algoritmos adaptáveis e dinâmicos. Nossa exploração deste tema começa com os parâmetros. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 135 Os parâmetros são variáveis que uma função ou um procedimento esperam receber. Eles são declarados no próprio cabeçalho do subprograma e devem ser entendidos como espaços reservados para valores que serão passados quando a função for chamada. Observe o código que segue: procedimento soma (a,b: inteiro) inicio res <- a + b fimprocedimento No cabeçalho do procedimento chamado soma são declaradas duas variáveis do tipo inteiro, a e b. Elas são, portanto, os parâmetros deste procedimento e deverão receber dois valores inteiros vindos do programa principal. Mesmo neste exemplo bastante simples, é possível entender a flexibilidade que a passagem de parâmetros confere ao algoritmo, já que quaisquer valores poderão ser transmitidos a estas duas variáveis. Já os argumentos são os valores reais passados para os parâmetros de um subprograma durante sua chamada. Eles podem ser constantes, variáveis ou até mesmo expressões. A fim de arrematarmos o exemplo da soma, observe o trecho de código do programa principal: var x, y: inteiro leia (x) leia (y) soma (x, y) As variáveis x e y, portanto, atuam como argumentos, já que elas contêm os valores que chamamos de “reais” logo acima. Neste caso específico, a atribuição deste valor se deu através de entrada de usuário. Uma observação deve ser feita acerca deste exemplo: os argumentos e os parâmetros devem coincidir em número e em tipo. Em outras palavras, se dois argumentos são passados via chamada de subprograma, então dois serão os parâmetros que devem recebê-los. A mesma regra vale para o tipo da variável. Além disso, o nome das variáveis que integram os argumentos não precisam ser os mesmos nomes dos parâmetros. Neste exemplo, o valor contido na variável x é passado à variável a e o valor contido na variável y é passado à variável b. O desenvolvimento de um exemplo completo ajudará a fixação destes conceitos. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 136 algoritmo “calculadora” var opcao: inteiro valor1, valor2, so, mu, di, su: real funcao soma (n1, n2: real): real var s: real inicio s < - n1 + n2 retorne s fimfuncao funcao multiplicacao (n1, n2: real): real var mult: real inicio mult < - n1 * n2 retorne mult fimfuncao funcao subtracao (n1, n2: real): real var sub: real inicio sub < - n1 – n2 retorne sub fimfuncao funcao divisao (n1, n2: real): real var div: real inicio se (n2>0) div <- n1 / n2 senão div <- 0 retorne div fimfuncao inicio escreval (“Operações aritméticas com dois valores”) escreva (“Informe o primeiro valor: ”) leia (valor1) escreva (“Informe o segundo valor: ”) leia (valor2) escreval (“Agora informe a operaçãoque deseja que seja ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 137 feita: ” ) escreval (“1 - Soma” ) escreval (“2 - Multiplicação” ) escreval (“3 - Subtração”) escreval (“4 - Divisão” ) leia (opcao) escolha opcao caso 1 so <- soma (valor1, valor2) escreva (“O resultado da operação é: “, so) caso 2 mu <- multiplicação (valor1, valor2) escreva (“O resultado da operação é: “, mu) caso 3 su <- subtracao (valor1, valor2) escreva (“O resultado da operação é: “, su) caso 4 di <- divisao (valor1, valor2) escreva (“O resultado da operação é: “, di) outrocaso escreva (“Opção inválida”) fimescolha fimalgoritmo Este algoritmo executa uma das quatro operações aritméticas, a critério da escolha do usuário. Observe que as variáveis opcao, valor1, valor2, so, mu, di, su foram declaradas no corpo do programa principal e, por isso, terão visibilidade global, ou seja, no programa todo. Na sequência são declaradas as quatro funções do programa, uma para cada operação aritmética. Essas funções (soma, multiplicação, subtração e divisão) apresentam basicamente a mesma estrutura: recebem dois argumentos reais e retornam um número real. O processamento contido em cada uma delas também é bastante simples: executam uma das operações e retornam um valor ao programa principal, através do comando retorne. ANOTE ISSO Toda operação de divisão feita por um algoritmo requer um cuidado especial: o desenvolvedor deve testar a validade do denominador, ou seja, um comando condicional deve verificar se aquele valor é diferente de zero. Em um programa real, uma divisão por zero não prevista causa parada súbita da execução do código, configurando um “erro em tempo de execução”. Em nosso exemplo, optamos por atribuir o valor zero de retorno da função neste caso. Outra particularidade associada à operação de divisão é o fato de que o valor resultante pode ser fracionário, o que obriga o desenvolvedor a declarar as variáveis que conterão os números como sendo do tipo real. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 138 No programa principal o usuário é convidado a informar dois valores que serão processados por uma das quatro operações. Na sequência, ele deve informar a operação a ser realizada e a variável opcao, que é do tipo inteiro, guardará esse dado. Depois disso, o comando escolha..caso será usado para uma das quatro opções possíveis e, com exceção de uma digitação incorreta, servirá para chamar uma das quatro funções já abordadas. Embora as vantagens obtidas pela utilização de parâmetros sejam muito parecidas com os argumentos favoráveis à modularização de um algoritmo, vale a pena retomarmos estes itens, desta vez de modo mais específico aos parâmetros. Reutilização de código: ao utilizar parâmetros, podemos reutilizar funções em diferentes contextos, adaptando seu comportamento de acordo com os valores passados. Flexibilidade: a capacidade de passar argumentos permite que os algoritmos sejam mais flexíveis e se ajustem dinamicamente às diferentes situações. Manutenção simplificada: a alteração do comportamento de uma função pode ser realizada ajustando os valores passados como argumentos, sem a necessidade de modificar a própria função. Legibilidade aprimorada: o uso correto de parâmetros e argumentos contribui para códigos mais legíveis e compreensíveis, facilitando a colaboração entre desenvolvedores. 15.2 Conceito variáveis globais e locais No contexto dos algoritmos criados com base em subprogramas, a utilização de variáveis globais e locais fornece meio eficiente para a correta estruturação destes algoritmos. Nas próximas linhas, abordaremos as diferenças entre estas duas modalidades de variáveis a fim de que possamos compreender o papel de cada uma no contexto dos algoritmos modulares. Variáveis locais: são aquelas declaradas dentro de um bloco específico do programa, como uma função ou um procedimento. Elas têm um escopo limitado à região em que foram definidas, o que significa que só podem ser acessadas e modificadas dentro desse contexto local. Ao sair desse bloco, as variáveis locais são destruídas, liberando a memória que ocupavam. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 139 Observe o exemplo que segue: algoritmo “exemploVariavelLocal” var idade: inteiro procedimento exemplo var nome: caractere inicio idade <- 20 escreva(“Idade dentro do procedimento: “, idade) fim inicio exemplo() //A variável ‘nome’ não está acessível aqui, por ser local ao procedimento //A variável ‘idade’ ainda é acessível aqui, por ser global escreva(“Idade fora do procedimento: “, idade) fimalgoritmo Este código serve para nos mostrar que, por ser global, a variável idade pode ser invocada a partir de qualquer contexto do algoritmo, seja no corpo do procedimento ou no corpo do programa principal. A variável nome, por sua vez, não pode ser invocada fora do contexto em que foi declarada, ou seja, do procedimento exemplo(). Variáveis globais: em contrapartida, variáveis globais são declaradas fora de qualquer bloco específico e têm um escopo que abrange todo o programa. Isso significa que elas podem ser acessadas e modificadas em qualquer ponto do código, inclusive dentro de funções e procedimentos. Entretanto, é preciso ter cuidado ao utilizar variáveis globais, pois seu uso indiscriminado pode tornar o código menos modular e mais difícil de manter. Usaremos mais uma vez um exemplo para ilustrar o conceito. algoritmo “ExemploVariavelGlobal” var idade: inteiro ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 140 procedimento alteraIdade inicio idade <- idade + 1 escreva(“Idade alterada dentro do procedimento: “, idade) fim inicio idade <- 20 escreva(“Idade antes do procedimento: “, idade) alteraIdade() //A variável ‘idade’ foi alterada dentro do procedimento escreva(“Idade depois do procedimento: “, idade) fimalgoritmo Neste exemplo, a variável idade, que tem escopo global, é exibida antes e depois de ser alterada no corpo do procedimento. Em cada um destes dois momentos, os valores exibidos são diferentes entre si: antes do procedimento, a variável apresenta o valor 20 e, depois da chamada e execução do procedimento, a variável apresenta este valor acrescido de uma unidade. A correta escolha entre variáveis locais e globais se dá com base na necessidade específica de cada situação. O uso adequado desses conceitos contribui para a criação de um código mais claro, modular e fácil de entender, fatos essenciais para o aprendizado e desenvolvimento de habilidades de criação de algoritmos. Ainda no contexto da utilização de parâmetros e argumentos, trataremos agora das duas modalidades de passagem de parâmetros admitidas na criação de algoritmos. 15.3 Passagem de parâmetros por valor e por referência Conforme tivemos a oportunidade de constatar, a passagem de parâmetros desempenha um papel fundamental na criação de nossas funções e procedimentos. A este respeito, no entanto, ainda precisamos entender as duas modalidades de passagem de parâmetros entre os módulos do algoritmo, a fim de que possamos desenvolver algoritmos ainda mais eficientes e adequados aos nossos propósitos. Estas modalidades, chamadas passagem por valor e passagem por referência, são abordadas na sequência. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 141 Passagem de parâmetro por valor: nesta modalidade, o valor da variável (ou do argumento) é copiado para o parâmetro da função ou procedimento. Isso significa que qualquer modificação feita no parâmetro dentro da função não afeta a variável original fora dela. Por padrão, o VisualGutiliza essa abordagem nas chamadas de subprogramas. Observemos o código que segue: algoritmo “PassagemPorValor” var numero: inteiro procedimento modificaValor(valor: inteiro) inicio valor <- valor + 1 escreva(“Valor dentro do procedimento: “, valor) fim inicio numero <- 5 modificaValor (numero) // A variável ‘numero’ permanece inalterada fora do procedimento escreva(“Valor fora do procedimento: “, numero) fimalgoritmo A execução deste código nos mostrará que a variável numero não teve seu valor alterado fora do procedimento. Para entender esta afirmação, note que a chamada de modificaValor passa o argumento numero ao procedimento e o parâmetro que o recebe se chama valor, do tipo inteiro. Esta variável valor, por sua vez, é alterada no corpo do procedimento, por meio da adição de uma unidade ao seu valor original. Como o parâmetro foi passado por valor, esta referida alteração não é refletida fora do procedimento em que foi feita. Passagem de parâmetro por referência: nesta modalidade, o endereço de memória da variável é passado como parâmetro. Dessa forma, qualquer modificação feita no parâmetro dentro da função afeta diretamente a variável original fora dela. Nosso exemplo de passagem por referência será desenvolvido como segue: ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 142 algoritmo “passagemPorReferencia” var a: inteiro procedimento ModificaReferencia (var b: inteiro) inicio b <- b + 1 escreva(“Valor dentro do procedimento: “, b) fim inicio a <- 10 ModificaReferencia(a) // O valor da variável a é alterado diretamente escreva(“Valor fora do procedimento: “, a) fimalgoritmo Qualquer modificação feita no conteúdo deste parâmetro passado por referência afetará também a variável global que está associada a ele. Durante a execução do subprograma, os parâmetros passados por referência são análogos às variáveis globais. No VisualG, este tipo de passagem é viabilizado pela inclusão da palavra var, antes do parâmetro declarado no subprograma. A execução deste algoritmo nos mostrará que a variável a teve seu valor alterado no procedimento e esta alteração foi refletida no programa principal. Com a abordagem deste assunto, encerramos o conteúdo que desejávamos transmitir a você. Nosso desejo é que sua vida acadêmica seja conduzida da melhor forma e que cada linha deste livro seja útil em sua vida profissional. Obrigado e boa sorte sempre! ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 143 CONCLUSÃO Ao chegarmos ao final desta jornada pelo vasto território da criação de algoritmos, é gratificante poder refletir sobre o conhecimento adquirido e as habilidades desenvolvidas ao longo destes quinze capítulos. Conforme já pontuamos, este livro foi concebido com o propósito de fornecer uma base sólida e abrangente para aqueles que buscam compreender e dominar os fundamentos da criação de soluções computacionais por meio dos algoritmos. No primeiro capítulo, exploramos as raízes dos algoritmos, entendendo seu conceito e suas diversas aplicações que permeiam o nosso cotidiano digital. O breve histórico dos algoritmos lançou luz sobre a evolução dessa disciplina em constante crescimento. Os capítulos subsequentes guiaram-nos por uma jornada desde as noções básicas de variáveis, constantes e tipos de dados até a manipulação de ddos por meio de expressões aritméticas, lógicas e relacionais. A introdução à programação estruturada nos equipou com ferramentas essenciais para criar códigos mais organizados e compreensíveis, utilizando estruturas sequenciais, condicionais e de repetição. Tivemos oportunidade de explorar diversas formas de representação de algoritmos, revelando técnicas valiosas para comunicar efetivamente o pensamento lógico por trás de uma solução. As estruturas condicionais, de repetição e as estruturas de dados homogêneas e heterogêneas foram desvendadas, proporcionando uma visão geral e prática para a construção de soluções eficientes. No capítulo dedicado à modularização, compreendemos a importância de organizar o código em unidades funcionais independentes, enquanto os capítulos finais sobre parâmetros e argumentos em subprogramas nos permitiram explorar ainda mais a reusabilidade e a eficiência na criação de programas. À medida que concluímos esta obra, é imperativo destacar que a jornada na programação é infinita. Este material serve como um ponto de partida, um guia que pavimenta o caminho para explorar as vastas possibilidades que a programação de computadores oferece. Esperamos você se sinta capacitado a enfrentar desafios mais complexos, aprimorando suas habilidades e expandindo seu conhecimento em um campo que está sempre em evolução. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 144 Esperamos que este material tenha cumprido seu propósito de fornecer uma base sólida, inspirar a curiosidade e preparar você para os incríveis desafios que te aguardam na jornada contínua da programação. Que cada algoritmo escrito seu seja uma expressão de criatividade, lógica e eficiência resolução de problemas. Que você faça uma ótima jornada em suas futuras explorações neste vasto universo dos algoritmos. ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 145 ELEMENTOS COMPLEMENTARES LIVRO Título: [Lógica de Programação: a construção de algoritmos e estruturas de dados] Autor: [ André Luiz Villar Forbellone e Henri Frederico Eberspächer] Editora: [Pearson Prentice Hall] Sinopse: [A programação de computadores está diretamente relacionada com a utilização de lógica e com a construção de algoritmos e estruturas de dados. Claro, simples e objetivo, este livro introduz o leitor no universo da lógica aplicada à programação de computadores. Ao final do estudo, o aluno estará capacitado a construir algoritmos, assim como a assimilar mais facilmente qualquer linguagem de programação existente ou futura. O texto não requer nenhum conhecimento prévio de informática e é independente de características de máquina. Didático, utiliza com frequência exemplos e analogias provenientes do dia a dia para facilitar a compreensão dos assuntos abordados. Cada capítulo conta com exercícios de fixação, que visam sedimentar os assuntos de cada subitem, e com exercícios propostos, que cobrem todo o conteúdo do capítulo] ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 146 FILME Título: [O Jogo da Imitação] Ano: [2014] Sinopse: [embora não seja especificamente sobre algoritmos, este filme retrata a história de Alan Turing, um matemático e cientista da computação, que desempenhou um papel crucial na quebra do código Enigma durante a Segunda Guerra Mundial. O filme destaca a importância do pensamento algorítmico e da computação em um momento histórico.] WEB [Este site em português oferece uma variedade de recursos sobre algoritmos, incluindo tutoriais, exercícios práticos e recursos didáticos. Ele é útil para estudantes que desejam iniciar a jornada em algoritmos]. https://educapes.capes.gov.br/bitstream/capes/560827/2/Apostila%20-%20Curso%20 de%20L%C3%B3gica%20de%20Programa%C3%A7%C3%A3o.pdf https://educapes.capes.gov.br/bitstream/capes/560827/2/Apostila%20-%20Curso%20de%20L%C3%B3gica%20de%20Programa%C3%A7%C3%A3o.pdf https://educapes.capes.gov.br/bitstream/capes/560827/2/Apostila%20-%20Curso%20de%20L%C3%B3gica%20de%20Programa%C3%A7%C3%A3o.pdf ALGORÍTIMO E LÓGICA DE PROGRAMAÇÃO I PROF. ROQUE MAITINO NETO FACULDADE CATÓLICA PAULISTA | 147 REFERÊNCIAS DEVLIN, Keith. The Math Gene: How Mathematical Thinking Evolved and Why Numbers Are Like Gossip. Basic Books, 2000. KNOTT, Ron. Fibonacci Numbers and the Golden Section. Disponível em: [http:// www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibnat.html] LIVIO, Mario. The GoldenRatio: The Story of Phi, the World’s Most Astonishing Number. Broadway Books, 2003. MELO, Ana Cristina Vieira de; SILVA, Flávio Soares Correa. Princípios de linguagem de programação. 3. ed. São Paulo: Blucher, 2014. PYTHON.ORG. Disponível em https://www.python.org/about/. Acesso em 18 out. 23. http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibnat.html http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibnat.html https://www.python.org/about/ _gjdgxs _30j0zll _Int_jy8JU579 _2et92p0 _gjdgxs _30j0zll _1fob9te _Int_r5mUoSWw _Int_mayhRL5s _Int_UjJSWGNT _Int_QdNaKV9a _Int_HQlAPVdJ _3znysh7 _30j0zll _1fob9te _Int_33L6b0TL _Int_UXMEi612 _gjdgxs _30j0zll _Int_DF1WZ1lB _Int_f9bwXXj1 _Int_wkTt7Xlt _Int_5uY0lSqj _Int_mh63H3oC _Int_AYSZmfml _Int_QJ6RTaum _Int_njD9xKDX _Int_u73v7RSd _Int_DZjhOKlV _Int_dh3Hb2JK _Int_Rtus6Juo _Int_HSoBNL9j _4d34og8 _30j0zll _1fob9te _1t3h5sf _30j0zll _1fob9te _30j0zll _1fob9te _35nkun2 _30j0zll _1fob9te _gjdgxs _30j0zll _heading=h.gjdgxs _heading=h.30j0zll _heading=h.gjdgxs _heading=h.30j0zll _heading=h.gjdgxs _heading=h.30j0zll _heading=h.1fob9te _heading=h.3znysh7 _gjdgxs _30j0zll _1fob9te _3znysh7 _30j0zll _1fob9te _gjdgxs INTRODUÇÃO A ALGORITMOS E LÓGICA DE PROGRAMAÇÃO VARIÁVEIS, CONSTANTES E TIPOS DE DADOS EXPRESSÕES ARITMÉTICAS, LÓGICAS E RELACIONAIS INTRODUÇÃO À ALGORITMOS ESTRUTURADOS FORMAS DE REPRESENTAÇÕES DOS ALGORITMOS ESTRUTURAS CONDICIONAIS ESTRUTURA DE REPETIÇÃO COM TESTE NO INÍCIO ESTRUTURA DE REPETIÇÃO COM TESTE NO FIM ESTRUTURA DE REPETIÇÃO COM NÚMERO DEFINIDO DE REPETIÇÕES ESTRUTURA DE DADOS HOMOGÊNEA UNIDIMENSIONAL: VETORES ESTRUTURA DE DADOS HOMOGÊNEA BIDIMENSIONAL: MATRIZES ESTRUTURA DE DADOS HETEROGÊNEA: REGISTROS CONCEITO E UTILIZAÇÃO DE STRINGS MODULARIZAÇÃO PARÂMETROS E ARGUMENTOS EM SUBPROGRAMAS