Estruturas de Dados 2018/2019 (CC1007) - DCC/FCUP

Aula Prática #02 - Input/Output e Mooshak
(semana de 25/02 a 01/03)


Exercício 1) Introdução à classe Scanner

Use a documentação da classe Scanner como referência quando precisar.

// Importa a classe Scanner, que será usada para leitura de dados
import java.util.Scanner;

public class Sum {
   public static void main(String[] args) {

      // Cria um objecto Scanner para ler da entrada padrão ("standard input")
      Scanner stdin = new Scanner(System.in);

      // Chama o método nextInt() para ir buscar o próximo inteiro
      int a = stdin.nextInt();
      int b = stdin.nextInt();

      // Imprime a soma dos dois números
      System.out.println(a+b);
   }

}

  1. O meu primeiro programa com Scanner.
    Copie este pedaço de código para um ficheiro, compile-o e execute o programa. A execução deverá ficar "parada" à espera que introduza dois números. Escreva 2 3, carregue no enter e verifique que o programa escreve 5 como resultado.
  2. Importação de classes.
    Experimente apagar/comentar a segunda linha do programa com a instrução import e compile de novo o código. O que acontece?
    A classe Scanner faz parte do pacote java.util, que contém toda uma série de classes muito úteis que iremos aos poucos começar a usar.
  3. Espaçamento.
    Experimente executar o programa novamente, mas desta vez escrever 2, carregar no enter, depois escrever 3 e carregar novamente no enter, verificando que o programa continue a funcionar. Isto acontece porque o método nexInt() aceita qualquer tipo de espaçamento entre inteiros (sejam apenas um ou vários espaços, tabs e mudanças de linha).
  4. Erro no tipo de dados. (Input Mismatch)
    Execute novamente o programa mas desta vez dê como input 2.5 3.6. O que acontece? Porquê?
  5. Lendo outro tipo de dados.
    Modifique o programa para que passe a conseguir ler número com casas decimais ao invés de inteiros. Queremos usar doubles em vez de ints. Que método deverá ser chamado no lugar do nextInt()? (espreite a documentação da classe Scanner, com todos os métodos disponíveis). Teste o seu programa para garantir que com 2.5 3.6 como entrada o resultado é 6.1.
    (Nota: se usar um computador configurado para português, pode acontecer que ao tentar ler um número decimal com um ponto receba um "Input Mismatch"; nesse caso é esperado que use uma vírgula no lugar do ponto; não há problema pois quando submeter o seu programa ele irá aceitar na mesma pontos - em todo o caso, se desejar usar pontos também no seu computador pode modificar o Locale de todo o programa ou apenas no Scanner)

Exercício 2) Leitura de strings

import java.util.Scanner;

public class Count {
   public static void main(String[] args) {
	
      Scanner stdin = new Scanner(System.in);
		
      // Ciclo para iterar sobre todas as linhas do input
      int counter = 0;
      while (stdin.hasNextLine()) {
         counter++;
         String s = stdin.nextLine();
         System.out.println(counter + ": " + s);
      }
   }
}

  1. Executando o programa.
    Faça download do ficheiro hamlet.txt contendo alguns versos de Hamlet, uma peça de William Shakespeare. Compile o código de cima e execute-o fornecendo como input o ficheiro hamlet.txt usando redirecionamento de input na shell:
     
    $ java Count < hamlet.txt
    1: To be or not to be that is the question
    2: Whether tis nobler in the mind to suffer
    3: The slings and arrows of outrageous fortune
    4: Or to take arms against a sea of troubles

     
    Note que redirecionar o input é "equivalente" a introduzir manualmente o conteúdo do ficheiro via teclado.
  2. Lendo tokens
    Altere o programa, substituindo a chamada a hasNextLine() por hasNext(), e a chamada a nextLine() por next(). Recompile e execute novamente o código com o mesmo input. O que acontece agora? O que está a ser contado?
    Em Java não existe nenhum "nextString" e o método next deve ser usado para ler pedaços de input (tokens) separados por algum tipo de espaçamento (sejam espaços ou mudanças de linha)

Exercício 3) Usando arrays

import java.util.Scanner;

public class ReadNumbers {

   // Escrever os numeros guardados num array no stdout
   static void writeArray(int v[]) {
      for (int i=0; i<v.length; i++)          
         System.out.println("v[" + i + "] = " + v[i]);      
   }
   
   public static void main(String[] args) {

      Scanner stdin = new Scanner(System.in);

      int v[] = new int[10];     // Cria um novo array com espaço para 10 inteiros           
      int n = stdin.nextInt();   // Ler a quantidade de numeros que se seguem
      
      for (int i=0; i<n; i++)    // Ler os numeros a partir do stdin
         v[i] = stdin.nextInt();
      
      writeArray(v);             // Chamar procedimento que escreve
   }
}
  1. Executando o código.
    Compile e execute este código, redirecionando o input a partir do ficheiro numbers.txt
    $ java ReadNumbers < numbers.txt
  2. Posições do array por preencher.
    Substitua o 10 no início do input por um 5 e execute novamente o programa. O que acontece? Porquê? Note como o atributo length de um array indica o tamanho que foi alocado, e não o número de posições "preenchidas". Note também qual o valor por omissão de um inteiro.
  3. Saindo fora dos limites do array. (ArrayIndexOutOfBoundsException)
    Mude o ficheiro de input para passar a conter 11 números (mude a primeira linha para 11 e acrescente um número no final da segunda linha) e execute novamente o programa. O que acontece? Porquê? Note que ao contrário do C, o Java indica explicitamente quando saimos fora dos limites de um array.
    Modifique o programa para passar a aceitar uma qualquer quantidade de números inteiros (a que for indicada na primeira linha de input) e teste a sua alteração.
  4. Calculando a amplitude.
    Acrescente uma função que recebe um array e devolve um inteiro indicando a amplitude (diferença entre máximo e mínimo) dos valores guardados no array. Teste o seu funcionamento e certifique-se funciona independentemente da magnitude dos números (podem ser tão pequenos - incluindo números negativos - ou grandes como quiserem, assumindo que cabem num int).

Exercício 4) O meu primeiro problema no Mooshak


Nesta unidade curricular vamos usar o sistema Mooshak para avaliar automaticamente os programas feitos (ver ajuda do Mooshak).

Para aceder ao sistema use o seu login (sem "up") e password correspondente à conta que tem no DCC.
(se tiver problemas em aceder envie um email ao docente Pedro Ribeiro)

  1. Resolva e submeta com sucesso o problema [ED183] Estatísticas
    Dicas:

Exercício 5) Mais dois problemas no Mooshak

Para consolidar os conhecimentos destas duas primeiras aulas, submeta os seguintes problemas no Mooshak:

  1. Resolva e submeta com sucesso o problema [ED120] Desenhando um Losango
    Algumas dicas:
    • Este problema é essencialmente igual a algo sugerido na aula anterior;
    • Para o resolver apenas necessita de saber como usar ciclos. Quantos pontos existem na primeira linha? E na segunda? E na terceira? E cardinais?
  2. Resolva e submeta com sucesso o problema [ED121] Palíndromos
    • Para a leitura do input pode usar o que aprendeu exercicio 2 desta aula;
    • Se quiser aceder ao i-ésimo caracter de uma String pode usar o método char charAt(int i), como viu na aula anterior.
    • Primeiro converta a a linha lida numa string só com letras minúsculas (sem espaços e pontuações). Faça para isso uma função que receba uma string e devolva a mesma string com os espaços retirados e todas as maiúsculas convertidas em minúsculas. Para isso é útil conhecer a classe Character, que contém várias funções para lidar com caracteres. Em particular:
      • A função boolean Character.isLetter(char c) indica se o caracter c é uma letra;
      • A função char Character.toLowerCase(char c) converte o caracter para a sua versão minúscula
    • Faça agora uma outra função que recebe uma string e devolve um valor booleano indicando se a string é ou não um palíndromo, sendo que pode aplicar esta função à linha já sem espaços e maiúsculas.

Exercícios extra para consolidação de conhecimentos

  1. Faça um programa para simular um autómato celular elementar.
    • Pode ler sobre o que é por exemplo no Wolfram MathWorld ou na Wikipedia.
    • A ideia será algo como receber como input o número da regra (um inteiro de 0 a 255), o tamanho n da "linha" e o número g de gerações, e mostrar no ecrã o estado das g primeiras gerações supondo que começa com uma única célula activa no meio da linha.
    • Pode ver online exemplos do resultado esperado das várias regras.
  2. Faça um programa para traduzir texto para código morse e vice-versa.

Exercício de Desafio

Para esta semana o desafio tem a ver com a eficiência algorítmica. Deve tentar resolver o seguinte problema, que está disponível para submissão no Mooshak:

Devem submeter para verificar se a solução não só está correcta do ponto de vista do que calcula, mas também se está suficientemente eficiente e consegue responder em menos de um segundo seja qual for o input dentro das restrições dadas. Um programa correcto, mas não eficiente, irá obter resolver parcialmente o probema (passa nos testes "pequenos", mas excede o limite de tempo nos testes "grandes").

Para este problema não vou dar nenhuma dica, ficando à espera de ver os vossos programas :)

Nota: é também agora possível submeter o desafio da semana anterior no Mooshak de EDados.