búsqueda

Búsqueda personalizada

resultados

miércoles, 7 de septiembre de 2011

Corte de control

Corte de control




1. Objetivo:

El objetivo de este artículo es describir una forma simple y prolija de implementar un algoritmo de corte de control.

2. Introducción:

2.1. ¿Qué es el corte de control?



El corte de control es un proceso en el cual partiendo de registros ordenados por el valor de uno o más campos, (denominados campos clave o llave o criterio de ordenamiento) se los procesa en categorías determinadas por los criterios de ordenamiento.
En otras palabras, se procesa un conjunto ordenado de registros en subconjuntos determinados por los criterios de orden.

2.2. ¿Para qué se podría aplicar un corte de control?

La aplicación más común para la cuál se realiza el corte de control es para generar reportes que acumulen cantidades y/o importes.

Supongamos que somos un ente regulador de supermercados y aplicamos varios impuestos por provincia, por cada cadena.
Deseamos saber cuánto se recauda por provincia, por impuestos para cada cadena de supermecado ubicadas en todo el país.
A cada cadena se le pueden aplicar uno o varios impuestos diferentes, se desea contabilizar la cantidad de impuestos y el importe total que pagará cada cadena en cada provincia.
Para esto queremos generar un informe a partir del archivo que recibimos una vez por mes con la recaudación.

3. Estructura del algoritmo:
Esta sería la estructura propuesta:

while(condicion_fin_recorrido)
{
   clave1_anterior = clave1Actual
   
   [inicializar contadores para el primer corte]
   
   while(condicion_fin_recorrido AND clave1_anterior == clave1Actual)
   {
       clave2_anterior = clave2Actual
       
       [inicializar contadores para el segundo corte]

       while(condicion_fin_recorrido AND clave1_anterior == clave1Actual
AND clave2_anterior == clave2Actual)
       {
           [incrementar contadores del segundo corte]
       
           [escribir detalle del segundo corte]    
           
           [avanzar_lectura]
       }
       
       [escribir totales acumulados del segundo corte]
               
       [incrementar contadores del primer corte]
   }
   
   [escribir totales acumulados del primer corte]
               
   [incrementar contadores generales]
}
[escribir totales generales]
Vamos a analizarla:

  1. Recordemos la precondición. La lista o el archivo debe venir ordenado/a por los criterios que usaremos para realizar el corte de control.
  2. Realizar un corte de control significa que vamos a agrupar y acumular por un criterio determinado, esto significa que mientras la clave sea la misma, estaremos en ese corte. Para esto necesitamos recordar la clave del registro anterior. Esto lo realizamos mediante variables que podemos llamar “claveNAnterior”, donde N un número que significa que clave es, la primera “1”, la segunda “2”, la enésima “N”; otra nomeclatura más clara es el nombre de esa clave, por ejemplo si la clave es “provincia”, la variable se llamará “provinciaAnterior”.
  3. Un while para la condición de fin de recorrido (preguntaremos por not EOF si es un archivo O si hay más elementos si es una lista), luego tenemos un while por cada corte de control. Notemos que en cada while se arrastra la condición del while anterior y se le agrega con un operador lógico AND la condición del nuevo corte de control.
  4. El incremento de lectura se realiza dentro del while más interno. Ahí es donde debemos avanzar la lectura del archivo o la lista.
  5. Dentro de cada corte tendremos acumuladores y contadores que incrementaremos.
  6. Estos contadores y acumuladores lo debemos inicializar arriba del  while correspondiente al corte de control donde serán incrementados.
  7. De igual manera, las claves anteriores se deben asignar en el mismo lugar.
  8. Cuando se sale de un while es porque terminamos el corte determinado, es ahí donde se pueden imprimir los totales acumulados.
  9. La condición de fin de recorrido se da después del avance de lectura que se hace en un sólo lugar, dentro del while más interno. Cuando está condición se da se sale de todos los whiles porque todos arrastran esta condición.
  10. Finalmente cuando se sale del último while se tienen los totales generales acumulados.



4. Ejemplo de implementación:

Veamos como implementamos lo que acabamos de detallar.

Estos son los datos que tenemos:

Lista ordenada por Provincia - Impuesto
ProvinciaCadenaImpuestoimporte
Buenos AiresCadena1i110
Buenos AiresCadena1i220
Buenos AiresCadena2i130
CABACadena1i120
CABACadena1i230
CABACadena2i160
CABACadena2i250
MendozaCadena1i120
MendozaCadena2i230



Este es el reporte solicitado:


 

Prov. :Buenos Aires
Cadena:Cadena1
Cantidad Impuestos2importe30
Cadena:Cadena2
Cantidad Impuestos1importe30
Cant. Cadenas:2importe: 60
Prov. :CABA
Cadena:Cadena1
Cantidad Impuestos2importe50
Cadena:Cadena2
Cantidad Impuestos2importe110
Cant. Cadenas:2importe: 160
Prov. :Mendoza
Cadena:Cadena1
Cantidad Impuestos1importe20
Cadena:Cadena2
Cantidad Impuestos1importe30
Cant. Cadenas:2importe: 50
Cant. Prov.:3importe270


Veámoslo en código C++:

int main()
{
    list lista;
    cargarLista(lista);

    cout << "--- Realizando corte de control ---" << endl;
    string provAnt;
    string cadenaAnt;

    int contCadena;
    int contProv=0;

    int total=0;
    int totProv;
    int impuestos;

    list::iterator it = lista.begin();


    while( it != lista.end() ) //Condición general. Fin de lista o EOF
    {

        provAnt = it->getProvincia();//Se carga la variable que guardará la clave anterior

        //Se inicializa los contadores
        contCadena = 0;
        totProv=0;


        while( it != lista.end() && provAnt == it->getProvincia() ) //Primer corte de control
        {
            cadenaAnt = it->getCadena();//var. que guardará la clave anterior

            //inicializo contadores
            total = 0;
            impuestos = 0;

            while ( it != lista.end() && provAnt == it->getProvincia() && cadenaAnt == it->getCadena() )//Segundo corte de control
            {
                total += it->getImporte();
                impuestos++;

                it++;//Se lee el siguiente
            }
            //Cambió la cadena
            cout << "Cadena " << cadenaAnt << "- impuestos "<< impuestos << "- total: " << total << endl;

            totProv += total;
            contCadena++;

        }
        //Cambio la provincia
        cout << "Prov - " << provAnt << "- cant cadenas "<< contCadena << "total prov "<< totProv  << endl;
    }

    return 0;
}

Esta es la impresión del código al std out:

Buenos Aires - Cadena1 - i1 - $10
Buenos Aires - Cadena1 - i2 - $20
Buenos Aires - Cadena2 - i1 - $30
CABA - Cadena1 - i1 - $20
CABA - Cadena1 - i2 - $30
CABA - Cadena2 - i1 - $60
CABA - Cadena2 - i2 - $50
Mendoza - Cadena1 - i1 - $20
Mendoza - Cadena1 - i1 - $30

--- Realizando corte de control ---
Cadena Cadena1- impuestos 2- total: 30
Cadena Cadena2- impuestos 1- total: 30
Prov - Buenos Aires- cant cadenas 2total prov 60
Cadena Cadena1- impuestos 2- total: 50
Cadena Cadena2- impuestos 2- total: 110
Prov - CABA- cant cadenas 2total prov 160
Cadena Cadena1- impuestos 2- total: 50
Prov - Mendoza- cant cadenas 1total prov 50