Repaso: control de flujo

En esta página
Enterate del próximo post
Un mail cuando publico algo nuevo. Sin spam y te podés dar de baja cuando quieras.

Un mail cuando publico algo nuevo. Sin spam y te podés dar de baja cuando quieras.
Organizar una mudanza a un departamento nuevo empieza mucho antes de que llegue el camión: hay que calcular cuántas cajas van a hacer falta, cuánto va a costar el traslado completo, si conviene contratar ayuda extra para cargar los muebles más pesados, y cómo empacar lo frágil sin que se rompa nada en el camino. Ninguna de esas cuatro preguntas se resuelve con la misma herramienta, y el enunciado nunca lo aclara: hay que reconocerlo en cada tramo, a medida que aparece.
Los cuatro posts anteriores de esta serie presentaron, uno por uno, secuencia, descomposición, condicionales con operadores lógicos y bucles. Este post no agrega ninguna herramienta nueva: resuelve un único problema, de principio a fin, usando las cuatro ya conocidas. Ningún título de sección va a decir "acá va un bucle" ni "acá va una condición": eso es, en cada tramo, lo primero que hay que decidir.
La cocina es la habitación con más objetos sueltos, así que es la primera en resolverse. Entre platos, ollas y utensilios pesa 172 kilogramos en total, y cada caja de mudanza resiste hasta 15 kilogramos antes de arriesgarse a romperse por abajo. Hace falta saber cuántas cajas hay que conseguir antes de poder pedir el flete, porque el precio del flete depende de cuánto volumen hay que trasladar.
Pista: esto ya apareció antes, con otro disfraz. El post de secuencia calculó cuántos paquetes de un ingrediente hacían falta a partir de un peso total y el peso de cada paquete, y el de descomposición calculó cuántos colectivos hacían falta a partir de una cantidad de personas y una capacidad fija. Los tres problemas comparten la misma forma: una cantidad total, un límite por unidad, y una división que casi nunca da un número entero.
algoritmo cajas_necesarias(peso_total, peso_maximo_caja):
dividir peso_total por peso_maximo_caja
redondear ese resultado hacia arriba
devolver el resultadoCon 172 kilogramos y un máximo de 15 por caja: 172 dividido 15 da 11,47, que redondeado hacia arriba son 12 cajas. Sobran casi 8 kilogramos de margen en la última caja, pero ese margen no le sirve a ninguna otra: cada caja es una unidad completa, no importa si queda medio vacía.
Con las cajas resueltas, ya se puede calcular el costo total. Son tres partes: el costo de las cajas (12, a $900 cada una), el costo del flete (8 kilómetros hasta el departamento nuevo, a $2500 el kilómetro) y un seguro de mudanza a valor fijo de $5000, sin importar cuánto se traslade.
Pista: antes de escribir un único algoritmo largo, conviene preguntarse, igual que con el costo del cumpleaños del post de descomposición, si estas tres partes necesitan saber algo unas de otras. El costo de las cajas no depende de la distancia, el costo del flete no depende de cuántas cajas hay, y el seguro no depende de ninguno de los dos.
algoritmo costo_cajas(cantidad_cajas, precio_caja):
multiplicar cantidad_cajas por precio_caja
devolver el resultadoalgoritmo costo_flete(distancia, precio_por_km):
multiplicar distancia por precio_por_km
devolver el resultadoalgoritmo costo_mudanza(cantidad_cajas, precio_caja, distancia, precio_por_km, seguro):
llamar a costo_cajas con cantidad_cajas y precio_caja para obtener las cajas
llamar a costo_flete con distancia y precio_por_km para obtener el flete
sumar las cajas, el flete y seguro
devolver el resultadoCon las 12 cajas a $900: $10800. Con 8 kilómetros a $2500 el kilómetro: $20000. Sumando el seguro fijo de $5000, el total es $35800. Ninguna de las tres partes tuvo que revisar cómo estaba resuelta otra: costo_cajas no sabe que existe un seguro, y el seguro no sabe que existe un flete.
Queda una pregunta que no depende de ningún número recién calculado: si hace falta contratar ayuda para cargar los muebles. Hay un ropero y un sofá, los dos objetos pesados de la casa, y solo dos personas disponibles el día de la mudanza. La regla que se maneja es: conviene contratar ayuda si hay muebles pesados y las personas disponibles son menos de tres, o directamente si la cantidad de cajas supera las 15, sin importar cuánta gente haya, porque a partir de ahí ya es demasiado volumen para dos viajes.
Pista: son dos criterios distintos, y alcanza con que se cumpla cualquiera de los dos para contratar ayuda. Uno de esos criterios, además, junta dos condiciones a la vez: no basta con que haya muebles pesados, también hace falta que falte gente para cargarlos.
algoritmo necesita_ayuda_extra(hay_muebles_pesados, personas_disponibles, cantidad_cajas):
si (hay_muebles_pesados y personas_disponibles es menor a 3) o cantidad_cajas es mayor a 15:
devolver "sí"
sino:
devolver "no"Con muebles pesados y solo 2 personas disponibles: la primera parte de la condición ya es verdadera (hay muebles pesados, y 2 es menor a 3), así que devuelve "sí" sin necesidad de mirar cuántas cajas hay -que además son 12, ni siquiera superan las 15-. Si en cambio hubiera 4 personas disponibles y las mismas 12 cajas, ninguna de las dos partes se cumpliría, y el resultado sería "no".
El último tramo es distinto de los anteriores: no alcanza con calcular un número, hay que repetir una acción. Hay 5 objetos frágiles -una fuente de vidrio, dos portarretratos, un florero y una lámpara- y cada uno tiene que envolverse en papel burbuja antes de ir a la caja.
Pista: antes de elegir cómo escribir la repetición, conviene preguntarse algo que ya apareció en el post de bucles: ¿se cuentan los objetos frágiles antes de empezar a envolver, o se envuelve uno detrás de otro hasta que no queda ninguno en la pila? Las dos preguntas llevan a la misma cantidad de vueltas, pero no al mismo bucle.
Si se cuentan primero, la cantidad de vueltas se conoce antes de la primera:
algoritmo empacar_fragiles_contados(cantidad_fragiles):
repetir contador desde 1 hasta cantidad_fragiles:
envolver el siguiente objeto frágil en papel burbuja
devolver "empacados"Si en cambio no se cuentan y se trabaja directo sobre la pila hasta vaciarla, la cantidad de vueltas no está escrita en ningún lado del algoritmo, aunque el resultado final sea el mismo:
algoritmo empacar_fragiles_hasta_vaciar():
mientras queden objetos frágiles sin envolver:
envolver el siguiente objeto frágil en papel burbuja
devolver "empacados"Con los 5 objetos frágiles, los dos algoritmos terminan después de la misma quinta vuelta y devuelven lo mismo. La diferencia no está en el resultado, está en qué necesita saber el algoritmo antes de arrancar: el primero necesita el número de antemano, el segundo solo necesita poder preguntar si todavía queda algo por envolver. Ninguno de los dos es más correcto que el otro; son la misma solución escrita desde dos preguntas distintas, igual que ya pasó con el candado de la pileta en el post de condicionales.
Con los cuatro tramos resueltos por separado, arman un único algoritmo que llama a cada uno y junta los resultados, siguiendo la misma idea de costo_cumpleanos y costo_excursion del post de descomposición: el algoritmo principal no vuelve a calcular nada, solo pide los resultados y los combina.
algoritmo plan_mudanza(peso_cocina, peso_maximo_caja, precio_caja, distancia, precio_por_km, seguro, hay_muebles_pesados, personas_disponibles, cantidad_fragiles):
llamar a cajas_necesarias con peso_cocina y peso_maximo_caja para obtener la cantidad_cajas
llamar a costo_mudanza con cantidad_cajas, precio_caja, distancia, precio_por_km y seguro para obtener el costo_total
llamar a necesita_ayuda_extra con hay_muebles_pesados, personas_disponibles y cantidad_cajas para obtener la ayuda
repetir contador desde 1 hasta cantidad_fragiles:
envolver el siguiente objeto frágil en papel burbuja
devolver la cantidad_cajas, el costo_total y la ayudaCon los datos de todo el post (172 kilogramos de cocina, cajas de 15 kilogramos, $900 por caja, 8 kilómetros a $2500, seguro de $5000, muebles pesados con 2 personas disponibles, y 5 objetos frágiles): plan_mudanza devuelve 12 cajas, un costo total de $35800 y que sí hace falta ayuda extra, después de envolver los 5 objetos frágiles uno por uno. Cuatro herramientas distintas -una cadena de dependencia, una descomposición en partes independientes, una condición con operadores lógicos y un bucle- resolvieron, cada una, el tramo que le correspondía, sin que el enunciado general lo separara por adelantado.
Un problema real casi nunca necesita una sola herramienta: necesita reconocer, tramo por tramo, cuál de las ya conocidas corresponde, sin que nadie lo diga de entrada. Secuencia, descomposición, condicionales y bucles no compiten entre sí por resolver el mismo problema: se combinan, porque la mayoría de los problemas reales los necesita a los cuatro a la vez.
El próximo paso de la serie deja el control de flujo por un momento y entra en los datos que un algoritmo maneja mientras se ejecuta: qué significa que un valor cambie con el tiempo, como idea, más allá de cualquier sintaxis puntual para declararlo.