Article image
Fernando Araujo
Fernando Araujo08/12/2021 19:43
Compartilhe

Desafios de Código, o Guia Definitivo

  • #Informática Básica
  • #Lógica de Programação

Olá, devs!

Este artigo trata de um mistério profundo da plataforma da DIO para os iniciantes: os desafios de código!

Em um artigo anterior (Desafios de Código: prós e contras), eu tratei dos desafios de código como um todo, dando a minha opinião sobre esta fantástica ferramenta didática oferecida pela plataforma, apontando seus prós e contras. No final, sugeri algumas melhorias para a equipe da DIO implementar nos desafios, como separar postagens de artigos, certificados e dúvidas em 3 links diferentes na página de artigos, ao invés de deixar tudo junto em um balaio só.

Neste artigo, eu vou analisar e detalhar cada etapa da resolução de um único desafio de código, da forma que funciona para mim. Vou mostrar os bastidores da solução de um desafio específico, tratando do enunciado, exemplos dados, algoritmo da solução, testes abertos e testes fechados. Excepcionalmente, neste artigo eu vou dar o código-fonte, pois é preciso mostrar aonde e porque o código dá erro na execução dos testes e como ele pode ser corrigido.

Nos próximos artigos que eu tratar de desafios de código, não vou dar o código-fonte, pois isso é tarefa do aluno, que deve buscar, na sintaxe e estruturas da linguagem pedida para implementação, a sua codificação para resolver o problema. Nos próximos artigos sobre desafios, darei apenas os algoritmos.

Sumário

  1. Introdução
  2. Enunciado
  3. Exemplos
  4. O Algoritmo
  5. Análise da lógica do problema
  6. A codificação
  7. Atenção!! Observação IMPORTANTÍSSIMA sobre as saídas!!!
  8. Executar seu programa para testar o código para os testes abertos
  9. Primeira correção do código
  10. Segunda tentativa de execução
  11. Executar seu programa para testar o código para todos os testes (abertos e fechados)
  12. Segunda correção do código
  13. Terceira tentativa de execução
  14. Observações sobre a entrada de dados
  15. Observações sobre a saída de dados
  16. Observações sobre ajuda
  17. Finalizando...

1 - Introdução

No artigo anterior, mencionado aí em cima, eu disse que tenho visto muita dificuldade dos alunos iniciantes para conseguir resolver os problemas propostos nos desafios de código. E até mesmo de devs experientes.

Algumas vezes, a dificuldade está no entendimento do enunciado e sua passagem para o código, na execução dos testes fechados, ou ainda no entendimento dos comandos de entrada e saída de dados. Mas a principal reclamação é sobre os testes fechados.

Para este artigo, eu escolhi analisar o primeiro desafio de código do curso “Resolvendo Desafios Básicos em JavaScript”, do bootcamp “Eduzz Fullstack developer #2”, no qual estou matriculado.

O desafio que será analisado é: Múltiplos (1/3)

image

2 - Enunciado

Basicamente, o enunciado pede que seja feito um programa que leia dois números inteiros A e B, imprima uma saída de uma das duas mensagens: “Sao multiplos” ou “Nao sao multiplos”. Só isso!

Parece fácil, não?

 

3 - Exemplos

Na parte dos exemplos, são mostrados exemplos de entradas para dois casos e suas respectivas saídas esperadas:

  • Entrada: 6 24, saída: “Sao Multiplos”
  • Entrada: 6 25, saída: “Nao sao Multiplos”

 

4 - O Algoritmo

O algoritmo para resolver este problema lista os passos básicos abaixo e parece ser bem simples e direto:

  1. Leia um número inteiro
  2. Leia outro número inteiro
  3. Teste se eles são múltiplos
  4. Imprima o resultado da escolha acima

 

5 - Análise da lógica do problema

Na matemática, dois números inteiros são múltiplos quando o menor deles cabe dentro do maior.

E como eu posso saber quando isso ocorre?

Dividindo o maior número pelo menor, a divisão deve ser exata, ou seja, com o resto igual a zero!

Então basta que eu codifique um teste para saber se a divisão entre eles é exata, certo? Certo!

No primeiro exemplo, se eu dividir 24 por 6, dá 4. Divisão exata, sem resto, portanto 24 e 6 são múltiplos.

No segundo exemplo, se eu dividir 25 por 6, dá 4,166666... (ou, de outra forma, dá 4, com resto 1). Ou seja, é uma divisão não exata, portanto, 25 e 6 não são múltiplos.

Ficou fácil codificar, não foi?

Basta dividir um pelo outro e testar se a divisão é exata ou não.

 

Aí, surgem algumas dúvidas:

  • E se a entrada fosse 24 e 6, ao invés de 6 e 24?
  • Como eu vou saber qual dos dois números lidos é o maior e qual é o menor, para fazer a divisão do maior pelo menor?
  • Eu preciso encontrar antes o maior e o menor antes de fazer o teste?

 

Bem, no primeiro caso, para o primeiro exemplo, se eu dividir 6 por 24, dá 0,25, divisão não exata. Portanto, você poderia dizer que 6 e 24 não são múltiplos. E estaria errado! Se eu fizer o contrário e dividir 24 por 6 dá 4, exato, sem resto. Então eles seriam múltiplos, sim!

No segundo caso, seria preciso determinar o maior e o menor e dividir sempre o maior pelo menor, senão a divisão do menor pelo maior sempre daria um valor menor que 1 (ou seja, divisão não exata) e você poderia incorrer em erro também!

No último caso, a resposta é não! Existem formas de você fazer o teste sem precisar determinar o maior e o menor.

Note que pode ter mais de uma maneira de codificar essa solução, para o mesmo algoritmo.

Fazendo uma primeira análise mais simplória, notamos que, nos dois exemplos, o primeiro número é sempre menor do que o segundo. Se isso for o padrão de entradas do desafio, bastaria dividir sempre o segundo pelo primeiro e testar se essa divisão é exata, certo?

Então vamos considerar que o primeiro número lido é A e o segundo é B e que o segundo sempre será maior que o segundo.

O algoritmo poderia ser atualizado para:

  1. Leia um número inteiro A
  2. Leia outro número inteiro B
  3. Divida B por A
  4. Teste se a divisão é exata
  5. Se a divisão for exata, eles são múltiplos
  6. Caso contrário, eles não são múltiplos
  7. Imprima o resultado da escolha acima

 

6 - A codificação

Este desafio foi realmente pensado para o dev iniciante, pois boa parte do código já está pronta, com a entrada de dados e os comandos para a saída da resposta, faltando inserir apenas 3 coisas.

Falta apenas codificar o teste do IF e as duas mensagens de saída, uma para cada resultado indicado no enunciado, de acordo com o comando IF.

Com base nas escolhas feitas aí em cima, uma codificação adequada para a parte que falta do algoritmo acima é:

if ( B % A == 0 )

   print("Sao Multiplos");                

else

   print("Nao sao Multiplos");

 

O operador % calcula o resto de da divisão entre valores inteiros.

Ou seja, teste se o resto da divisão de B por A é igual a zero.

Se for, a divisão é exata e A e B são múltiplos, senão, não são!

Simples assim!

 

Não esqueça de salvar seu código com o botão “Salvar”. Se não salvar e passar para o próximo desafio, quando voltar para este, todo o seu código será apagado.

 

7 - Atenção!! Observação IMPORTANTÍSSIMA sobre as saídas!!!

Após a execução do seu programa, o diagnóstico do sucesso ou erro na execução dos testes é feito automaticamente, comparando a sua saída com a saída esperada pela plataforma para cada teste específico. Esta comparação é feita caractere a caractere e basta um único caractere ser diferente para a sua resposta será considerada errada!

A sua resposta precisa ser exatamente igual à resposta esperada para o desafio!

Por exemplo, no caso deste desafio, uma das saídas esperadas é “Sao Multiplos”.

Veja que as palavras estão sem os acentos (til e acento agudo) e são iniciadas por maiúsculas. A sua saída também não pode ter os acentos (mesmo que esteja com a ortografia incorreta) e precisa usar as maiúsculas do mesmo jeito que está no enunciado. Ela tem que bater exatamente com a saída esperada. Se você inserir um espaço adicional no início (ou entre as palavras) da mensagem de saída, também dará erro. Se trocar algum caractere minúsculo por maiúsculo (ou o contrário), também dará erro!

A outra saída esperada é “Nao sao Multiplos”, também sem acentos, com o N maiúsculo e o “s” de “sao” minúsculo, neste caso.

 

8 - Executar seu programa para testar o código para os testes abertos

Para testar seu código para os testes abertos, basta clicar no botão azul “Executar Testes”. Todos os testes abertos serão executados de uma só vez. O resultado da execução pode ser um dos 3 casos possíveis a seguir:

  • Se for uma mensagem em verde informando do sucesso, seu código foi aprovado em todos os testes abertos;

image

  • Se for uma mensagem em vermelho informando que apenas parte dos testes abertos teve sucesso, seu código deu a resposta errada em um ou mais testes abertos;

image

  • Se for uma mensagem em marrom, com fundo creme (salmão), informando que houve um erro ou retorno inválido, seu código não foi nem executado e tem algum erro na sintaxe da linguagem.

image

No primeiro caso, é só clicar no botão “Entregar Desafio” para enviar o código e receber a confirmação de aprovação no desafio.

No segundo caso, é algum erro na lógica do código, mas é um erro de execução. Erro é preciso realizar alguma correção no código e submetê-lo novamente aos testes abertos, com o botão “Executar Testes”.

No terceiro caso, existe algum erro no código, mas não é erro de execução, e sim de compilação. Ou seja, o código está em descordo com as regras de sintaxe da linguagem, impedindo a sua execução. Pode ser apenas um parêntese a mais, a falta de um ponto-e-vírgula, uma variável não declarada, um parâmetro faltando em uma função, um tipo de dado usado erradamente ou algum erro mais grave. Aqui também é preciso realizar alguma correção no código e submetê-lo novamente aos testes abertos, com o botão “Executar Testes”.

Ainda com relação ao segundo caso, os testes abertos permitem que o usuário veja os dados de entrada usados, a saída que é esperada e qual foi a saída do seu programa para aqueles dados após a execução do código. Aí, a análise destes dados pode lhe dar alguma ideia de onde está o erro de lógica no seu código.

Assim, você pode (e deve!) checar os dados dos testes abertos.

 

Neste caso, o teste que eu usei foi if( B % A == 0 ) e o resultado foi a mensagem vermelha informando que:

2/3 testes de abertos tiveram sucesso.

Portanto, o meu código (teste e saídas) está correto para 2 testes abertos, mas deu erro no teste aberto restante.

Logo, alguma coisa no meu código precisa ser corrigida. Vamos checar os testes abertos, analisá-los para ver como corrigir (depurar) o código.

 

9 – Primeira correção do código

Clicando no teste #1, são mostrados os dados de entrada, a saída esperada e a minha saída, bastando rolar a tela para cima para vê-los.

Vamos organizar os dados dos testes abertos para uma tabela:

image

Nos testes #1 e #2, a resposta do meu código está correta, batendo com a resposta esperada, pois os números não são múltiplos.

No teste #1, no meu código, a divisão de B por A (11 por 7) dá 1,57 (ou 1 com resto 4), não exata, indicando que eles não são múltiplos. O meu código deu a resposta esperada, portanto, foi aprovado neste teste.

No teste aberto #2, a divisão de B por A (-2 por 5) dá -0,4, não exata, indicando que eles não são múltiplos. O meu código deu a resposta esperada e foi aprovado.

Já para o teste #3, a minha resposta está errada porque indicou que os números -1 e -6 não são múltiplos e a solução esperada era que eles são múltiplos.

O resultado da divisão de B por A, (-1 por -6), dá 0,1666..., não exata, por isso a minha resposta foi que -1 e -6 não são múltiplos.

Mas neste caso, o segundo número não é maior do que o primeiro, conforme nossa suposição inicial, lembra?

Quer dizer que os testes desse desafio nem sempre vão ter a segunda entrada maior do que a primeira, certo?

Se for assim, então -1 e -6 são múltiplos, pois dividindo A por B, (-6 por -1), dá 6, com divisão exata, por isso a resposta esperada é que eles são múltiplos.

Vamos alterar a condição original do if invertendo o que foi codificado antes, ou seja, if( A % B == 0 ), para ver o que vai dar.

Fazendo apenas essa mudança no código, vamos salvá-lo e executar novamente os testes abertos.

 

10 – Segunda tentativa de execução

Então, vamos partir para uma segunda tentativa de execução, após essa modificação do código.

O resultado foi a mensagem verde abaixo:

3/3 testes de abertos tiveram sucesso. Clique em “ENTREGAR DESAFIO” para executar todos os testes e finalizar esse desafio.

Agora parece que deu certo! Ó código está correto (teste e saídas) para todos os testes abertos.

Agora só falta tentar finalizar o desafio, submetendo-o aos testes fechados.

 

11 - Executar seu programa para testar o código para todos os testes (abertos e fechados)

Para testar seu código para todos os testes, basta clicar no botão verde “Entregar Desafio”. Todos os testes (abertos e fechados) serão executados de uma só vez. Se o código já foi aprovado nos testes abertos, apenas os testes fechados oferecem risco de erro.

Pode ocorrer um dos 2 resultados possíveis para o resultado da execução do seu código:

  • Ele pode estar correto e valer para as entradas de todos os testes fechados;
  • Ele pode estar incorreto e não valer para as entradas de um ou mais testes fechados;

No primeiro caso, seu desafio está concluído e você pode passar para o próximo (uhuuuuuuuuuuuuuuu!!!!!!!!!!!!). Uma janela é mostrada coma mensagem de sucesso.

No segundo caso, você perde um heart (coração) e precisa corrigir algum erro na lógica do seu código para depois testá-lo novamente (botão azul “Executar Testes”) para os testes abertos. Quando passar em todos, submeter novamente aos testes fechados (botão azul “Entregar Desafio”). Uma janela é mostrada com a mensagem de erro.

 

Vamos testar nosso código agora. Clicando no botão “ENTREGAR DESAFIO”, recebemos o seguinte resultado:

image

Os testes fechados não permitem que você os abra para ver quais são os dados de entrada, a saída esperada, nem a sua saída.

É preciso inferir quais dados poderiam causar o erro na sua resposta, já que seu código passou por todos os testes abertos.

 

12 – Segunda correção do código

Retomando a tabela com os dados dos testes abertos, note que no teste #3, a nossa resposta agora estava correta porque ela indicou que o resultado da divisão de A por B (-6)/(-1) dá exata e ela realmente dá (6).

Então o que pode estar errado?

Bem, neste mesmo teste para a primeira versão do if o código foi reprovado porque a divisão feita foi o menor número pelo maior. Já a versão atual do código, o código não deu erro para a divisão do maior pelo menor.

Ou seja, o erro do nosso código acontece quando a gente divide o número menor pelo maior, certo?

Então, basta garantir que o comando if teste as duas possibilidades de divisão de uma vez só. Se uma delas der divisão exata, os números são múltiplos.

 

Nesse caso, o código ficaria assim

if( A % B == 0 || B % A == 0)

 

Explicando, o comando testa se a divisão do primeiro número pelo segundo é exata OU se a divisão do segundo número pelo primeiro é exata.

O operador || significa OU e associa duas condições, de modo que a condição só será verdadeira se as duas condições individuais o forem, caso contrário, ela será falsa.

Se uma das duas for exata, o teste dá verdadeiro e a resposta de que são múltiplos será impressa corretamente.

Caso as duas divisões não sejam exatas (caso de números não múltiplos), o teste dará falso e a resposta impressa também será impressa corretamente.

 

13 – Terceira tentativa de execução

Vamos partir para a terceira tentativa de execução de código.

Vamos testar a condição do if com os dois testes juntos, como mostrado acima contrário, ou seja, if( A % B == 0 || B % A == 0) para ver o que vai dar.

Agora, vamos salvá-lo e executar mais uma vez os testes abertos.

Opa!! O resultado foi a mensagem verde informando que:

3/3 testes abertos tiveram sucesso. Clique em “ENTREGAR DESAFIO” para executar todos os testes e finalizar esse desafio.

Vamos testar nosso código agora nos testes fechados, clicando no botão “ENTREGAR DESAFIO”:

E o resultado foi esse aqui:

image

Agora clique no botão e tente resolver os dois próximos desafios deste curso.

É o seu dever de casa!!!

 

Mas, antes de terminar, seguem algumas explicações sobre os desafios que considero importantes para os devs iniciantes.

 

14 - Observações sobre a entrada de dados

No caso deste desafio, a linguagem utilizada para a codificação é Javacript, mas em outros bootcamps pode ser exigida outra linguagem, como java, C# ou Python.

Em Javascript, a função padrão de entrada de dados é prompt(), caso você esteja executando seu programa no navegador para inserir um dado de entrada. Ela abre uma janela no navegador, escreve uma mensagem para o usuário e fica esperando que ele digite uma entrada e confirme com o botão “Ok”.

Já aqui na plataforma da DIO, o comando prompt() dará um erro, pois não tem como o usuário informar dados de entrada pelo teclado.

Os dados de entrada para cada teste são fornecidos pelo próprio teste e serão lidos para dentro do programa pelo comando gets(). Esta é a forma de entrar com os dados que alimentarão seu programa.

Portanto, se você testar seu programa do desafio diretamente no navegador e funcionar lá, ao copiar sua solução para a plataforma da DIO, terá que trocar todos os comandos prompt() por gets() para testar na plataforma.

 

As funções prompt() e gets() leêm uma cadeia de caracteres (string) da entrada padrão e a retornam, a qual pode ser atribuída a uma variável.

Se seu programa vai usar esta entrada como string, pode fazê-lo diretamente, mas se ela será usada como número, devem ser usadas funções para convertê-la para inteiro, com parseInt(), ou decimal, usando parseFloat().

 

Para concluir, acho que vale a pena explicar o que faz cada instrução de entrada de dados já escrita antecipadamente para este desafio:

let lines = gets().split("\n");

Aqui, a função gets() lê os dados de entrada, com múltiplas linhas de uma vez só, a função split(“\n”) separa todos os caracteres lidos em linhas diferentes, com base no caractere de final de linha (\n). Os caracteres lidos são atribuídos à variável lines (do tipo array, ou vetor), onde cada elemento é uma linha completa de entrada. No nosso desafio, a entrada só vai ter uma única linha, então não seria necessário usar a função split(“\n”).

Por exemplo, se a entrada fosse:

“7 11”

“5 -2”

“-6 -1”

Então, cada linha seria um dos 3 elementos do array lines[ ]:

 lines = [“7 11”, “5 -2”, “-6 -1”]

 

let line = lines.shift().split(" ");

Agora, a função split(“ ”) separa todos os caracteres lidos em uma linha da variável lines, com base no caractere de espaço em branco ( ), a função shift() exclui da variável lines os caracteres lidos nessa linha e os atribui à variável line (do tipo array, ou vetor), onde cada elemento é um dos dados de entrada do problema. Para a primeira linha da variável lines, o array line[ ] fica:

line = [“7”, “11”]

 

let A = parseInt(line[0]);

let B = parseInt(line[1]);

Finalmente, a função parseInt() converte o primeiro elemento (line[0]) do array line, o string “7”, para o número 7, e atribui este valor à variável A, na primeira linha acima.

Da mesma forma, a mesma função converte o segundo elemento (line[1]) do array line, o string “11”, para o número 11, e atribui este valor à variável B, na segunda linha acima.

 

15 - Observações sobre a saída de dados

As linguagens de programação oferecem comandos para escrever as saídas na tela ou no papel, em geral, chamadas de print, printf, println, etc. Em Javascript, até existe o método print(), mas ele não serve para escrever mensagens na saída. É usado para abrir a caixa de diálogo (como <CTRL> + P) para imprimir o conteúdo da janela do navegador, quando chamado por um objeto window, como em window.print(), por exemplo. E não é o que queremos aqui.

Em Javascript, a função padrão de saída de dados é console.log(), que tem o mesmo efeito que a função print() de outras linguagens, ou seja, ela imprime uma sequência de caracteres na tela (seja do navegador ou da plataforma da DIO), que é o que queremos aqui. É essa função que costumamos usar na plataforma da DIO para escrever os resultados com as mensagens de saída pedidas nos enunciados dos problemas.

Importante! Nos desafios anteriores que já codifiquei na plataforma da DIO, o comando print() sempre dava erro de execução e era necessário substituí-lo por console.log(), quando a execução de uma solução codificada em Javascript funcionava no navegador e era copiada para cá.

Estranhamente, neste desafio, a execução com o comando print() não está dando erro, portanto, não é preciso substituí-lo por console.log(). Talvez tenham mudado isso na plataforma, não sei.

Em geral, para qualquer linguagem de programação, a saída de mensagens com os resultados pedidos pelos desafios precisa de formatação para juntar os textos das mensagens com os valores das variáveis do resultado, por meio de comandos de formatação e concatenação de strings e números. Neste caso, não será preciso fazer isso.

 

16 - Observações sobre ajuda

A plataforma da DIO para resolução dos desafios oferece algumas funcionalidades de ajuda aos devs, listadas abaixo (ver a figura, que foi recortada e reduzida):

image

Na lateral esquerda, de cima para baixo, há 3 ícones, para:

  • Descrição: enunciado do problema e exemplos de entrada e saída esperada;
  • Instruções: instruções dos comandos aceitos para entrada e saída de dados, para várias linguagens de programação;
  • Dúvidas: respostas às principais dúvidas (FAQ) sobre os desafios de código;

 

Na barra inferior, tem um botão para acessar o Discord, fórum de discussões onde os devs podem interagir, fazer perguntas e tirar dúvidas sobre bootcamps, desafios, acelerações, etc. com outros devs da plataforma.

 

E na barra superior, o ícone da engrenagem permite a configuração do ambiente de programação dos desafios, como tipo de fonte (tema), tamanho, tabulação, autocompletar, etc.

 

 

17 - Finalizando...

Publiquei um novo artigo listando todos os artigos que já escrevi para a DIO, uma espécie de índice (ou sumário), pois vejo que os artigos antigos acabam ficando inacessíveis após algum tempo. Este novo artigo será referenciado em todos os artigos que eu escrever daqui para a frente e será atualizado a cada artigo novo que eu escrever.

Segue o link abaixo:

Iniciantes 5 – Meus artigos na DIO

 

Bem, por hoje é só! Obrigado por ter lido até aqui.

O próximo artigo tratará dos outros 2 desafios desse mesmo curso, mas só irei até a definição do algoritmo, pois não acho legal dar o código da solução doe desafios, isso é tarefa dos alunos.

Neste artigo atual, eu dei o código todo porque queria dar uma aula bem didática e detalhada para os iniciantes sobre como resolver um desafio de código, principalmente para aqueles que nunca viram programação ou estão usando a plataforma da DIO para resolver desafios de código pela primeira vez!!

Acho que o novo artigo será publicado ainda hoje!

OBS: Se você gostou deste artigo, veja o artigo anterior que eu escrevi na DIO:

Meus artigos na DIO

 

E o meu artigo seguinte a este:

Eduzz, resolvendo desafios básicos em Javascript

Compartilhe
Comentários (4)
Rafael Silva
Rafael Silva - 02/07/2023 16:57

Esse artigo me ajudou muito, o meu problema eram as mensagens de saida

Fernando Araujo
Fernando Araujo - 31/07/2022 12:23

Obrigado, pessoal,

Comentários assim me incentivam a continuar escrevevendo artigos!

Marcone Araújo
Marcone Araújo - 04/07/2022 14:27

Grande artigo.

Parabéns pelo trabalho.

Alexandre Filho
Alexandre Filho - 08/12/2021 21:33

Excelente artigo!! Vai ajudar bastante o pessoal que está começando agora!!