Problemas

Agosto-Diciembre 2018

Enero-Junio 2018

Recomendaciones

  1. 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.
  2. 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).
  3. 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
            )
          )
        )
      )
      
  4. 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.
  5. 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 ifs 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.

    • 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)))
        
  6. 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.