Translate

lunes, 18 de mayo de 2020

Juego de Serie en C# con .net core


Cuando quiero aprender un nuevo lenguaje desarrollo un juego de series, es decir aparece una serie con un valor faltante y el jugador debe completarlo.

Uno de los requerimientos no funcionales es que se pueda agregar una serie nueva fácilmente, para cumplirlo vamos a utilizar las ventajas de herencia y polimorfismo.

Vamos a desarrollar este juego en C# para ello creamos el proyecto con : 
$ dotnet new console -lang C#

Luego de esto abrimos nuestro editor favorito, podemos utilizar visual code.  

Empecemos desarrollo de la serie, la serie tiene como responsabilidad generarse y si lo hacemos de esta manera podemos utilizar la potencia del polimorfismo, para que no quede acoplado la generación de la serie con el desarrollo del juego:

using System;
namespace dotNetSecuencia.model
{
    public abstract class Secuencia
    {
        protected int[] nros = new int[4];

        public Secuencia() {
            this.generar();
        }

        public int[] Nro {
            get {
                return nros;
            }
        }
        
        public abstract void generar();
    }
}

Ahora vamos a ver las implementaciones de secuencia:

using System;

namespace dotNetSecuencia.model
{
    public class SecuenciaPar : Secuencia
    {        
        public override void generar() {
            int semilla = new Random ().Next (99) * 2;
            for (int i = 0; i<4; i++) {
                this.nros[i] = (semilla + i * 2); 
            }
        }
    }
}

Y ahora la secuencia Impar: 

using System;

namespace dotNetSecuencia.model
{
    public class SecuenciaImpar : Secuencia
    {
        public override void generar() {
            int semilla = new Random ().Next (99) * 2;
            for (int i = 0; i<4; i++) {
                this.nros[i] = (semilla + i * 2) + 1; 
            }
        }
    }
}

Y ahora la secuencia Fibonacci :

using System;

namespace dotNetSecuencia.model
{
    public class SecuenciaFibonacci : Secuencia
    {
        public override void generar() {
            int until = new Random ().Next (15);
            this.nros[0] = 0;
            this.nros[1] = 1;
            this.nros[2] = 1;
            this.nros[3] = 2;
            for (int i = 0; i<until; i++) {
                 this.nros[0] = this.nros[1];
                 this.nros[1] = this.nros[2];
                 this.nros[2] = this.nros[3];
                 this.nros[3] = this.nros[1] + this.nros[2];
            }
        }
    }
}

Ahora vamos a ver el juego, este tiene la responsabilidad de verificar si el usuario acertó y tambien debe llevar los puntos: 

 using System;

namespace dotNetSecuencia.model
{
    public class Juego
    {
        private int puntaje = 0;
        private Secuencia secuencia = null;

        public Juego() 
        {
            this.generarSecuencia();
        }

        public int Nro1 {
            get {
                return this.secuencia.Nro[0];
            }
        }

        public int Nro2 {
            get {
                return this.secuencia.Nro[1];
            }
        }

        public int Nro4 {
            get {
                return this.secuencia.Nro[3];
            }
        }

        public int Puntaje {
            get {
                return this.puntaje;
            }
        }

        public void generarSecuencia() 
        {
            int semilla = new Random ().Next (3);
            switch (semilla)
            {
                case 0 :
                    this.secuencia = new SecuenciaPar(); 
                break;
                case 1 :
                    this.secuencia = new SecuenciaImpar(); 
                break;
                default:
                    this.secuencia = new SecuenciaFibonacci();
                break;
            }
        }

        public bool isOk(int valor) {
            bool result = false;
            if (this.secuencia.Nro[2] == valor) {
                this.puntaje++;
                result = true;
            } else {
                this.puntaje--;
            }
            this.generarSecuencia();
            return result;
        }
    }

}

Ahora programemos la interfaz por consola por supuesto : 

using System;
using dotNetSecuencia.model;
namespace dotNetSecuencia
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Bienvenido !!");
            Juego juego = new Juego();
            String respuestaStr;
            do {
                Console.WriteLine("La secuencia es : ");
                Console.WriteLine(" " + juego.Nro1 + " " + juego.Nro2 + "  __  " + juego.Nro4 );
                respuestaStr = Console.ReadLine();
                int respuesta = Convert.ToInt32(respuestaStr);
                if (juego.isOk(respuesta)) {
                    Console.WriteLine("Ganaste!! Tu puntaje es " + juego.Puntaje);
                } else {
                    Console.WriteLine("Perdiste!! Tu puntaje es " + juego.Puntaje);
                }
                Console.WriteLine("Quieres seguir Jugando [n/s] ? ");
                respuestaStr = Console.ReadLine();
            } while (respuestaStr != "n");
        }
    }
}

Y eso es todo!!! a jugar se a dicho!!

Dejo el repositorio git: 

sábado, 16 de mayo de 2020

Coconut, programación funcional simple, elegante en python



Coconut es un lenguaje de programación funcional que compila a Python. Como todo codigo válido en Python es válido en Coconut , el uso de Coconut solo extenderá y mejorará lo que ya es capaz de hacer en Python.

¿Por qué usar Coconut? Coconut está hecho para ser útil. Coconut mejora el repertorio de los programadores de Python para incluir las herramientas de la programación funcional moderna, de tal manera que esas herramientas sean fáciles de usar e inmensamente poderosas; es decir, Coconut hace a la programación funcional lo que Python hizo a la programación imperativa. Y el código Coconut se ejecuta igual en cualquier versión de Python.

Instalar Coconut es tan fácil como

instalando Python,
abriendo una línea de comandos,
y escribir:

pip install coconut

Veamos un poco de código: 

Un "hola mundo" : 

"hello, world!" |> print

lambdas:

x -> x ** 2

pattern-matching:

match [head] + tail in [0, 1, 2, 3]:
    print(head, tail)

composición de funciones: 

(f..g..h)(x, y, z)

Data types: 

data Empty()
data Leaf(n)
data Node(l, r)

def size(Empty()) = 0
addpattern def size(Leaf(n)) = 1
addpattern def size(Node(l, r)) = size(l) + size(r)

Sin, más dejo link: 
http://coconut-lang.org/

Funciones recursivas en listas parte 2


Si deseamos realizar una operación en cada elemento de una lista map es la función indicada. map aplica una función a cada elemento de una lista

map f [x0,x1,x2] -- > [f x0, f x1, f x2]

map es una de las herramientas más utilizadas en programación funcional. Un estilo común es definir un conjunto de cálculos simples usando el map y componerlos.

map f (map g xs) = map (f . g) xs

Map se puede definir de esta manera: 

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

Una iteración sobre una lista para producir un valor se llama pliegue y existen varias variaciones: plegado desde la izquierda, plegado desde la derecha y varias variaciones que tienen que ver con la "inicialización" y algunas variaciones más avanzadas.

Los pliegues pueden parecer difíciles al principio, pero son extremadamente poderosos y se usan mucho. Y en realidad no son muy complicados.

foldl se pliega desde la izquierda y ed como una iteración en una lista, de izquierda a derecha.

Una aplicación típica es por ejemplo, foldl f z xs
Donde z es un valor inicial
El argumento xs :: [a] es una lista de valores que combinamos sistemáticamente utilizando la función proporcionada f
Una intuición útil: piense en el argumento z :: b como un "acumulador".
La función f toma el valor actual del acumulador y un elemento de lista, y da el nuevo valor del acumulador.

foldl :: (b-> a-> b) -> b -> [a] -> b

Similar a foldl, pero funciona de derecha a izquierda

foldr :: (a -> b -> b) -> b -> [a] -> b


Hemos visto que una lista [x0, x1, x2] también se puede escribir como
    x0: x1: x2: []

cons (:) sobre una lista usando la lista vacía [] como el acumulador da:

foldr (:) [] [x0, x1, x2]
  ->
  x0: x1: x2: []
¡Esto es idéntico a construir la lista usando (:) y []! Podemos formalizar esta relación de la siguiente manera:

foldr cons [] xs = xs
 
Veamos algunos ejemplos: 

suma xs = foldr (+) 0 xs
producto xs = foldr (*) 1 xs

De hecho, podemos "factorizar" las x que aparecen a la derecha de cada lado de la ecuación, y escribir:

sum = foldr (+) 0
producto = foldr (*) 1

(Esto a veces se denomina estilo "point free" porque está programando únicamente con las funciones; los datos no se mencionan directamente).


viernes, 15 de mayo de 2020

Programación explicada con música



Funciones recursivas en listas

Hay dos enfoques para trabajar con listas:
  • Escribir funciones utilizando definiciones recursivas que atraviesan la estructura de la lista.
  • Utilizar funciones estandar. 
Se prefiere el segundo enfoque, pero las funciones estándar de procesamiento de listas deben definirse, y esas definiciones usan el primer enfoque (definiciones recursivas).

Se crea una lista a partir de la lista vacía [] y la función cons :: a → [a] → [a] la cual se puede escribir con ":"

Una lista se puede definir como : 
  • [] o
  • (x: xs) para algunas x (el encabezado de la lista) y xs (la cola) donde (x: xs) 
La lista esta definida de forma recursiva, el caso base de la recursión es [], el caso de recursión (o inducción) es (x: xs).

La longitud de una lista se puede calcular de forma recursiva de la siguiente manera:

length :: [a] -> Int           -- function type
length [] = 0                  -- base case
length (x:xs) = 1 + length xs  -- recursion case

filter recibe un predicado (una función que da un resultado booleano) y una lista, y devuelve una lista de los elementos que satisfacen el predicado.
filter :: (a->Bool) -> [a] -> [a]

filter (<5) [3,9,2,12,6,4] -- > [3,2,4]

La podemos definir así:

filter :: (a -> Bool) -> [a] -> [a]
filter pred []    = []
filter pred (x:xs)
  | pred x         = x : filter pred xs
  | otherwise      = filter pred xs

Muchos cálculos que serían calculados con bucles for / while en un lenguaje imperativo se expresan naturalmente como cálculos de lista en un lenguaje funcional.

En Haskell tenemos: 
  • Map : Realizar un cálculo en cada elemento de una lista
  • Foldl: Iterar sobre una lista, de izquierda a derecha 
  • Foldr: Iterar sobre una lista, de derecha a izquierda

Es una buena práctica usar estas tres funciones cuando corresponda

Podemos expresar un gran cálculo "encadenando" una secuencia de funciones que realizan cálculos más pequeños. Por ejemplo tenemos un argumento de tipo a, aplicamos una función g :: a−> b, obteniendo un resultado intermedio de tipo b luego aplicamos una función f :: b−> c al resultado intermedio, obteniendo el resultado final de tipo c. El cómputo completo (primero g, luego f) se escribe como f∘g. Esta es la notación matemática tradicional; solo recuerda que en f∘g, las funciones se usan en orden de derecha a izquierda.

Haskell utiliza el . como operador de composición de funciones

(.) :: (b->c) -> (a->b) -> a -> c
(f . g) x = f (g x)


lunes, 11 de mayo de 2020

Tutorial de Python, Guía básica de Python en español


Quiero compartir una página que nos permite aprender python de forma fácil y práctica. 

Reza la página: 

"Este tutorial de Python es una introducción a los principales aspectos y características del lenguaje. El tutorial sirve de guía de inicio para dar los primeros pasos con Python, familiarizarse con su sintaxis, conocer los principales tipos de datos y entender algunos conceptos clave.

¿Para quién es este tutorial Python?
En principio, el tutorial es apto para todos los públicos. Si nunca has programado en Python, te servirá para aprender lo básico del lenguaje. Por el contrario, si ya lo conoces y/o has desarrollado alguna aplicación con él, te será útil para reforzar conceptos y descubrir cosas nuevas que quizá desconozcas."

domingo, 10 de mayo de 2020

HumanOS


HumanOS es un blog de software libre. Tiene post sobre temas de variados de diferentes temas y el propósito de este blog es mostrarle a las personas cuales son los provechos que se les puede sacar al software libre

Dejo link:
http://humanos.uci.cu/

Juego de adivinanzas en Haskell

Vamos a ser un juego de adivinanzas. Va a haber una palabra que el jugador debe adivinar. Para cada turno del juego, el jugador adivina una sola letra. Si esa letra es correcta, las letras adivinadas se muestran en los lugares correctos de la palabra. Si esa letra es incorrecta, el usuario pierde una estrella. Una vez que el usuario no tiene estrellas, ha perdido el juego. Sin embargo, si el usuario adivina todas las letras de la palabra, ha ganado el juego, como el ahorcado.

Comience creando un archivo de texto vacío llamado ahorcado.hs: la extensión hs es para indicar que este archivo contiene el código fuente de Haskell.


El corazón del juego consiste en verificar las conjeturas del jugador. Queremos saber si la suposición fue correcta. Este resultado es un valor Bool, ya sea verdadero o falso. Necesitamos actualizar la palabra mostrada, si la suposición fue correcta, reemplazando los guiones apropiados en la palabra mostrada con el carácter correctamente adivinado. Por lo tanto, el tipo de resultado de la función es un par (Bool, String). El primer elemento del par es el resultado de la suposición. El segundo elemento es la Cadena que se mostrará al usuario para la próxima ronda.

Ahora, la función de verificación necesita saber:
  • La palabra secreta, una cadena
  • La palabra que se muestra en pantalla o real, también una cadena
  • El carácter elegido por el jugador.
Estas son las entradas a la función de verificación. Entonces ahora podemos establecer el tipo de la función:

check :: String -> String -> Char -> (Bool, String)

Siempre es útil determinar primero el tipo de función. Esto enfoca su atención en lo que se supone que debe calcular la función, y qué datos necesita para hacerlo. Los buenos ingenieros de software hacen las especificaciones antes de la implementación.

¿Cómo será el cuerpo de la función de verificación? La suposición del jugador es correcta si y solo si el carácter elegido c está en la palabra secreta. Entonces la suposición es correcta si :

c palabra `elem`

La nueva palabra que se mostrará será:

[(if x==c then c else y) | (x,y) <- zip word display]

Esta es una lista de comprensión, donde seleccionamos cada letra de la palabra real o de la pantalla anterior. La palabra es texto sin formato, mientras que la pantalla comienza con todos los caracteres discontinuos.

check :: String -> String -> Char -> (Bool, String)
check word display c
  = (c `elem` word, [if x==c
          then c
          else y | (x,y) <- zip word display])

La siguiente función que definiremos es la función de giro. Esta es la función que se llamará cada vez que sea el turno del jugador para ingresar un caracter. Primero debemos comprobar cuántas conjeturas le quedan al jugador:

if n == 0

Si quedan algunas conjeturas, entonces necesitamos ver si el jugador completo la palabra o no:

if word == display

Entonces tendremos dos verificaciones if, cada una seguida de mensajes de estado putStrLn y el final de la secuencia de llamada a la función (ya que es el final del juego). Sin embargo, si ninguna de las condiciones es verdadera, entonces el jugador puede tomar un turno, por lo que llamamos a otra función para obtener otro personaje de la entrada del usuario.

turn :: String -> String -> Int -> IO ()
turn word display n =
  do if n==0
       then putStrLn "You lose"
       else if word==display
              then putStrLn "You win!"
              else mkguess word display n

Tenga en cuenta que hay una forma más ordenada de escribir la función de giro, utilizando Haskell guards, pero no vamos a utilizarlas para simplificar el programa.

mkguess word display n =
  do putStrLn (display ++ "  " ++ take n (repeat '*'))
     putStr "  Enter your guess: "
     q <- getLine
     let (correct, display') = check word display (q!!0)
     let n' = if correct then n else n-1
     turn word display' n'

¿Cuál es el tipo de mkguess? ¿Puedes resolverlo y agregarlo antes de la definición de la función? Tomamos una línea de entrada del usuario, pero solo usamos el primer carácter para la suposición. Esto fallará si el usuario simplemente presiona ENTER sin escribir ningún carácter, ya que q será una cadena vacía.

Bien, ahora solo necesitamos una función de nivel superior:

starman :: String -> Int -> IO ()
starman word n = turn word ['-' | x <- word] n

Esta función toma dos argumentos, el primero es la palabra a adivinar y el segundo es el número de conjeturas incorrectas que el jugador tiene permitido.

Pongamos todas estas cuatro funciones en un archivo de texto, llamado starman.hs

Guarde el archivo, luego inicie ghci quizás escribiendo ghci en un símbolo del sistema de DOS, ejecutando WinGHCi o escribiendo ghci en una ventana de terminal (macOS o Linux).

Si está en el directorio correcto, es decir, en el que guardó starman.hs, debería poder escribir

:l starman.hs

y el programa debería cargarse. Dirá algo como:

Prelude> :l ahorcado.hs
[1 of 1] Compiling Main             ( ahorcado.hs, interpreted )
Ok, one module loaded.

Starman "funcionalmente" 5

y comienza a jugar el juego! Volverá a la solicitud de GHCi cuando se complete la función starman.

Y Listo!!

miércoles, 6 de mayo de 2020

martes, 5 de mayo de 2020

Swift es como Kotlin


Quiero compartir una pagina que compara Swift con Kotlin.

Lo comparto porque hasta esta pagina, no me parecían similares pero son igualitos :D

Dejo link: http://nilhcem.com/swift-is-like-kotlin/

lunes, 4 de mayo de 2020

Libro gratuito de Scala




Quiero compartir el siguiente libro de Alvin Alexander que es gratuito y esta bueno. El libro tiene aproximadamente 225 páginas y proporciona una breve pero completa introducción al lenguaje Scala. Según el autor.

Sin más dejo el link: https://alvinalexander.com/scala/scala-book-free/

Ejemplo de pattern matching en Scala

La idea es que tenemos que saber si un string (en realidad lo hice con lista de caracteres) tiene sus paréntesis balanceados. Onda si abre uno lo tiene que cerrar y por supuesto, no puede comenzar cerrando.

Veamos como podemos solucionar esto en Scala:

def balance(palabra : List[Char]) : Boolean = {

  def balanceAux(palabra : List[Char], n : Int) : Int = palabra match {
    case List() => n
    case '('::tail => balanceAux(tail, n + 1)
    case ')'::tail => if (n == 0) -1 else balanceAux(tail, n - 1)
    case _::tail => balanceAux(tail, n)
  }

  balanceAux(palabra, 0) == 0
}

Se puede ver que el ultimo case tiene el valor que no es un paréntesis, cualquier otra letra y esto lo decimos con "_"

Es un ejemplo bastante pavo, pero podemos ver la potencia que tiene Pattern matching

domingo, 3 de mayo de 2020

Primer acercamiento a I/O en Haskell



Las funciones puras son el tipo de funciones toman valores como argumentos, procesan algunos de esos valores y luego devuelven un valor de resultado. Una función pura no depende del "estado del mundo". El cálculo es totalmente autónomo e independiente. Dados los mismos argumentos, una función pura siempre devolverá el mismo resultado.

I/O es impuro. Las operaciones de entrada y salida son impuras. Influyen e interactúan con el "mundo exterior". Esencialmente, esta es la única forma de hacer que las computadoras hagan cosas interesantes.

La función getLine lee la entrada del usuario y la devuelve como un tipo especial de valor de cadena: una cadena de I/O. La función putStrLn toma una entrada de cadena y la imprime en el terminal, devolviendo un valor de IO vacío, es decir, IO ().

Los tipos IO es lo que nos permite no mezclar funciones puras e impuras: el sistema de tipos nos mantiene honestos. Sabemos por el tipo de función si está involucrado con I/O.

Veamos esta simple función :

let greet() = do
    planet <- getLine
    home <- getLine
    putStrLn ("greetings " ++ planet ++ "ling.")
    putStrLn ("I am from " ++ home ++ ".")
    putStrLn "Take me to your leader."

Podemos escribirlo como una (¡muy larga!) Línea única:

do { planet <- getLine; home <- getLine; putStrLn ("greetings " ++ planet ++ "ling."); putStrLn ("I am from " ++ home ++ "."); putStrLn "Take me to your leader."}

Si ejecutamos esto, escribiendo "Earth" y "Mars". Entonces deberías ver:

greetings Earthling.
I am from Mars.
Take me to your leader.

Tenga en cuenta que el orden es importante aquí:

queremos que la primera llamada getLine obtenga el nombre del planeta en el que hemos aterrizado
queremos que la segunda llamada getLine obtenga el nombre de dónde somos.

El orden de evaluación de funciones no importa en código puro, por Ej.

let a = reverse "winston"
    b = reverse "churchill"
in "sir " ++ a ++ " " ++ b

No importa si hacemos el primer reverso antes del segundo; el resultado de la expresión sigue siendo el mismo. Sin embargo, este no es el caso de I/O. La secuencia es vital para las acciones de I/O.

La notación do nos permite secuenciar acciones. Esto se parece a una secuencia de comandos en un lenguaje de programación imperativo. Sin embargo, hacer es solo azúcar sintáctico. Debajo, se reescribe como una cadena de llamadas de función donde la salida de la primera función se convierte en la entrada de la segunda función. El operador de enlace realiza esta secuencia de funciones. Es una parte clave de la mónada IO. Estamos comenzando a arañar la superficie de Haskell IO y descubrimos que es muy complejo. Reservaremos la discusión de Mónadas y operadores de enlace para más adelante en el curso.

Por ahora, todo lo que necesitamos entender es:
  • Las operaciones de I/O son impuras
  • Puede usar do para especificar una secuencia de acciones
  • use <- dentro de un do para asociar valores de entrada con nombres
  • cualquier valor o función que implique I/O tiene IO en su tipo
  • una secuencia de acciones de  I/O se describe como estar en la Mónada de IO

sábado, 2 de mayo de 2020

Tail recursion


Cuando me tocaba enseñar recursibidad, hago el típico ejemplo de la Sucesión de Fibonacci la cual podríamos definir de este modo :

f(0) = 1
f(1)= 1
f(n) = f(n - 1) + f(n - 2)

Y listo! esto es muy fácil de programar en scala, sería:

def fibo(n: Int) : Long = if (n==0 || n==1) 1 else fibo(n-1) + fibo(n-2)

esto funciona si no pretendemos calcular el fibonacci en la posición  n = 55

El problema es que este algoritmo tiene una complejidad de dos a la n.

Podemos resolver este problema empezando desde el comienzo. Si lo pensamos de forma imperativo, sería algo así :

  def fibonacci(n: Int): Long = {
     var fa = 1l
     var faa = 1l
     var i = 1
    while (i < n) {
      var aux = fa
      fa = fa + faa
      faa = aux
      i = i + 1
    }
    faa
  }

Es decir si empezamos desde el principio ya tenemos los f(n-1) y f(n-2), pero no solo es solucionable de este modo de forma imperativa, tambien podemos utilizar recursividad de este modo :

  def fibonacci(n: Int): Long = {
     def fibonacciAux(n: Int, fa: Long, faa: Long) : Long =
       if (n == 3) fa + faa
       else fibonacciAux(n-1, fa + faa, fa)

    if (n<=2) 1
    else fibonacciAux(n,1,1)
  }


Tail recursion es un tipo específico de recursión. En particular, se dice que una función recursiva usa tail recursion si la llamada a sí misma ocurre como la última acción de la función.

Cuando una función recursiva se llama a sí misma varias veces, las llamadas van acumulándose en la pila de llamadas, que de por sí, no es muy eficiente porque consume memoria. Pero aun peor, si se acumulan demasiadas llamadas en esta pila, eventualmente se desborda la pila de llamadas y te lanza la excepción java.lang.StackOverflowError.

Esta ineficiencia es un problema serio para un lenguaje funcional como Scala que promueve el uso de funciones recursivas.

Sin embargo, cuando Scala detecta que la función recursiva usa tail recursion, entonces el compilador es capaz de efectuar una optimización al código de forma automática, efectivamente eliminando la recursividad enteramente y reemplazándolo con un simple bucle. Esta optimización es comúnmente conocida como tail call optimization.

Y listo! La función de fibonacci funciona rápido porque utilizamos tail recursion y porque bajamo su complejidad.

Dejo link :
https://es.stackoverflow.com/questions/121965/que-es-tail-recursion

Libros gratuitos de Haskell

Me gustaría recomendar estos libros gratuitos :


Real World Haskell
Real World Haskell es un típico libro de lenguaje de programación O’Reilly. El contenido está disponible en línea de forma gratuita : http://book.realworldhaskell.org/read/

Learn You a Haskell
Learn You a Haskell es una introducción muy informal, pero hace un gran trabajo al explicar conceptos complejos. Puedes leerlo : http://learnyouahaskell.com/chapters