martes, 30 de noviembre de 2010

Patrones de diseño

Un patrón de diseño es una solucion repetida generalmente aplicada a un problema comunmente ocurrente en el diseño de software.

Un patrón de diseño no es una solucion final absoluta que puede transformarse directamente a código, es una descripción o plantilla de como resolver un problema que puede usarse en muchas situaciones diferentes.

Los patrones de diseño pueden acelerar el proceso de desarrollo, al proporcionar paradigmas que muchas veces han sido probados útiles.

Patrones de creación

Estos patrones de diseño tratan acerca de la instanciación de clase. Esta clase puede dividirse en patrones de creación de clases y patrones de creación de objetos. Los patrones de creación de clases usan herencia en el proceso de instanciación y los patrones de creación de objetos usan la delegación para realizar el trabajo.

  • Abstract Factory
Provee una interfaz para crear familias de objetos relacionados o dependientes, sin especificar sus clases concretas.



  • Builder
Separa la construcción de un objeto complejo de su representación de tal manera que el mismo proceso de construcción pueda crear distintas representaciones.



  • Factory Method
Define una interfaz para crear un objeto, pero deja que las subclases decidan que clase instanciar. Permite que una clase delegue la creación de objetos a sus subclases.




  • Object Pool
Evade costosa adquisición y gasto de recursos, mediante el reciclado de objetos que no se encuentran en uso.




  • Prototype
Especifica el tipo de objetos a crear, usando una instancia prototipo y creando nuevos objetos copiando este prototipo.


  • Singleton
Garantiza que una clase sólo tenga una instancia y provee de un punto de acceso global a ella.






Patrones estructurales

Estos patrones de diseño tratan sobre la composicion de clases y objetos. Estos patrones usan herencia para componer interfaces. Definen maneras de componer un objeto para obtener nuevas funcionalidades.

  • Adapter
Transforma la interfaz de una clase en una interfaz que el cliente espera. Este patron permite que distintas clases trabajen en juntas.






  • Bridge
Desempareja la abstraccion de un objeto de su implementacion de tal manera que las dos puedan variar independientemente.



  • Composite
Compone objetos en estructuras tipo árbol para representar jerarquías de parte completa. Permite tratar objetos individuales y composiciones de objetos uniformemente.



  • Decorator
Agrega responsabilidades a un objeto dinámicamente. Provee de una alternativa flexible a subclasificación para extender la funcionalidad.



  • Facade
Provee de una interfaz unificada de un conjunto de interfaces en un subsistema. Define una interfaz de alto nivel para hacer un subsistema fácil de usar.



  • Flyweight
Usa el compartir para soportar grades números de objetos "de grano fino" de manera eficiente.



  • Private Class Data
Controla el acceso de escritura a los atributos de una clase.



  • Proxy
Proporciona un representante de otro objeto para controlar el acceso al objeto que está siendo representado.









Patrones de comportamiento

Estos patrones tratan acerca de la comunicacion entre objetos y conjuntos de estos.

  • Chain of responsibility
Evita emparejar el objeto que envía una petición con el objeto que recibe esa petición, al permitir que mas de un objeto pueda tomar la responsabilidad de la petición enviada. Encadena los objetos que podrán recibir la petición y pasa la petición a lo largo de la cadena hasta que un objeto acepte la petición.




  • Command
Transforma una peticion en un objeto de tal manera que puedas catalogar clientes con diferentes peticiones, mantener un registro de las peticiones y soportar el deshacer operaciones.



  • Interpreter
Permite definir una representación de la gramática de un lenguaje para usarla en un interprete del lenguaje.



  • Iterator
Provee de una manera de acceder secuencialmente a los elementos de una colección sin exponer su representación interna.



  • Mediator
Crea un objeto mediador que encapsula el cómo un conjunto de objetos interactúan. Mediator promociona la organización de como interactúan los objetos, sin necesidad de que entre ellos mismos se referencíen.



  • Memento
Sin violar el principio de encapsulación, captura y externaliza el estado interno de un objeto de manera que el objeto pueda regresar al estado después.







  • Null Object
Encapsula la ausencia de un objeto al proveer de una alternativa que ofrece un comportamiento predefinido de "hacer nada". Permite que el comportamiento de hacer nada, sea el valor predefinido de un objeto.




  • Observer
Define una dependencia de uno a muchos entre objetos de tal manera que cuando un objeto cambie su estado, todas las dependencias del objeto sean notificadas y actualizadas automáticamente.



  • State
Permite que un objeto cambie su comportamiento al cambiar su estado. El objeto parecerá que cambio de clase.



  • Strategy
Crea un conjunto de algoritmos encapsulados intercambiables. Permite que un algoritmo varíe independientemente de los clientes que los usan.



  • Template method
Otorga el derecho a subclases de re definir ciertos pasos o procedimientos de un algoritmo sin cambiar la estructura de tal algoritmo.


  • Visitor
Permite definir una operación nueva sin cambiar las clases de los elementos en los cuales opera.



Bibliografía (e imágenes tomadas de):

Lenguaje de programación Oz

Oz es un lenguaje multiparadigma diseñado para aplicaciones avanzadas, concurrentes, de redes, en tiempo real, y reactivas.

Provee de los componentes de un lenguaje orientado a objetos, incluyendo estado, tipos de datos abstractos, objetos, clases y herencia. Posee componentes de un lenguaje funcional, incluyendo sintaxis composicional, procedimientos o funciones de primera clase y ámbito léxico. Provee de los componentes de un lenguaje lógico y con restricciones, incluyendo variables lógicas, restricciones, construcciones disyuntivas, y mecanismos de búsqueda programables. Y permite a los usuarios la creación dinámica de cualquier nu'mero de threads o hilos de ejecución.

El sistema de programación mozart ha sido desarrollado por investigadores de DFKI (Deutsches Forschungszentrum Für Künstliche Intelligenz), SICS (Swedish Institute of Computer Science), University of the Saarland, UCL (the Université Catholique de Louvain).

Aquí hay un ejemplo de la sintaxis del lenguaje:

local X Y Z in
   X = 5 Y = 10
   if X >= Y then Z = X else Z = Y end
end

La sintaxis es separada por bloques de código en donde la primera parte es una declaración de variables (que pueden tomar valores regresados por funciones) y la segunda parte (separada por la palabra especial "in") consiste en una serie de procedimientos que se ejecutarán.

Este es mi programa de para calcular un elemento de la serie fibonacci y el factorial de algún número.


%Esto es un comentario

/**Esto es un 
comentario extendido
**/

local Fac Res in
   
proc {Fac N Res} %procedimiento
      if N < 2 then Res = 1
               else Res = N * {Fac N - 1} end
   end
   {Fac 5 Res%llama al procedimiento
       {Browse Res}
end

local Fib 
   fun {Fib N} %funcion
     case N %similar a switch case en C
     of 0 then 0
     [] 1 then 1
     else {Fib N - 1} + {Fib N - 2} end
   end
in
   
{Browse {Fib 7}} %llama a la funcion
end


Recursos para aprender:

UML (Unified Modeling Language)

UML es un lenguaje estandarizado de propósito general para el modelado de software. Fue creado y es mantenido por Object Managment Group.

Es usado para especificar, visualizar, modificar, construir y documentar elementos o componentes de un sistema de software orientado a objetos, ya sean procesos o valores.

En UML se pueden representar:


  • Actividades.
  • Actores.
  • Procesos de negocio.
  • Esquemas de bases de datos.
  • Componentes lógicos.
  • Componentes reusables.

UML tiene catorce tipos de diagramas, cada uno representando un elemento o componente en particular, se dividen en diagramas de estructura y diagramas de comportamiento.

Diagramas de estructura

Se refiere a los elementos que deben de estar presentes en el sistema.

Los diagramas de este tipo son:


  1. Diagrama de clases.
  2. Diagrama de componentes.
  3. Diagrama de objetos.
  4. Diagrama de estructura compuesta.
  5. Diagrama de despliegue.
  6. Diagrama de paquetes.
  7. Diagrama de perfil.
Diagramas de comportamiento

Le importa que es lo que tiene que suceder en el sistema.

Los diagramas de este tipo son:


  1. Diagrama de actividades.
  2. Diagrama de casos de uso.
  3. Diagrama de estados.

Diagramas de interacción

Pertenecen a los diagramas de comportamiento, éstos se 
comunican acerca del flujo de control y datos en el sistema.

Los diagramas de este tipo son:


  1. Diagrama de secuencia.
  2. Diagrama de comunicación.
  3. Diagrama de tiempos.
  4. Diagrama global de interacciones.

Ejemplo simple de UML.







Clase flightVehicule, subclase airplane hereda de flightVehicule.

Estándar IEEE 754-2008

El estándar IEEE 754-2008 son una serie de normas que establecen dos formatos básicos para representar a los números reales en una computadora digital: precisión simple y precisión doble. Ha sido definido por el Instituto de Ingenieros Eléctricos y Electrónicos (Institute of Electrical and Electronics Engineers, IEEE).

Para escribir un número real en precisión simple se usan 32 bits (4 bytes): 1 bit para el signo (s) del número (positivo o negativo), 23 bits para la mantisa (m) y 8 bits para el exponente (exp).

El exponente se suele representar en Exceso a $2^{n-1}-1$, mientras que, para la mantisa, normalmente se utiliza Signo Magnitud. Además, la mantisa se suele normalizar colocando la coma decimal a la derecha del bit más significativo.

Por ejemplo, si queremos escribir el número:

$101110,010101110100001111100001111100010011_2$

en el estándar IEEE 754 con precisión simple, exponente en Exceso a $2^{n-1}-1$ y mantisa en Signo Magnitud, primero hay que normalizarlo colocando la coma a la derecha del bit mas significativo:

$1,01110010101110100001111100001111100010011_2 \times 2^5$

El exponente, en Exceso a $2^{n-1} -1$, será:

$510 + (2^{8-1} - 1)_{10} = 510 + (2^7 - 1)_{10} = 510 + (128 - 1)_{10} = 10000100_2$

De la mantisa se cogen los bits 23 bits más significativos, se trunca:

$1,0111001010111000000111_2$

El resto de bits no se pueden representar, porque no caben en la mantisa. Sin embargo, cuando la mantisa se normaliza situando la coma decimal a la derecha del bit más significativo, dicho bit siempre vale 1. Por tanto, se puede prescindir de él, y coger en su lugar un bit más de la mantisa. De esta forma, la precisión del número representado es mayor. Así, los bits de la mantisa serán:

$01110010101110100001111_2$

Al bit omitido se le llama bit implícito. Por otra parte, el bit de signo vale 0, ya que, el número es positivo. En consecuencia, el número se puede representar como:

0     10000100     01110010101110100001111
s         exp                        mantisa


En precisión doble, al escribir un número real se emplean 64 bits (8 bytes): 1 bit para el signo (s) del número, 52 bits para la mantisa (m) y 11 bits para el exponente (exp).

Si se quiere escribir el número 19,562510 en el estándar IEEE 754 con precisión doble, exponente en Exceso a $2^{n-1}-1$ y mantisa en Signo Magnitud con bit implícito, los pasos a seguir son:


1º) Cambiar 19,562510 a base 2. Parte entera y fraccionaria:

$19,5625_{10} = 10011,1001_2$

2º) Normalizar el número binario obtenido, colocando la coma decimal a la derecha del bit más significativo:

$10011,1001_2 = 1,00111001 \times 2^4$

3º) Escribir el exponente en Exceso a $2^{n-1}-1$:

$4_{10} + (2^{11-1} - 1)_{10} = 4_{10} + (2^{10} - 1)_{10} = 4_{10} + (1024 - 1)_{10} $

$= 4_{10} + 1023_{10} = 1027_{10} = 10000000011$

4º) Establecer la mantisa utilizando bit implícito. Para ello, se cogen los ocho bits que están a la derecha de la coma (00111001) y el resto de la mantisa se rellena con ceros:

$0011100100000000000000000000000000000000000000000000$

5º) Expresar el número en el estándar IEEE 754 con precisión doble. En este caso, hay que tener en cuenta que el bit de signo vale 0, ya que, el número es positivo:

0     10000000011     0011100100000000000000000000000000000000000000000000
s            exp                                                  mantisa


Bibliografia:


Smalltalk

Smalltalk es un lenguaje de programación orientado a objetos puro, de tipado dinámico y reflectivo y con recolector de basura, creado por Alan Kay de Xerox PARC y otros durante los 1970.

Interactúa entre objetos mediante el envío de mensajes Es multiplataforma y puede compilar en tiempo de ejecución o interpretado. Smalltalk tuvo gran influencia en la creación de otros lenguajes como Java o Ruby.

Todo en smalltalk es un objeto que puede hacer 3 cosas:
  • Mantener un estado.
  • Recibir mensajes de si mismo o de otros objetos.
  • En el curso de procesar un mensaje, enviarse un mensaje el mismo o a los demás objetos.
Características de los objetos en smalltalk:
  • Tienen una memoria propia.
  • Poseen capacidad para comunicarse con otros objetos.
  • Poseen la capacidad de heredar características de objetos ancestros.
  • Tienen capacidad de procesamiento.
Para instalar gnu-smalltalk en ubuntu:

sudo apt-get install gnu-smalltalk

Para iniciar solo escribimos gst:

gst

Aquí esta un ejemplo que hice de una cuenta a la cual se le puede depositar y retirar cierta cantidad.

"cuenta.st"

Object subclass: Cuenta [
       saldo := 0.

       deposita: cantidad [
       saldo := saldo + cantidad.
       ^true
       ]

       retira: dinero [
       (saldo > dinero) ifTrue:
        [saldo := saldo - dinero.
        ^true] ifFalse: [^false]
       ]

       saldo [
       ^saldo printNl.
       ]
]

c := Cuenta new.
c deposita: 2000.
c retira: 500.
c saldo.
c retira: 700.
c saldo.

Recursos para aprender:

miércoles, 24 de noviembre de 2010

Punteros, ejemplos en lenguaje C

Entrada para puntos extras.

Lo punteros son variables que apuntan a otras variables, es decir, que guardan la dirección de la ubicación en memoria de otras variables. La dirección es representada con un numero por lo general en base hexadecimal, ejemplo: 0x4F8G23A.

Ahora veremos ejemplos de como manejar punteros en lenguaje C.

La forma de declarar un puntero es: tipo* puntero;

#include <stdio.h>

int main(int inicial, char** args){
  int* puntero;
  double* otro_puntero;
  char* char_puntero;
  int* arreglo[100];
  return 9;
}

Hay que ver el signo asterisco (*) llamado indirección antes del nombre del puntero, este designa que es propiamente un puntero y no una variable convencional. Aquí solamente le decimos al compilador que reserve una posición en la memoria para nuestro puntero.

Antes de usar el puntero tenemos que iniciarlo, para eso le pediremos que guarde la dirección (representada con el signo amperson &) de alguna variable.

tipo* puntero;
tipo variable;
puntero = &variable;

Para cambiar el contenido de la dirección a la que apunta el puntero, utilizamos el operador de indirección. Esto hace que cambie el valor al que hace referencia y no la dirección del mismo puntero.

*puntero = nuevo_valor;

Un ejemplo completo

#include <stdio.h>

int main(int a, char** args){
  int* p; //puntero                                                            
  int m[100];

  p = &m[0]; //& direccion                                                      
  m[0] = 4;
  printf("%d\n", *p); //* indireccion                                           

  m[0] = 883;
  printf("%d\n", *p); //cambia dinamicamente, la direccion es fija              

  *p = 999; //accediendo al valor                                                printf("%d\n", *p);
  return 24;
}

La relación estrecha existente entre arreglos y punteros se debe a que el nombre de un arreglo es equivalente a la dirección del primer elemento (&arreglo[0]), no es necesario utilizar el operador de dirección para apuntar al primer elemento de un arreglo.

int* p; //puntero                                                              int m[100];

p = &m[0]; //& direccion                                                       
m[0] = 993;
printf("%d\n", *p);

p = m;
printf("%d\n", *p);

Si a un puntero se le suma una cantidad, cambia la dirección esa misma cantidad mas no el valor al que apunta. Es equivalente a hacer:

int* p; //puntero
p++;
p--;

Debido a la precedencia de operadores, la expresión:

variable = *(puntero++);

significa asignar el contenido de lo apuntado, a la variable, y después incrementar la dirección un lugar, y la expresión

variable = *(++puntero);

significa incrementar la dirección del puntero y después asignarle el contenido de lo apuntado, a la variable.

Si declaramos una variable en C, el espacio se reserva durante todo el tiempo de ejecución aun cuando solo lo necesitemos para una pequeña fracción del programa, para evitar esto podemos almacenar el arreglo en la zona de amontonamiento ó Heap.

double* puntero;

esta declaración no crea un espacio para almacenar la varible, solamente una dirección para después almacenarla, ahora tenemos que designar la cantidad de espacio que deseamos, para ello utilizamos una función de la librería stdlib dedicada a la tarea, entre ellas esta la mas común: malloc

puntero = malloc(8);

malloc recibe la cantidad de bytes que se desea reservar y nos devuelve un puntero o dirección a la primera posición de la "pila" reservada en el Heap, si no se puede crear el espacio, devuelve un puntero a NULL.

Utilizando la función sizeof podemos designar el tamaño de cada variable.

puntero = malloc(sizeof(double));

Si queremos guardar en el Heap alguna operación solamente haríamos algo como

*puntero = operacion;

Y como y habíamos visto para recuperar la variable almacenada

var = *puntero;

Como una cadena no es mas que un arreglo que almacena caracteres, podemos guardar cadenas en punteros

char* puntero;
puntero = "Hello World"

Así *puntero contendría el primer elemento de la cadena (H) y si necesitamos el segundo solamaente escribimos *(puntero + 1).

De la misma manera que podemos declarar arreglos, podemos declarar arreglos de punteros

char* arreglo[] = {"frase1", "frase2", "frase3"};

Con *arreglo podemos acceder a la primera frase, con *(arreglo + 1) a la segunda frase, etc.

Para crear estructuras y asignar valores a los miembros hacemos:

typedef struct estudiantes{
  int a;
  double b;
  char c[10];
}estudiante;

estudiante* yo;
yo = (estudiante*) malloc(sizeof(estudiante));
yo -> a = 234;
yo -> c = 'H';
yo -> (c + 1) = 'e';

Nota que antes de inicializar un elemento de la estructura es necesario alojarla en la memoria con la ayuda de malloc.

Cuando pasamos una estructura a una función, se copian todos los elementos de la estructura, para resolverlo pasamos un puntero que apunta a la estructura en lugar de toda la estructura.

struct nombre{
  int datos;
}*puntero;

funcion(struct nombre *puntero);

Una función que regresa un puntero se escribe

tipo *funcion(struct nombre *puntero);

Bibliografía

jueves, 18 de noviembre de 2010

Lenguajes lógicos

El problema que elegí es Knights and Knaves 2.
There are three people (Alex, Brook and Cody), one of whom is a knight, one a knave, and one a spy. 
The knight always tells the truth, the knave always lies, and the spy can either lie or tell the truth. 
They are brought before a judge who wants to identify the spy. 
Alex says: "I am not a spy." 
Brook says: "I am a spy." 
Now Cody is in fact the spy. The judge asks him: "Is Brook really a spy?" 
Can Cody give an answer so that he doesn't convict himself as a spy?

Cosas que sabemos:
  • Uno siempre miente.
  • Otro siempre dice la verdad.
  • El restante miente o dice la verdad.
Tendremos que analizar dos situaciones, una en la que Cody responde si y otra en el que responde no, además de revisar cada combinación posible de lo que podría ser cada uno de los sospechosos.

Suponemos primero que Alex es un Knave, miente y lo que dice debe ser lo opuesto, "I am a spy", pero como no puede ser dos cosas al mismo tiempo, es imposible que sea Knave.

Ahora hay menos cosas que probar, en el primer caso suponemos que Alex es Knight y concuerda con lo que dice, y que Brook es Knave y también concuerda con lo que dice, es decir, no se contradicen, Cody es espía, ya no importa lo que diga Cody.

Si Cody responde al juez que Brook es un espía se presentan los siguientes acontecimientos, el juez puede pensar que efectivamente Brook es espía pero Cody estaría diciendo la verdad y lo convertiría en Knight y como Alex no puede ser Knave es imposible catalogarlos de esta manera.

En el segundo caso el juez puede pensar que Alex es el espía por lo que Cody miente al decir que Brook es espía, entonces Cody es Knave.

Tercer caso, el juez podría pensar que Cody mismo es el espía, y Alex podría ser Knight mientras Brook no se contradice quedándose como Knave, otra vez Cody se queda atrás.

De otra manera si Cody responde que Brook no es espía se presentan los siguientes casos.

El juez sigue pensando, si Alex es espía Cody dice la verdad al decir que Brooks no es espía y Brook queda como Knave y no se contradice, si Brook es espía, Cody sería Knave ya que mintió y Alex está otra vez como Knight. Ahora Alex o Brook pueden ser espía.

Cody dirá que  Brook no es espía.

Y Cody se ha salvado?

Lenguajes funcionales

 Esto es el producto de actividad de la semana 7: 2 programas en lenguajes funcionales distintos.


Haskell

Como agregar el resaltador de código haskell a emacs:

sudo apt-get install haskell-mode

Más información:

Haskell es un lenguaje hermoso, o mejor dicho "a polymorphically statically typed, lazy, purely functional language" como lo definen en la pagina oficial, es decir, un lenguaje polimorfo, fuertemente tipado, "lazy", y puramente funcional. Se le llama haskell como referencia a Haskell Brooks Curry, matemático cuyo trabajo en la lógica sentó las bases de la programación funcional.

Haskell B. Curry

Foundations of mathematical logic


Debido a su fundamento en las matemáticas y por lo tanto en conjuntos y listas, los lenguajes funcionales tienden a ser mas concisos que los imperativos y se logra hacer cosas de mayor tamaño con menor cantidad de código, por ejemplo un ordenamiento quicksort en dos líneas:


qsort []     = []
qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs)


Lo anterior se lee fácilmente como: El ordenamiento de una lista vacía ([]) es una lista vacía y el ordenamiento de una lista donde el primer elemento es x y el resto es xs es la concatenación (++) de los siguientes objetos:

  • Los elementos ordenados menores que x en xs.
  • El elemento x.
  • Los elementos ordenados mayores que x en xs.

Las cosas buenas en la mayoría de los lenguajes funcionales como haskell, lisp, erlang, scheme, ml, etc, es que al programar te concentras mas en el que quieres y no en el como lo haces, siendo una de las consecuencias una menor cantidad de errores.

Aquí están mis ejemplos de factorial de un numero y serie fibonacci respectivamente.


fac 0 = 1
fac n = n * fac (n - 1)


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


Se pueden ver y entender fácilmente 3 cosas, la declaración de funciones, la llamada recursiva, y la evaluación iterativa de los parámetros que toman las funciones (de arriba a abajo).

Otra alternativa aun mas simple para el factorial (iterativo) es:


fac n = product [2..n]


Donde product es una función que toma una lista y multiplica todos sus elementos y la lista a la que nos referimos es esa que va del 2 al n. Podemos ver los tipos de variables y tipos de parámetros de funciones con el comando :t, en nuestro método recursivo se vería como:


Prelude> :t fac
fac :: (Num a) => a -> a


Que se lee como: La variable "a" es un tipo Num, la función fac toma como parámetro una variable del mismo tipo que "a" y regresa una variable del mismo tipo que "a".

Otra ventaja de haskell es que permite el cómputo en paralelo.

Pueden optar por instalar ghci ó hugs (solo intérprete):

sudo apt-get install ghc hugs

Para compilar:

ghc helloWorld.hs

Éste es mi intento de RSA. No puedo manejar bien el tipo de variable IO Integer que me devuelve Random.
    import Random
    
    rand min max = randomRIO(min, max) --regresa IO Integer
    
    
    rande phi min max = if (gcd e phi == 1) && (e < phi) then e else rande phi min max
        where e = rand min max
    --gcd(e, phi) == 1
    
    randp a b | prime num = num
              | otherwise = randp a b
              where num = rand a b
    
    prime n | list == [] = True
            | list == [n] = True
            | otherwise = False
            where list = [x | x <- 2:[3,5..round (sqrt (fromIntegral n))], n `mod` x == 0]
    
    extGcd a b (c, d) (e, f)
        | (a > b) && r1 == 0 = (e, f)
        | (a > b) && r1 /= 0 = extGcd (a - (q1 * b)) b (c - (q1 * e), d - (q1 * f)) (e, f)
        | (a <= b) && r2 == 0 = (c, d)
        | (a <= b) && r2 /= 0 = extGcd a (b - (q2 * a)) (c, d) (e - (q2 * c), f - (q2 * d))
        where
          r1 = a `mod` b
          r2 = b `mod` a
          q1 = a `div` b
          q2 = b `div` a
                 
    egcd a b = extGcd a b (1, 0) (0, 1)
    
    getPvte x y = fst (egcd x y)
    
    cifrar m e n = (m ^ e) `mod` n
    decifrar c d n = (c ^ d) `mod` n
    
    primos = do let min = 2
                    max = 10000
                p <- randp min max
                q <- randp min max
                let 
                    n = (p * q)
                    phi = (p - 1) * (q - 1)
                e <- rande phi min max
                d <- getPvte e phi
                let
                    mensaje = 27436
                c <- cifrar mensaje e n
                print c
                d <- decifrar c d n
                print d
       
    

    Tutoriales RSA:

    Aprender haskell, lo mas recomendable es que lean el inicio rápido de la página oficial. Seguido de los tutoriales:

    Scheme

    Lenguaje funcional impuro y un dialecto principal de lisp, desarrollado en el laboratorio de IA del MIT por Guy L. Steele y Gerald Jay Sussman.

    Su simple sintáxis se basa en las expresiones S, una listas encerradas en paréntesis donde el operador va seguido de sus argumentos. Scheme es poderoso para manejar listas debido a su herencia por parte de Lisp con primitivas como cons, car y cdr.

    Aquí esta mi ejemplo de quicksort que se parece al de haskell (concatena listas filtradas).

    (define (qsort lista)
      (if (> (length lista) 1)
          (append
           (qsort (filter (lambda (x) (< x (car lista))) (cdr lista)))
           (cons (car lista) '())
           (qsort (filter (lambda (x) (> x (car lista))) (cdr lista)))
           ) lista 
      )
    )