Problemas
Agosto-Diciembre 2018
Enero-Junio 2018
- Cumplen
- Número creciente
- Número contenido en
- Secuencia equilibrada
- Número feliz
- Dígito verificador del IMSS
- Mayores y menores repeticiones
- Cifrado por grupos inversos
- Número de dígitos irrepetibles
- Problema de las puertas
- Centro de gravedad
- Cuadrado en el plano cartesiano
Recomendaciones
Resuelve los problemas definiendo funciones totales:
- No se presentan errores durante la aplicación de una función.
- Las funciones siempre regresan algún resultado.
Resuelve los problemas dando prioridad a una función de order superior:
map
,filter
,reduce
,apply
,iterate
,take-while
, etc.- No hagas uso de recursividad de cola o tail recursion (
letfn
en Clojure).
- No hagas uso de recursividad de cola o tail recursion (
Evita el razonamiento imperativo.
No pierdas el tiempo pensando en referencias ó punteros.
- En programación funcional no existe eso: las funciones reciben valores y regresan valores.
No uses binding (
let
,letfn
,def
,defn
, etc) como si fuera asignación.- Recuerda los conceptos de función pura y transparencia referencial.
No hagas uso indebido de if, cond, case, etc (incluyendo anidaciones innecesarias).
- Son expresiones por lo que siempre regresan algo a alguien.
Evita condicionales que expresen un pleonasmo, ejemplos en programación imperativa-orientada-a-objetos pero que puedes también encontrar en programación funcional:
Pleonasmo (mal):
if (x > y) { return true; } else { return false; }
Solución (bien):
return (x > y);
Pleonasmo (mal):
if ((x > 0) == true) { return true; } else { return false; }
Solución (bien):
return (x > 0);
Pleonasmo (mal):
if (x == true) { return false; } else { return true; }
Solución (bien):
return !x;
Pleonasmo (mal):
if ((x >= 10) && (x <= 20)) { return true; } else { return false; }
Solución (bien):
return (x >= 10) && (x <= 20);
Pleonasmo (mal):
if ((x >= 1) && (x <= 50)) { if ((y >= 1) && (y <= 50)) { return true; } else { return false; } } else { return false; }
Solución (bien):
return ((x >= 1) && (x <= 50)) && ((y >= 1) && (y <= 50));
No busques dar ordenes/comandos/sentencias precisas/secuenciales a la computadora.
Busca hacer transformación de valores-colecciones de manera que evites pensar en:
- Guardar resultados temporales en variables: uso incorrecto de binding.
- Hacer uso de banderas, centinelas o auxiliares: uso incorrecto de binding.
- Condicionar la ejecución de ordenes: uso incorrecto de
if
,case
,cond
, etc.
No imprimas algo en pantalla: mostrar errores mediate el uso de strings como el resultado de aplicar una función. Ejemplo:
Problema (mal): se usa un string para "imprimir" errores.
(defn el-número-negativo-mayor [xs] (if (empty? xs) "Error, xs está vácio" (let [resultado (filter neg? xs)] (if (empty? resultado) "Error, no existen números negativos" (apply max resultado)))))
Soluciones (bien):
Primera solución: se define una sola función que resuleve el problema:
(defn el-número-negativo-mayor [xs] (let [resultado (filter neg? xs)] (if (empty? resultado) -1 (apply max resultado))))
Segunda solución: se definen dos funciones y por composición una tercerca que resuelve el problema:
(defn los-números-negativos [xs] (filter neg? xs)) (defn el-negativo-mayor [xs] (if (empty? xs) -1 (apply max xs))) (def el-número-negativo-mayor (comp el-negativo-mayor los-números-negativos))
No busques replicar la sintaxis de otro lenguaje de programación: los lenguajes funcionales suelen tener una sintaxis ajena/diferente/contraria a lo que la mayoria se acostumbra: bloques de código basado en el Lenguaje de Programación C. Ejemplo:
(defn unaFuncion [un_valor] (let [unaVariable otro_valor] (if (algoEsVerdadCon un_valor) (ejecuto otraFuncion un_valor ) (case otroValor esto (if algoEsVerdadCon unaVariable (ejecuto alguna_otra_funcion) "Error: hubo un error." ) aquello (ejecuto otraFuncion un_valor) o lo_otro ) ) ) )
Razona siempre en las propiedades de las funciones.
- Dado estos argumentos, esta función regresa este resultado.
- No importa cuantas veces aplique esta función, siempre regresa lo mismo dado los mismos argumentos.
Da la prioridad/importancia a las colecciones disponibles.
Mientras que en la programación imperativa-orientada-a-objetos se tiene cierto "miedo" a las colecciones (estructuras de datos), en la programación funcional es de lo más normal hacer uso de ellas.
No todo es un arreglo.
En la programación imperativa-orientada-a-objetos la única estructura de datos que suelen conocer es el arreglo por lo que los algoritmos se basan en el acceso a los elementos en base a sus índices como si de una sección de la memoria RAM se tratara, consecuencias:
- Código fuente propenso a errores: por lo que requieren de un abuso de
if
s anidados para evitarlos, como por ejemplo acceder a un índice inexistente en el arreglo. - Código fuente demasiado complejo de analizar/leer: por la naturaleza de recorrer o iterar una y otra vez el arreglo.
Puedes encontrar esto en un lenguaje como Clojure por el abuso de
.indexOf
,get
,nth
,first
,second
, etc.- Código fuente propenso a errores: por lo que requieren de un abuso de
No todo es una cadena de carácteres o string.
En la programación imperativa-orientada-a-objetos se puede conocer por primera vez al tipo de valor conocido como string, con el cual se representa una secuencia de carácteres/letras/símbolos de un alfabeto, sin embargo es importante observar que no es una colección o estructura de datos propia para otros tipos de valores. El problema principal surje a partir de un abuso de la misma dando pie a errores en la lectura/ejecución de algún pedazo de código al ver a un string como si fuera un arreglo de datos.
Aunado a las cadenas de carácteres se encuentra el uso incorrecto, llegando al abuso, de las expresiones regulales dando como resultado (al igual que con arreglos) a código propenso a errores y de difícil lectura. Recomendación: evita el uso de expresiones regulares.
Existen colecciones finitas y colecciones infinitas.
Evita pensar solo en colecciones finitas (o de cantidad fija de elementos) y considera que puedes tener "generadores" de valores a quienes les puedes pedir uno por uno un valor que contengan (colecciones infinitas o perezosas):
=> (take 10 (map vector (range) (range)))
Es importante que tus soluciones sean propias/originales, concisas, directas y sin rodeos.
- Evita copiar&pegar código buscado en la web. Así como a tí te resulta fácil buscar soluciones en la web, a otras personas les resulta fácil encontrar y darse cuenta que no resuelves los problemas por tu propia cuenta.