image

Access unlimited bootcamps and 650+ courses forever

60
%OFF
Vitor Oliveira
Vitor Oliveira30/04/2023 19:36
Share

Pensamento computacional para iniciantes em programação: como sistematizar a solução de problemas?

    O pensamento computacional é uma habilidade generalista que pode ser aplicada não só a computação, mas também na matemática (modelagem e otimização), ciências exatas, gestão de projetos e equipes, robótica e outras diversas áreas,e inclusive na redação deste artigo. Entretanto na computação, o pensamento computacional torna-se fundamental e é um fator relevante para definir a senioridade do programador, engenheiro ou cientista da computação.

    Mesmo se você está começando a programar, já deve ter percebido que lidar com problemas é a base do nosso trabalho, e incluem-se aí os “bugs” (falhas inesperadas, incompatibilidade de dependências, etc…), erros de compilação (sintaxe errada) e os erros de lógica (resolução incorreta do problema). Dados os recursos cada vez mais diversos e aprimorados para lidar com erros de compilação e administrar dependências e integrações em projetos, os erros de lógica costumam ser os mais difíceis de solucionar por estarem diretamente ligados à uma interpretação incorreta da solução do problema em questão, por parte do programador.

    Por vezes, passamos longos minutos (horas, dias…) procurando pelo erro em nosso código responsável por uma saída incorreta do nosso programa. Lidar com problemas no código de maneira cada vez mais eficiente (e por “eficiência” entende-se resultados mais satisfatórios e não necessariamente mais rápidos) é mais do que necessário, é fundamental para a evolução técnica do programador. Desenvolver essa habilidade, que deve ser constantemente aprimorada, passa pela compreensão dos principais aspectos do pensamento computacional.

    Nós queremos um código legível e que realize o processamento dos dados de maneira correta, mas que também possa ser escalonado e reutilizado, ou ainda, adaptado sem muita dificuldade para solucionar outros problemas.

    Muitos iniciantes em programação ainda não entendem a relevância de compreender os conceitos mais básicos da computação e pensando nisso, decidi fazer um overview que pudesse ressaltar a necessidade de desenvolver o pensamento computacional e ainda, apresentar alguns exemplos práticos.

    Uma visão geral de pensamento computacional engloba alguns pilares: decomposição do problema, reconhecimento de padrões e abstração.

    Não são raros os momentos em que nos deparamos com um problema que não fazemos ideia de como começar a resolver, e aí entra o primeiro pilar do pensamento computacional: a decomposição.

    Se você tem um problema grande que não sabe como resolver, com certeza há um problema menor que você sabe resolver, encontre-o! — Linus Torvalds ( engenheiro de software criador do sistema operacional Linux)

    Não desespere-se (pelo menos não por enquanto) ao ver um problema gigante e não souber como resolver. A lógica de programação dá se por componentização, modularização, interligações entre códigos e uso de dados externos ao programa. Em resumo, tudo está na sua função, no seu escopo, na sua instância. Uma lógica de soma deve estar em uma função “Somar(a,b)” e ser chamada quando necessário em nosso código, uma lógica de subtração, por sua vez, deve estar em uma lógica separada também. Transforme o problema em soluções menores e agrupe as soluções. Vamos ao exemplo?

    Exemplo de Decomposição:

    Suponha que você tem um vetor com n elementos e precisa ordená-lo em ordem crescente. Podemos decompor o problema em etapas menores:

    1. Receber o vetor de entrada com n elementos.
    2. Identificar o menor elemento do vetor.
    3. Colocar o menor elemento na primeira posição do vetor.
    4. Identificar o segundo menor elemento do vetor.
    5. Colocar o segundo menor elemento na segunda posição do vetor.
    6. Repetir os passos 4 e 5 até que todos os elementos estejam em ordem crescente.

    Cada uma dessas etapas pode ser ainda mais decomposta em sub-etapas. Por exemplo, para identificar o menor elemento do vetor, precisamos percorrer todo o vetor e comparar cada elemento com o menor elemento atual. Se um elemento for menor do que o menor elemento atual, ele se torna o novo menor elemento. Esse processo pode ser representado por:

    1. Inicializar o menor elemento como o primeiro elemento do vetor.
    2. Percorrer o vetor a partir do segundo elemento.
    3. Comparar o elemento atual com o menor elemento.
    4. Se o elemento atual for menor do que o menor elemento, atualizar o menor elemento.
    5. Repetir os passos 3 e 4 até percorrer todo o vetor.

    Por mais que você ainda não tenha clareza quanto a lógica pela qual o vetor será ordenada, decompor o problema pode ajudar a entender e determinar a melhor forma de ordenação para o seu caso, como o Bubble Sort ou o Selection Sort, por exemplo.

    Um outro pilar do pensamento computacional é o reconhecimento de padrões.

    E isso tem muito a ver com modularização de código. Seu código não é eficiente se ele realiza a mesma operação em vários lugares. Se em vários lugares do seu código você precisa fazer uma ordenação, crie uma função que faça isso e chame-a sempre que necessário. Isso confere legibilidade (permite que outros programadores entendam o seu código) e eficiência.

    Todavia, reconhecimento de padrões na computação não tem a ver apenas com modularização, digamos que o reconhecimento de padrões tem como objetivo final a modularização do código e organização dos dados do problema, para só então solucioná-lo. A solução do problema passa ainda por outros pilares do pensamento computacional, como a abstração e a generalização.

    Quando pensamos em velocidade, logo pensamos em tempo e distância percorrida, há um padrão aí. Quando pensamos em triângulos, pensamos em formas geométricas com 3 arestas, e quadrados com 2 arestas. Reconhecer padrões é o começo de qualquer solução. Precisamos agora, acomodá-los. Aí vem a abstração.

    O verbo “abstrair” remete a observação dos padrões e acomodação dos mesmos em características e propriedades em separado. Simples? Sim. Observar e organizar os dados nos permite entender como vamos criar uma solução para o nosso problema. Trazemos todas as informações para o campo das ideias. Ora, se a velocidade depende da distância percorrida e do tempo, como a velocidade varia em relação às demais propriedades?

    Se um carro percorre a mesma distância em mais tempo, a sua velocidade foi menor. Se um carro leva o mesmo tempo e percorre uma distância maior, a velocidade é maior quando a distância é maior. O inverso acontece com o tempo : se o tempo é maior e a distância perrcorrida foi a mesma, a velocidade foi menor.

    Velocidade média = distância percorrida / tempo gasto .

    Raciocínio simples, mas poderoso se você compreender que as grandezas diretamente proporcionais (que variam na mesma direção da velocidade) são multiplicadas e as inversamente proporcionais são os divisores. Fórmulas simples precisam ser compreendidas e não decoradas.

    Ao entender as relações entre as variáveis, podemos aplicar a lógica de pensamento computacional para identificar padrões e fazer previsões sobre o desempenho do carro em diferentes situações.

    Encontrada uma solução, a mesma pode ser generalizada, para qualquer caso. Podemos calcular a velocidade de qualquer corpo com as mesmas informações. Outras situações precisam de variáveis adicionais ou adaptações na fórmula, mas podemos partir de um mesmo princípio e poderíamos criar uma função em nosso programa que recebesse esses parâmetros e permitisse ainda parâmetros adicionais como por exemplo a inclinação do terreno, a resistência do ar ou a velocidade do vento. Com essas variáveis adicionais, podemos abstrair ainda mais a solução e torná-la mais precisa e adaptável a diferentes situações. A partir daí, podemos criar algoritmos que permitam a previsão de desempenho de um carro em diferentes cenários, como em uma corrida de fórmula 1, por exemplo, considerando todas as variáveis relevantes. Assim, o pensamento computacional nos permite não apenas resolver problemas específicos, mas também criar soluções mais abrangentes e flexíveis, que possam ser aplicadas a diferentes situações e contextos.

    Pensamento computacional é tão importante que existe uma área da engenharia de software que foca no reconhecimento de padrões para criar objetos e funções de uso geral chamada design patterns. Mas talvez um exemplo mais avançado não seja produtivo para o leitor iniciante. Não se preocupe, teremos um estudo de caso simples logo abaixo onde tudo será esclarecido.

    Estudo de Caso de Pensamento Computacional — Soma de intervalos :

    Problema:

    Calcule a soma de todos os números de 1 à 1000.

    Generalização do Problema :

    Calcule a soma de todos os números de um intervalo fechado crescente.

    Reconhecimento de padrões e dados:

    Como a ordem é crescente, sabemos que :

    O menor número é sempre o primeiro: 1 .

    O maior número é sempre o último: 1 000 .

    Decomposição do problema:

    Vamos somar menos números:

    Soma mais óbvia (do primeiro e do último) : 1 + 1000 = 1001 .

    Agora temos um intervalo menor : [2 , 999]

    Soma mais óbvia : 2 + 999 = 1001.

    OPA! ACHAMOS UM PADRÃO! 1001 (A SOMA DOS PRIMEIROS E OS ÚLTIMOS NÚMEROS DOS SUB-INTERVALOS).

    Poderíamos seguir somando: 3 + 998 , depois 4 + 997 e obteríamos sempre o nosso curinga : 1001.

    E quantas vezes precisaríamos fazer essas somas? Ora, se temos 1000 numeros e estamos somando 2 a 2, faríamos esse mesmo processo em metade das vezes do que somar 1 a 1, ou seja, 500 vezes, ou ainda 1000/2.

    Abstração:

    Vamos sair de uma solução específica, que é contar dois a dois os números do intervalo por quinhentas vezes, e partir para uma solução mais matemática e generalista.

    (1000/2) * 1001 = soma de todos os elementos.

    que de maneira generalizada pode ser escrito na forma:

    (Maior número / 2) * (soma do maior e menor números) = Soma de todos os elementos.

    E finalmente, podemos concluir que a soma desse intervalo é 500500, realizando a conta 500*1001.

    Se você percebeu que somar 2 elementos 500 vezes é mais eficiente na solução do problema do que somar 1000 elementos, você decompôs o problema de uma soma grande em somas menores, encontrou um padrão, abstraiu esses elementos e encontrou uma solução geral para o problema, agora você pode encontrar um algoritmo que possa automatizar esse processo. Logo, escreverei sobre isso por aqui…

    Afinal…

    Falar é fácil , mostre-me o código! — George Polya, engenheiro de software.

    Melhorar a habilidade do pensamento computacional é um caminho para diminuir a frustração e a FALSA sensação de impotência na solução de problemas como programador. Desenvolva essa habilidade desde o início.

    Vítor Alves de Oliveira

    alves.vitor1@aluno.ifsp.edu.br

    Share
    Comments (0)