MAC0110 - Dicas 3

Nessa página vamos descrever algumas dicas e informações que poderão ajudar na matéria e na vida de programador (isso ficou igual aos dicas 2, mas também vale aqui).

Muitas vezes fazemos eps que funcionam e estamos pensando que estamos fazendo na melhor maneira possível, no entanto quando relemos nossos eps de semestres passados vimos que tomamos péssimas escolhas. Em MAC0110, não espero de vocês EPs super eficientes e com truques mirabolosos, mas gostaria que antes de entregar o código relessem o que foi escrito e tentasse dar uma melhorada. Nessa página vou expor alguns exemplos que serão retirados de EPs que vocês já fizeram, não vou colocar nomes. Se seu EP estiver nessa página, não se sinta mal e faça melhor da próxima vez. Todos os exemplos aqui presentes foram retirados do January 1 (se seu código está aqui ele ainda pode ficar com 10, não se preocupe).

Repetição, é necessário?

Veja o código abaixo.

for (int j=1; j<=12; j++){
  if (leapYear == true && j == 2){
    for (int k=1; k<=days[j]+1; k++){
      if (k == d && j == m){
        if (dia%7 == 6)
          sat++;
        else if (dia%7 == 0)
          sun++;
      }
      dia++;
    }
  }

  else{
    for (int k=1; k<=days[j]; k++){
      if (k == d && j == m){
        if (dia%7 == 6)
          sat++;
        else if (dia%7 == 0)
          sun++;
      }
      dia++;
    }
  }
}

Nesse código a pessoa pode ter feito tudo certo, no entanto há uma repetição dentro de cada condição. A única diferença é a soma do +1 dentro da condição do for. Como você melhoraria isso? Eu, colocaria uma variável para representar esse 1, fazendo assim:

for (int j=1; j<=12; j++){
  int soma = 0;
  if (leapYear == true && j == 2)
    soma = 1;
  for (int k=1; k<=days[j]+soma; k++){
    if (k == d && j == m){
      if (dia%7 == 6) sat++;
      else if (dia%7 == 0) sun++;
    }
    dia++;
  }
}

Veja que ambos os códigos fazem exatamente a mesma coisa, no entanto o segundo está muito mais sucinto e sem repetição.

Muitas vezes queremos que nosso EP tenha um comportamento diferente para cada situação e começamos a escrever vários ifs (lembro que no meu primeiro ep de 110 tomei uma bela chamada de atenção nisso). Quando você percebe que há varios ifs e que dentro deles há comportamentos semelhantes, tente melhorar. Veja abaixo uma implementação com muitos ifs e ao lado o seu equivalente:

if (E == 0) s = 1;
if (E == 1) s = 2;
if (E == 2) s = 3;
if (E == 3) s = 4;
if (E == 4) s = 5;
if (E == 5) s = 6;
if (E == 6) s = 7;

// fevereiro
if (m == 2 && d == 29 && leapYear && s == 6) sat ++;        
if (m == 2 && d == 29 && leapYear && s == 7) sun ++;  

if (m == 2 && d != 29 && s == 6) sat ++;
if (m == 2 && d != 29 && s == 7) sun ++;
          
// resto do ano
if (m > 2 && s == 6) sat ++;        
if (m > 2 && s == 7) sun ++;

if (m == 1 && s == 6) sat ++;        
if (m == 1 && s == 7) sun ++;
if (E >= 0 && E <= 6) s = E + 1;

if (m >= 1 && (leapYear || d != 29)) {
  if (m > 2 && s == 6) sat ++;        
  if (m > 2 && s == 7) sun ++;
}

⇧ Se você que que algo seja feito para várias condições pense no caso em que você não quer que seja feito. Lembre que para ajudar temos a seguinte propriedade !(a && b) = !a || !b e !(a || b) = !a && !b. Isso ajuda bastante na hora de fazer decisões.

Vetores não mordem.

Esse título me fez pensar em uma piada muito ruim, se quiser veja aqui.

Muitas vezes temos um código que tem o mesmo comportamento para diversos elementos, mas há alguns elementos que apresentam uma excessão. Uma ideia, ainda mais se o número de elementos é pequeno, como 12, é repetir o código para cada um deles. Veja abaixo:

...
DiaMes=1;
for (i=0;i<31;i++) { //maio
  if (M==5 && DiaMes==D && DiaSemana==6){
    saturday++;
  }
  if (M==5 && DiaMes==D && DiaSemana==7){
    sunday++;
  }
  DiaSemana++;
  if (DiaSemana > 7) {
    DiaSemana=1;
  }
  DiaMes++;
  if (DiaMes > 31) {
    DiaMes=1;
  }
}
DiaMes=1;
for (i=0;i<30;i++) { //junho
  if (M==6 && DiaMes==D && DiaSemana==6){
    saturday++;
  }
  if (M==6 && DiaMes==D && DiaSemana==7){
    sunday++;
  }
  DiaSemana++;
  if (DiaSemana > 7) {
    DiaSemana=1;
  }
  DiaMes++;
  if (DiaMes > 30) {
    DiaMes=1;
  }
}
...

⇧ Vê como fica repetido? O modo mais fácil de resolver isso é utilizando um vetor e uma condição para o elemento diferente. Portanto sempre fique atentos quando podemos usar vetores para melhorar o código e evitar repetições. Lembre, programadores são em sua maioria preguiçosos e programam para não fazerem o trabalho na mão.

Se não é bonito pode ser bom?

A resposta é sim. Veja o código abaixo:

public static void main(String[] args){
  int D=Integer.parseInt(args[0]);
  int M=Integer.parseInt(args[1]);
  int dw=7;
  int sat=0;
  int sun=0;
  for (int y=2017;y<2417;y++){
    for(int my=1;my<=12;my++){
        if((my==1)||(my==3)||(my==5)||(my==7)||(my==8)||(my==10)||(my==12)){
          for(int dm31=1;dm31<=31;dm31++){
            if((dm31==D)&&(my==M)&&(dw==6)){
              sat++;
            }else{
              if((dm31==D)&&(my==M)&&(dw==7)){
                sun++;
              }
            }
            dw++;
            if(dw==8){
              dw=1;
            }
          }
        }else{
          if(my==2){
            for(int dmfeb=1;dmfeb<=29;dmfeb++){
              if((y%400==0)||((y%100!=0)&&(y%4==0))){
                if((dmfeb==D)&&(my==M)&&(dw==6)){
                  sat++;
                }else{
                  if((dmfeb==D)&&(my==M)&&(dw==7)){
                    sun++;
                  }
                }
              }else{
                if(dmfeb!=29){
                  if((dmfeb==D)&&(my==M)&&(dw==6)){
                    sat++;
                  }else{
                    if((dmfeb==D)&&(my==M)&&(dw==7)){
                      sun++;
                    }
                  }
                }else{
                  dw=dw-1;
                  if(dw==0){ 
                    dw=7; //************** o que essa linha faz? **************
                  }
                }
              }
              dw++;
              if(dw==8){
                dw=1;
              }
            }
          }else{
            for(int dm30=1;dm30<=30;dm30++){
              if((dm30==D)&&(my==M)&&(dw==6)){
                sat++;
              }else{
                if((dm30==D)&&(my==M)&&(dw==7)){
                  sun++;
                }
              }
              dw++;
              if(dw==8){
                dw=1;
              }
            }
          }
        }
    }
  }
  System.out.println("Saturday: "+sat+"; Sunday: "+sun);
}

⇧ veja que existe uma pergunta no código, qual a resposta? Para responder a essa pergunta você deve ter a capacidade de responder entrar em aproximadamente 8 laços/condições. Sempre quando enviar um EP pense no seguinte: você está trabalhando e será promovido, mas só depois de você explicar o seu código para seu substituto. Vocês vão ver que isso pode causar muitos problemas em trabalhos em grupo.

Apesar desse código estar bem confuso ele incrivelmente funciona.

Se é inútil, não mande.

Quando vocês estiverem programando em C, vão ver que variáveis não usadas vão causar avisou e esses avisos vão ser descontados (independente do monitor). Portanto variáveis que nunca são usada não precisam ser declaradas. Quando eu escrevo um código, sempre crio um monte de lixo, mas no hora de enviar tento tirar essas coisas.

if(m <= 0 || m > 12) {
    
  } else if (d > DAYS[m] || d <= 0) {
    
  } else {
  }
  ...
}

⇧ para quê mandar uma condição que não tem nada no corpo?

Reinventar a roda

Vocês vão ver que ao longo da matéria vão ter vários EPs. A primeira coisa a fazer e se perguntar se há alguma coisa que o professor passou relacionada ao EP. Se essa coisa existe, provavelmente já há um código base no material do livro ou no próprio enunciado. Utilize isso, vocês vão ver que vai facilitar muito. Nesse ep que vocês mandaram houve tentativa (várias pessoas) de reinventar o método de cálculo do próximo dia. A reinvenção mais interessante foi a seguinte:

double[] january = {1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.21, 1.22, 1.23, 1.24, 1.25, 1.26, 1.27, 1.28, 1.29, 1.30, 1.31}; 
double[] february = {2.01, 2.02, 2.03, 2.04, 2.05, 2.06, 2.07, 2.08, 2.09, 2.10, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18, 2.19, 2.20, 2.21, 2.22, 2.23, 2.24, 2.25, 2.26, 2.27, 2.28};
double[] februaryleap = {2.01, 2.02, 2.03, 2.04, 2.05, 2.06, 2.07, 2.08, 2.09, 2.10, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18, 2.19, 2.20, 2.21, 2.22, 2.23, 2.24, 2.25, 2.26, 2.27, 2.28, 2.29};
double[] march = {3.01, 3.02, 3.03, 3.04, 3.05, 3.06, 3.07, 3.08, 3.09, 3.10, 3.11, 3.12, 3.13, 3.14, 3.15, 3.16, 3.17, 3.18, 3.19, 3.20, 3.21, 3.22, 3.23, 3.24, 3.25, 3.26, 3.27, 3.28, 3.29, 3.30, 3.31};
double[] april = {4.01, 4.02, 4.03, 4.04, 4.05, 4.06, 4.07, 4.08, 4.09, 4.10, 4.11, 4.12, 4.13, 4.14, 4.15, 4.16, 4.17, 4.18, 4.19, 4.20, 4.21, 4.22, 4.23, 4.24, 4.25, 4.26, 4.27, 4.28, 4.29, 4.30}; 
double[] may = {5.01, 5.02, 5.03, 5.04, 5.05, 5.06, 5.07, 5.08, 5.09, 5.10, 5.11, 5.12, 5.13, 5.14, 5.15, 5.16, 5.17, 5.18, 5.19, 5.20, 5.21, 5.22, 5.23, 5.24, 5.25, 5.26, 5.27, 5.28, 5.29, 5.30, 5.31}; 
double[] june = {6.01, 6.02, 6.03, 6.04, 6.05, 6.06, 6.07, 6.08, 6.09, 6.10, 6.11, 6.12, 6.13, 6.14, 6.15, 6.16, 6.17, 6.18, 6.19, 6.20, 6.21, 6.22, 6.23, 6.24, 6.25, 6.26, 6.27, 6.28, 6.29, 6.30}; 
double[] july = {7.01, 7.02, 7.03, 7.04, 7.05, 7.06, 7.07, 7.08, 7.09, 7.10, 7.11, 7.12, 7.13, 7.14, 7.15, 7.16, 7.17, 7.18, 7.19, 7.20, 7.21, 7.22, 7.23, 7.24, 7.25, 7.26, 7.27, 7.28, 7.29, 7.30, 7.31}; 
double[] august = {8.01, 8.02, 8.03, 8.04, 8.05, 8.06, 8.07, 8.08, 8.09, 8.10, 8.11, 8.12, 8.13, 8.14, 8.15, 8.16, 8.17, 8.18, 8.19, 8.20, 8.21, 8.22, 8.23, 8.24, 8.25, 8.26, 8.27, 8.28, 8.29, 8.30, 8.31};
double[] september = {9.01, 9.02, 9.03, 9.04, 9.05, 9.06, 9.07, 9.08, 9.09, 9.10, 9.11, 9.12, 9.13, 9.14, 9.15, 9.16, 9.17, 9.18, 9.19, 9.20, 9.21, 9.22, 9.23, 9.24, 9.25, 9.26, 9.27, 9.28, 9.29, 9.30}; 
double[] october = {10.01, 10.02, 10.03, 10.04, 10.05, 10.06, 10.07, 10.08, 10.09, 10.10, 10.11, 10.12, 10.13, 10.14, 10.15, 10.16, 10.17, 10.18, 10.19, 10.20, 10.21, 10.22, 10.23, 10.24, 10.25, 10.26, 10.27, 10.28, 10.29, 10.30, 10.31}; 
double[] november = {11.01, 11.02, 11.03, 11.04, 11.05, 11.06, 11.07, 11.08, 11.09, 11.10, 11.11, 11.12, 11.13, 11.14, 11.15, 11.16, 11.17, 11.18, 11.19, 11.20, 11.21, 11.22, 11.23, 11.24, 11.25, 11.26, 11.27, 11.28, 11.29, 11.30};
double[] december = {12.01, 12.02, 12.03, 12.04, 12.05, 12.06, 12.07, 12.08, 12.09, 12.10, 12.11, 12.12, 12.13, 12.14, 12.15, 12.16, 12.17, 12.18, 12.19, 12.20, 12.21, 12.22, 12.23, 12.24, 12.25, 12.26, 12.27, 12.28, 12.29, 12.30, 12.31}; 

⇧ não sei se funciona ou não.

Por último vou dar um bom exemplo:

public class SSCounter{
  public static void main(String[] args) {
    int day = Integer.parseInt(args[0]);
    int month = Integer.parseInt(args[1]);
    int d = 1;                                             // igual do Tomorrow
    int m = 1;                                             // igual do Tomorrow
    int w = 1;                                             // igual do Tomorrow
    int sundays = 0;
    int saturdays = 0;
    boolean leapYear;                                      // igual do Tomorrow
    int y = 2017;
    // igual do Tomorrow
    int[] DAYS = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

    while(y <= 2416){
      if((d == day) && (m == month)){
        if(w == 7) saturdays++;
        else if(w == 1) sundays++;
      }
      if (y % 400 == 0) leapYear = true;                   // igual do Tomorrow
      else if (y % 100 == 0) leapYear = false;             // igual do Tomorrow
      else leapYear = y % 4 == 0;                          // igual do Tomorrow
      if (d + 1 <= DAYS[m] || d == 28 && leapYear) d++;    // igual do Tomorrow
      else if (m < 12) { d = 1; m++; }                     // igual do Tomorrow
      else { d = 1; m = 1; y++; }                          // igual do Tomorrow

      if(w == 7) w = 1;
      else w++;
    }
    
    System.out.println("Saturday: " + saturdays + "; Sunday: " + sundays);
  }
}

⇧ Os comentários foram adicionados por mim e visam mostrar como reaproveitar o código do Tomorrow.java.


Dicas 2

Escrito por Gabriel Capella.