Ver Mensaje Individual
  #6 (permalink)  
Antiguo 29/04/2015, 15:34
eferion
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 7 meses
Puntos: 204
Respuesta: Establecer diferencia en días de 2 fechas distintas [C].

Cita:
Iniciado por rdv316 Ver Mensaje
Me podrías ayudar con esto, no encuentro como reducir el código, para mi todas las líneas son necesarias.
Te intento contestar mañana... que ya es tarde y mañana hay que madrugar :)

-----

Cita:
Iniciado por rdv316 Ver Mensaje
Me podrías ayudar con esto, no encuentro como reducir el código, para mi todas las líneas son necesarias.
Te intento contestar mañana... que ya es tarde y mañana hay que madrugar :)

-----

Variables que se pueden eliminar

Los resultados temporales únicamente interesa almacenarlos en variables si:
  • El resultado se reutiliza varias veces dentro de un bucle (sobretodo si la operación es costosa)
  • Estamos en fase de desarrollo y facilita las tareas de depuración

Fuera de estos contextos es mejor evitar el uso de variables temporales. Tener una función con multitud de variables es como tener el escritorio lleno de cosas, al final cuesta encontrar la información que necesitamos.

En tu caso puedes llegar a eliminar el 30% de las variables del main:
  • regBisAnioF1 y regBisAnioF2 se pueden sustituir directamente por la llamada a la función:

    Antes:
    Código c:
    Ver original
    1. regBisAnioF1 = regularBisiesto(tFecha1);
    2. difDias += regBisAnioF1;

    Despues:
    Código c:
    Ver original
    1. difDias += regularBisiesto(tFecha1);
  • difAnios puede correr la misma suerte con toda tranquilidad:

    Antes:
    Código c:
    Ver original
    1. difAnios = fecha2.anio - fecha1.anio;
    2. if( difAnios == 0){

    Despues:
    Código c:
    Ver original
    1. if( (fecha2.anio - fecha1.anio) == 0 ){

Refactorización del código

1. Cuando realizas una comparación ( >, <, >=, <=, ==, != ) puedes obtener dos resultados posibles: 1 (TRUE) o 0 (FALSE)

Si miras el código de regularBisiesto verás que esta función está devolviendo 1 para TRUE y 0 para FALSE. Si eliminas el if y pasas a retornar el resultado de la comparación conseguirás el mismo resultado y todo en una única línea:

Código c:
Ver original
  1. int regularBisiesto( int anio ){
  2.     return ((anio %4 == 0 && anio%100 != 0) || (anio%400) == 0);
  3. }

Por cierto, llamar tFecha al argumento de regularBisiesto no parece buena idea. Los nombres de las variables tienen que dar una idea acerca de lo que almacenan y de la utilidad que se le pretende dar. tFecha da a entender que estás almacenando una fecha, cuando realmente está almacenando el valor del año.

2. Cuando estás trabajando con fechas de diferentes años haces uso de un bucle que itera sobre los años intermedios para calcular el número de días de este intervalo. En cada iteración sumas 365 días más la corrección por bisiesto cuando procede. Este bucle te lo puedes ahorrar si tienes en cuenta un par de consideraciones:
  • Todos los años tienen, al menos 365 días
  • Puedes saber la cantidad de bisiestos que hay entre dos fechas con una sencilla operación matemática

Dicho con código:

Antes:
Código c:
Ver original
  1. for( i=tFecha1 + 1; i < fecha2.anio ; i++)
  2. {
  3.   tFecha1++;
  4.   difDias += 365;
  5.   difDias += regularBisiesto(tFecha1);
  6. }

Despues:
Código c:
Ver original
  1. difDias += (fecha2.anio - fecha1.anio - 1) * 365;
  2. difDias += (fecha2.anio-1) / 4 - (fecha2.anio-1) / 100 + (fecha2.anio-1) / 400;
  3. difDias -= (fecha1.anio / 4) - (fecha1.anio / 100) + (fecha1.anio / 400);

Explicación del código:
  • La primera línea coge el número de años completos entre las dos fechas y lo multiplica por 365
  • En la segunda se calcula el número de años bisiestos hasta la fecha final (año no incluído).
  • En la tercera se calcula el número de años bisiestos hasta la fecha inicial.
  • Si se restan los dos últimos cálculos se obtiene el número de bisiestos en el rango dado.

Al quitarte un bucle consigues, aunque en este caso no se note, que el tiempo de ejecución sea el mismo, independientemente del número de años que haya entre las dos fechas. El código propuesto es algo más lento que el que tenías si las dos fechas están bastante próximas, pero según se van distanciando las fechas tu código va siendo cada vez más lento, mientras que el mío se mantiene estable.

Por supuesto lo puedes dejar "más bonito" si mueves el cálculo a una función:

Código c:
Ver original
  1. difDias += (fecha2.anio - fecha1.anio - 1) * 365;
  2. difDias += numeroBisiestos(fecha2.anio-1) - numeroBisiestos(fecha1.anio);
  3.  
  4. //...
  5.  
  6. int numeroBisiestos( int anio )
  7. {
  8.   return anio / 4 - anio / 100 + anio / 400;
  9. }

3. ¿Y si en vez de un array con los días de cada mes devolvemos el día del año?

A veces, cambiar la perspectiva nos ayuda a encontrar soluciones más sencillas a nuestros problemas. En tu caso estás haciendo distincción entre dos posibles casos:
  • Las fechas son del mismo año
  • Las fechas son de años diferentes

Conseguir un algoritmo que sea capaz de trabajar indistintamente con ambos casos te evita tener código duplicado y, como has comentado, dificil de refactorizar. Vamos a intentar acabar con la duplicidad.

Lo primero que vamos a cambiar es, como indica el título, calcular el día del año. Esto se puede conseguir reutilizando parte de tu código:

Código c:
Ver original
  1. int diaDelAnio( Fecha fecha )
  2. {
  3.   int tot_dias[] = { 00,31,28,31,30,31,30,31,31,30,31,30,31 };
  4.  
  5.   int dias = fecha.dia;
  6.   int mes;
  7.  
  8.   for( mes = 1; mes < fecha.mes; ++mes )
  9.     dias += tot_dias[mes];
  10.  
  11.   if( fecha.mes > 2 )
  12.     dias += regularBisiesto( fecha.anio );
  13.  
  14.   return dias;
  15. }

Con esta sencilla función podemos obrar milagros dentro de tu código. Como verás, ahora ya ha desaparecido el if y todo el código duplicado:

Código c:
Ver original
  1. #include <stdio.h>
  2.  
  3. typedef struct{
  4.     int dia;
  5.     int mes;
  6.     int anio;
  7. }Fecha;
  8.  
  9. int regularBisiesto( int tFecha );
  10. int numeroBisiestos( int anio );
  11. int diaDelAnio( Fecha fecha );
  12.  
  13. // Algoritmo para calcular el número de días entre dos fechas
  14. // Los cálculos realizados son:
  15. // Si las fechas son del mismo año se calcula el intervalo:
  16. //   - fecha1 -> fecha2
  17. // Si las fechas son de diferentes años se calculan 3 intervalos:
  18. //   - fecha1 -> fin año fecha1
  19. //   - días por los años intermedios
  20. //   - inicio año fecha2 -> fecha2
  21. int main(void){
  22.   Fecha fecha1 = { 1,1, 2050 }, fecha2 = { 12,12, 2050 };
  23.   int difDias = 0;
  24.  
  25.   // Cálculo del intervalo fecha1 -> fecha2
  26.   // no se tienen en cuenta los años en este punto porque la corrección se realiza después
  27.   difDias = diaDelAnio( fecha2 ) - diaDelAnio( fecha1 );
  28.  
  29.   if( fecha1.anio != fecha2.anio )
  30.   {
  31.     // Días por los años completos
  32.     difDias += (fecha2.anio - fecha1.anio - 1) * 365;
  33.     difDias += numeroBisiestos(fecha2.anio - 1) - numeroBisiestos(fecha1.anio);
  34.  
  35.     // Cálculo correspondiente al intervalo fecha1 -> fin año fecha1
  36.     fecha1.dia = 31;
  37.     fecha1.mes = 12;
  38.     difDias += diaDelAnio( fecha1 );
  39.   }
  40.  
  41.   printf("\n Del %d/%d/%d al %d/%d/%d hay %d dias", fecha1.dia, fecha1.mes, fecha1.anio,
  42.             fecha2.dia, fecha2.mes, fecha2.anio, difDias);
  43.  
  44.   getchar();
  45.   return 0;
  46. }
  47.  
  48. int regularBisiesto( int anio)
  49. {
  50.   return ((anio%4 == 0 && anio%100 != 0) || (anio%400) == 0);
  51. }
  52.  
  53. int numeroBisiestos( int anio )
  54. {
  55.   return anio / 4 - anio / 100 + anio / 400;
  56. }
  57.  
  58. int diaDelAnio( Fecha fecha )
  59. {
  60.   int tot_dias[] = { 00,31,28,31,30,31,30,31,31,30,31,30,31 };
  61.  
  62.   int dias = fecha.dia;
  63.   int mes;
  64.  
  65.   for( mes = 1; mes < fecha.mes; ++mes )
  66.     dias += tot_dias[mes];
  67.  
  68.   if( fecha.mes > 2 )
  69.     dias += regularBisiesto( fecha.anio );
  70.  
  71.   return dias;
  72. }

¿Te gusta la nueva versión? Es bastante corta, es legible y utiliza muy poquitas variables :)

Un saludo

Última edición por eferion; 30/04/2015 a las 04:37