lunes, 28 de noviembre de 2011

Curso: Redes Neuronales en DrRacket

Redes Neuronales Artificiales
Version: 5.2

Redes Neuronales Artificiales

Abdiel E. Cáceres González <abdielc@acm.org>

1 Presentación

Este es el documento que acompaña el curso de Redes Neuronales Artificiales con un enfoque orientado a objetos, utilizando el lenguaje de programación DrRacket 5+.

Este curso está dirigido a estudiantes de Ciencias Computacionales o equivalente, y en lo que se refiere al enfoque, este es ligeramente diferente al enfoque convencional. Aquí todas las operaciones que se llevan a cabo, las realizan las mismas neuronas artificiales; incluyendo la actualización de los pesos, que típicamente es una operación matricial.

Como es claro que no trataremos con neuronas biológicas, sino que en todos los casos nos referiremos a las neuronas artificiales, cuando se encuentre la palabra neurona, nos referiremos a las neuronas artificiales, excepto cuando claramente se especifique otra cosa.

En un intento por proporcionar un rápido acercamiento al tema, utilizaremos el lenguaje de programación DrRacket, que permite concentrar los mayores esfuerzos en el aprendizaje del tema en cuestión (redes neuronales artificiales), que en el aprendizaje del propio lenguaje. Espero que sea tan fácil el aprendizaje del lenguaje, que sólo en ocasiones y mayormente en el principio del curso, hagámos referencia a las características propias del lenguaje de programación.

Sin embargo, para tener un propio acercamiento al lenguaje de programación, los invito a referirse a los manuales correspondientes, o a este buen libro de introducción a la programación (en inglés) How to Design Programs: An Introduction to Computing and Programming , o bien, este otro excelente libro escrito por Harold Abelson, Gerald J. Sussman con Julie Sussman Structure and Interpretation of Computer Programs, del cual haremos algunos ejemplos de sus útiles enseñanzas.

2 Introducción al lenguaje Racket

Abdiel E. Cáceres González <abdielc@acm.org>

    2.1 Los elementos del lenguaje

      2.1.1 Expresiones primitivas

      2.1.2 Maneras de combinar las expresiones primitivas

      2.1.3 Medios de abstracción

    2.2 DrRacket es multiparadigma

      2.2.1 Programación secuencial

      2.2.2 Estructuras de datos en Racket

      2.2.3 Objetos

    2.3 Las listas son la materia prima

      2.3.1 car y cdr

      2.3.2 cons para construir listas

        2.3.2.1 Otros medios de construir listas

    2.4 El primer mandamiento

2.1 Los elementos del lenguaje

En esta sección presentaré una breve introducción al lenguaje de programación DrRacket y a las redes neuronales artificiales. No es la intención profundizar en estos campos de estudio, ya que cada uno de ellos, por su parte, merece dedicar suficiente tiempo para su estudio y análisis. El objetivo entonces es presentar los temas y justificar las herramientas. Si usted ya conoce estos elementos, puede pasar a la siguiente sección que proporciona una breve introducción (part "Introducción-RNA")

Prácticamente son sólo tres los elementos del lenguaje:
  1. Expresiones primitivas, que son los elementos más básicos del lenguaje.

  2. Maneras de combinar las expresiones primitivas, que son los medios para construir elementos compuestos a partir de otros más simples.

  3. Medios de abstracción, que es la manera en que podemos nombrar las construcciones realizadas, para poder construir nuevas o manipularlas.

2.1.1 Expresiones primitivas

En Racket así como en cualquier otro lenguaje de programación, llamamos una expresión primitiva a una expresión válida en el lenguaje de programación que no requiere evaluación, como en los siguiente ejemplos:

> 10

10

Es decir, los números no requieren evaluación. Los números son datos primitivos.

> "Hola mundo"

"Hola mundo"

Las cadenas de caracteres, encerradas entre comillas, también son datos primitivos.

> #f

#f

Algunas constantes como las constantes booleanas de falso y verdadero, son datos primitivos.

Una expresión en Racket la llamaremos R-expresión. Y es o bien un dato primitivo, o una cadena de símbolos construida con paréntesis, operador y operandos del siguiente modo:

(operador [operando ... ])

Donde la lista de operandos [operando ...] puede tener 0 o más dependiendo claro, de la operación. Como ejemplos de R-expresiones tenemos

> 20

20

> (+ 10 10)

20

> (* 2 5 2)

20

El primer ejemplo es una R-expresión compuesta por un dato primitivo, mientras que en el segundo ejemplo tenemos una R-expresión compuesta por una lista de elementos, el primero de esos elementos es un operador (el +) y el resto de los elementos son sus operandos.

Así como hay datos primitivos, también hay operadores primitivos, como la suma, la multiplicación, división, etc., pero con un poco más de experiencia, notaremos que son la misma cosa, los datos y las operaciones se tratan como iguales.

2.1.2 Maneras de combinar las expresiones primitivas

En Racket, es posible combinar expresiones primitivas para obtener expresiones compuestas que posteriormente podemos combinar para obtener otras expresiones aún más complejas.

La siguiente secuencia de ejemplos puede ilustrar el modo de hacer las combinaciones.

> 100

100

> (+ 50 50)

100

> (+ (* 25 2) 50)

100

> (+ (* 25 2) (/ 200 4))

100

> (+ (* 25 2) (/ 200 (* 2 2)))

100

2.1.3 Medios de abstracción

El modo más sencillo de hacer abstracciones en Racket es mediante la palabra reservada define.

Como en cualquier diccionario, una definición, es decir, una entrada del diccionario, se crea al asociar un nombre con un concepto. Un programa en Racket es como un diccionario, donde se dan definiciones partiendo de aquellas más básicas y luego utilizando las mismas definiciones para generar nuevas y tener un conocimiento mayor, lo que se traduce como el poder hacer algo.

El siguiente ejemplo ilustra cómo podemos crear las definiciones necesarias para obtener la suma de dos números cuadrados (con una aplicaciones en la oprimización matemática mediante análisis numérico, en estadística tiene propiedades en la media aritmética, teoría de números y muchas otras mas).

; ==========================================================
; Contrato: cuadrado : numero -> numero
 
; Propósito: Calcular el cuadrado de un número <n>
 
; Ejemplo: (cuadrado 10) debe devolver 100
 
; Definición: El cuadrado de un número <n> es el producto de ese número <n> por sí mismo
 
(define cuadrado
  (λ (n)
    (* n n)))
 
; ==========================================================   
; Contrato: areaCirculo : numero -> numero
 
; Propósito: Calcular el área de un círculo dado su diámetro <d>
 
; Ejemplo: (areaCirculo 10) debe devolver 78.53975
 
; Definición: El área de un círculo con diámetro <D> es la medida de la superficie limitada por la circunferencia del círculo dado. Basados en el diámetro de la circunferencia, el área del círculo se obtiene al obtener un cuarto del producto de la constante pi por el cuadrado del diámetro.
 
(define areaCirculo
  (λ (D)
    (/ (* 3.14159 (cuadrado D))
          4)))
; ==========================================================

En los ejemplos anteriores, aquellos que empiezan con define, son entradas al diccionario, y asocian un concepto a un nombre. Las entradas al diccionario pueden ser de datos o de procedimientos; se pueden definir datos como en el caso de (define d1 3) donde se asocia el dato primitivo 3 con el nombre d1, lo que significa que a partir de esta definición se conocerá que d1 significa 3 y en cada ocurrencia del nombre d1, se debe sustituir por su significado, es decir 3.

Además de asignarle un nombre a los datos, también se puede asignar un nombre a los procedimientos, como los ejemplos de cuadrado y areaCirculo. En el primero de estos ejemplos, el de cuadrado, se ha asociado este nombre al procedimiento (λ (n) (* n n)), que puede leerse como el procedimiento que para algún número n, se multiplica ese número n, por sí mismo; este procedimiento desde ahora en adelante lo vamos a conocer como cuadrado.

En el entorno de desarrollo integrado (IDE) de Racket, es posible escribir λ o lambda con el mismo resultado. La lista que aparece enseguida de λ es la lista de parámetros del procedimiento. Si el procedimiento no requiere parámetros, entonces se debe poner una lista vacía (λ () ...), pero en el caso del procedimiento cuadrado, se ha definido un parámetro, por lo que en el momento de ser invocada esta función, se debe declarar también un argumento.

2.2 DrRacket es multiparadigma

DrRacket permite en el desarrollo de la programación, utilizar diferentes paradigmas o estilos de programación. DrRacket es descendiente directo de DrScheme, que es un descendiente de Scheme y éste a su vez, de Lisp. Al paso de los años y de las versiones revisadas, se han ido agregando diferentes capacidades que permiten al lenguaje utilizar el estilo de programación que se requiera, sin que para ello se tenga que sacrificar la filosofía minimalista que es el espíritu de su existencia.

2.2.1 Programación secuencial

para entender la manera de programar en DrRacket, empezaremos con el siguiente problema: "Se desea saber el área de un anillo, que tiene un diámetro exterior D y un diámetro interior d". La figure 1 muestra un anillo, y el área que se desea obtener está dibujada en color verde.

Figure 1: Problema: Calcular el área de un anillo con diámetro exterior D y diámetro interior d.

Aunque este es un ejemplo sencillo, será útil para demostrar los tres principales paradigmas de la programación.

Mediante un análisis sencillo podemos concluir que el área del anillo queda bien determinado al restar el área del círculo con diámetro d, del área del círculo con diámetro D. Siendo

; ==========================================================         
; Contrato: areaAnillo-v01 : numero numero -> numero
 
; Propósito: Calcular el área de un anillo cuyo diámetro exterior es <D> y el diámetro interior es <d>
 
; Ejemplo: (areaAnillo-v01 10 8)
 
; Definición: El área de un anillo con diámetro exterior <D> y diámetro interior <d> está dado por la diferencia entre el área del círculo determinada por el diámetro exterior, menos el área del círculo determinada por el diámetro interior.
 
(define areaAnillo-v01
  (λ (D d)
    (- (/ (* 3.14159 D D)
          4)
       (/ (* 3.14159 d d)
          4))))
; ==========================================================

(areaAnillo-v01 10 8)

>28.27431

Aunque esta definición es correcta, sería mejor utilizar definiciones auxiliares y comentarios, con el fin de que el lenguaje de programación sirva además de herramienta de cálculo, de un medio formal de expresión.

Veámos el siguiente código.

; ==========================================================         
; Contrato: creaAnillo : numero numero -> void
 
; Propósito: Crear un anillo cuyo diámetro exterior es <D> y el diámetro interior es <d>
 
; Ejemplo: (creaAnillo 10 8)
 
; Definición: Un anillo es una figura geométrica en forma de círculo con diámetro exterior <D>, pero que tiene un hueco concéntrico de diámetro menor y que determina el diámetro interior <d>.
 
(define creaAnillo
  (λ (D d)
    (list D d)))
 
; ==========================================================
; Contrato: diametroExterior : anillo -> numero
 
; Propósito: Obtener el diámetro exterior de un anillo.
 
; Ejemplo: (diametroExterior unAnillo) suponiendo la definicion previa de unAnillo, se obtiene su diámetro exterior.
 
; Definición:
 
(define diametroExterior
  (λ (A)
    (car A)))
 
; ==========================================================
; Contrato: diametroInterior : anillo -> numero
 
; Propósito: Obtener el diámetro interior de un anillo.
 
; Ejemplo: (diametroInterior unAnillo) suponiendo la definicion previa de unAnillo, se obtiene su diámetro interior.
 
; Definición:
 
(define diametroInterior
  (λ (A)
    (cadr A)))
 
; ==========================================================
; Contrato: areaAnillo-v02 : numero numero -> numero
 
; Propósito: Calcular el área de un anillo cuyo diámetro exterior es <D> y el diámetro interior es <d>
 
; Ejemplo: (areaAnillo-v02 10 8) debe devolver 28.27431
 
; Definición: El área de un anillo con diámetro exterior <D> y diámetro interior <d> está dado por la diferencia entre el área del círculo determinada por el diámetro exterior, menos el área del círculo determinada por el diámetro interior.
 
 
(define areaAnillo-v02
  (λ (A)
    (- (areaCirculo (diametroExterior A))
       (areaCirculo (diametroInterior A)))))
; ==========================================================

> (define A01 (creaAnillo 10 8))

> (areaAnillo-v02 A01)

28.27431

>

Este código tiene dos ventajas sobre el primer ejemplo "(areaAnillo-v01 D d)":

  1. El código es más legible. Esto es de gran ayuda a la hora de corregir, actualizar o simplemente modificar el código. Poner comentarios ayuda en primer lugar al programador y en segundo lugar a las personas que lean el código. Aunque se requiere más tiempo para escribir, se requiere de mucho más tiempo para encontrar errores en un código no documentado.

  2. Se crean barreras de abstracción. El utilizar funciones auxiliares, permite una comprensión más rápida del problema, escondiendo las operaciones primitivas detrás de esas barreras. Las barreras de abstracción también ayudan a aligerar el mantenimiento del código; y las definiciones en texto se apegan mucho mejor a las definiciones en el lenguaje.

2.2.2 Estructuras de datos en Racket

Racket es un lenguaje de programación multiparadigma, esto significa que en un mismo programa se puede utilizar la programación secuencial, orientada a estructuras de datos y orientada a objetos; es posble utilizar definiciones estáticas o dinámicas de tipos.

Aunque la programación secuencial ya ha sido ejemplificada, consideraremos como ejemplo, el mismo problema de determinar el área de una figura plana en forma de anillo, conociendo su diámetro exterior y su diámetro interior (ver la figure 1).

La manera más simple de crear una estructura de datos en con la palabra reservada struct, y se utiliza casi del mismo modo que las definciones.

; ==========================================================         
; Contrato: anillo : numero numero -> #<anillo>
 
; Propósito: Crear una estructura de datos de tipo anillo, con la información de sus diámetros, respectivamente el diámetro exterior es <D> y el diámetro interior es <d>
 
; Ejemplo: (anillo 10 8)
 
; Definición: Un anillo es una figura geométrica en forma de círculo con diámetro exterior <D>, pero que tiene un hueco concéntrico de diámetro menor y que determina el diámetro interior <d>.
 
(struct anillo
  (D d) #:mutable)
; ==========================================================

La estructura anillo tiene dos campos D y d, referentes al diámtero exterior e interior respectivamente. Para crear instancias de tipo anillo se deben definir símbolos y asociarlos con una estructura.

>(define A (anillo 10 8))

>A

#<anillo>

Así para calcular el área del anillo, utilizamos los accesores definidos para las estructuras:

; ==========================================================         
; Contrato: areaAnillo-v03 : anillo -> numero
 
; Propósito: Calcular el área de un anillo
 
; Ejemplo: (areaAnillo-v03 A) debe devolver 28.27431
 
; Definición: El área de un anillo con diámetro exterior <D> y diámetro interior <d> está dado por la diferencia entre el área del círculo determinada por el diámetro exterior, menos el área del círculo determinada por el diámetro interior.
 
(define areaAnillo-v03
  (λ (A)
    (- (areaCirculo (anillo-D A))
       (areaCirculo (anillo-d A)))))
; ==========================================================

>(areaAnillo-v03 A)

28.27431

2.2.3 Objetos

El nivel de abstracción puede manipularse a antojo en el lenguaje de programación DrRacket. En este apartado modelaremos el mismo anillo, pero desde una perspectiva orientada a objetos.

; ==========================================================
; ======== INICIO DE LA CLASE
; Clase: anillo%
 
; Proporciona: Método para calcular el área de un anillo
 
; Ejemplo: (send ID area) debe devolver 28.27431
 
; Definiciónes: El área de un anillo con diámetro exterior <D> y diámetro interior <d> está dado por la diferencia entre el área del círculo determinada por el diámetro exterior, menos el área del círculo determinada por el diámetro interior.
 
(define anillo%
  (class object%
    (init D) ; Valor de inicialización para el diámetro externo
    (init d) ; Valor de inicialización para el diámetro interno
    (define diametro-externo D) ; Atributo inicializado por init
    (define diametro-interno d) ; Atributo inicializado por init
    (define color 'negro) ; Atributo inicializado en la clase
    (super-new) ; inicialización de la superclase
    (define/public area ; método público
      (λ ()
        (- (areaCirculo diametro-externo) ; se usan atributos
           (areaCirculo diametro-interno)))) ; de objetos de la clase
    (define/public cuadrado ; método publico
      (λ (n) (* n n)))
    (define/private areaCirculo ; método privado
      (λ (diametro)
        (/ (* 3.14159 (cuadrado diametro))
           4)))))
 
; ======== FIN DE LA CLASE
; ==========================================================

> (define A (new anillo% [D 10] [d 8]))

> A

(object:anillo% ...)

> (send A area)

28.27431

>

Como una mera convención, las clases en DrRacket se identifican por el símbolo % que aparece al final del nombre, este símbolo no agrega ninguna funcionalidad, es simplemente una marca sintáctica que permite al programador, identificar una clase.

En DrRacket, los atributos declarados en las clases deben estar inicializados, ya sea mediante valores pasados en la creación de instancias (mediante init ) o bien en la misma definición de los atributos, como en el caso de color que se ha inicializado con el valor 'negro.

Las principales declaraciones de métodos son públicos o privados (de hecho hay una amplia variedad de tipos de métodos como public, override, augment, pubment, overment, augride, public-final, override-final, augment-final, y private; para una mas profunda explicación de cada uno de ellos, por favor refiérase al manual de referencia en la sección 5.2.3.1 Method Definitions La interacción con objetos se hace mediante el paso de mensajes, pasar un mensaje a un objeto indica al objeto que debe invoar alguno de sus métodos. El paso de mensajes se hace con la directiva send, como en el ejemplo:

(send A area)

Esto indica el envío de una orden al objeto identificado por A, indicándole que debe invocar su método area. Si hubiera la necesidad de pasar argumentos, estos se indican en seguida del nombre dejando un espacio después de cada argumento; en la misma clase, se puede pasar un mensaje al mismo objeto para invocar el método cuadrado, y con el argumento 10 del siguiente modo:

> (send A cuadrado 10)

100

>

En este curso tendremos una perspectiva de las redes neuronales artificiales desde el punto de vista orientado a objetos.

2.3 Las listas son la materia prima

Como descendiente de Lisp, DrRacket es un sistema procesador de listas, se utilizan las listas para definir los elementos del lenguaje y de hacer cualquier operación.

La definición de las listas es muy interesante, porque desde su propia definición, se puede observar el espíritu recursivo.

Definición: (Lista)

   Una lista puede ser la lista vacía, si no tiene elementos; en cuyo caso de denota por '().

   Pero en caso de que sí tenga elementos, una lista está compuesta por un primer elemento llamado el 'car de la lista' denotada por (car id), y por una lista llamada el 'cdr de la lista (se pronuncia couder)' que contiene el resto de los elementos, denotada por (cdr id)

En estos casos id representa un identificador para una lista.

Como ejemplos, podemos mostrar las siguientes listas:

  • '() – La lista vacía

  • (+ 5 6) – La lista que denota una operación. La operación de sumar los números 5 y 6.

  • (cons 'a '(s d f)) – también denota una operación. La operación de agregar el elemento 'a a la lista '(s d f)

  • '(+ 5 6) – Esta lista no denota ninguna operación. Es la lista de tres símbolos.

Como regla general, una lista es un elemento del lenguaje de programación que empieza con el símbolo "(" y termina con el símbolo ")". Cuando la lista está precedida por quote o el símbolo que lo representa como en el caso de la lista vacía '(), o bien la lista '(s d f), o la lista '(+ 5 6), representan listas de datos.

En caso contrario, cuando la lista no está precedida por el quote, entonces es una lista que se trata de una operación, como en el caso de (+ 5 6), que indica que se debe evaluar la suma del número 5 con el número 6.

> '()

'()

> (+ 5 6)

11

> (cons 'a '(s d f))

'(a s d f)

> '(+ 5 6)

'(+ 5 6)

Cuando la lista no inicia con el modificador quote (el apóstrofe ’), entonces la lista debe ser una operación, donde el primer símbolo es una operación, y los siguientes símbolos son sus operandos, así por ejemplo en la lista

(+ (* 3 4) (/ 4 2))

Como la lista no inicia con el apóstrofe, entonces se trata de una operación y se espera que el primer símbolo indique el procedimiento que se debe realizar:

> +

#<procedure:+>

En la invocación de una función, después del símbolo que identifica la función a realizar, se deben colocar todos sus argumentos, la cantidad de argumentos de la funión está determinada por la aridad de la función declarada en su definición. En Racket es posible determinar la aridad de un procedimiento invocando (arity-at-least-value (procedure-arity id))

> (arity-at-least-value (procedure-arity +))

0

En el caso de la suma, se requiere al menos de ningún operando, esto es que la suma funciona aún sin operandos:

> (+)

0

> (+ 1)

1

> (+ 1 2)

3

> (+ 1 2 3)

6

De vuelta al ejemplo, (+ (* 3 4) (/ 4 2)) indica la evaluación de una suma con dos operandos, el primero es la evaluación de una multiplicación y el segundo es la evaluación de una división.

2.3.1 car y cdr

Cuando una lista no es una lista vacía, entonces la lista se compone de dos elementos fundamentales:

  • (car id) – El primer elemento de una lista.

  • (cdr id) – La lista que contiene al resto de los elementos de la lista.

Los siguientes ejemplos ilustran claramente las evaluaciones del car y cdr

> (car '())

car: expects argument of type <pair>; given '()

> (cdr '())

cdr: expects argument of type <pair>; given '()

Porque ambos car y cdr funcionan con listas no vacías. Los siguientes ejemplos muestran las reacciones cuando se proporciona una lista no vacía.

> (car '(a))

'a

> (cdr '(a))

'()

y este otro ejemplo,

> (car '(a b))

'a

> (cdr '(a b))

'(b)

Notamos entonces que la lista pasada como argumento es la construcción del car y el cdr.

2.3.2 cons para construir listas

Para construir listas tenemos algunas primitivas, la principal de ellas es la primitiva cons. cons recibe un car y una lista cdr, y agrega ese car por la izquierda a la lista cdr.

> (cons 'a '(b c d))

'(a b c d)

> (cons '() '(b c d))

'(() b c d)

> (cons '(a) '(b c d))

'((a) b c d)

Es decir, cons toma su primer argumento y lo inserta al inicio de la lista que es identificada por el segundo argumento, no importa si es o no una lista.

Notemos que la lista '(a b c d) es el resultado de construir recursivamente:

> (cons 'a (cons 'b (cons 'c (cons 'd '()))))

'(a b c d)

2.3.2.1 Otros medios de construir listas

Además de cons para construir listas, tenemos otro par de primitivas para construir listas, dependiendo de los insumos:

  • list. Requiere una lista de elementos. Construye una lista con todos los argumentos.

  • append. Requiere dos listas. Construye una lista agregando los elementos de una lista al frente de los elementos de la lista pasada como segundo argumento

En los siguientes ejemplos se ilustra el uso de la primitiva list
> (list 'a 'b 'c)

'(a b c)

> (list 'a '(b c) 'd)

'(a (b c) d)

Y en estos otros, el uso de la primitiva append
> (append '(a b) '(c d))

'(a b c d)

> (append '(c d) '(a b))

'(c d a b)

2.4 El primer mandamiento

Como una introducción al lenguaje Scheme (precursor de Racket), en el libro The Little Schemer de Friedman y Felleisen, se han seleccionado 10 reglas que les han llamado los diez mandamientos, que forman una colección de consejos muy útiles para una buena programación.

Un consejo básico al manipular recursivamente listas de datos, números o R-expresiones, es construir un caso base adecuado; esto es

  • Si id es una lista (de lo que sea), se deben hacer 2 preguntas. (empty? id) y "else". Por ejemplo, si deseamos determinar el número de elementos de una lista, podemos proceder recursivamente como
    ; ==========================================================
    ; Contrato: cardinalidad : lista -> numero
     
    ; Propósito: Calcular el número de elementos de una lista
     
    ; Ejemplo: (cardinalidad '(a b c d e)) debe devolver 5
     
    ; Definición: La cardinalidad de una lista es 0 si la lista es vacía, o bien es 1 + la cardinalidad del resto de la lista, en caso de que la lista no sea la lista vacía.
     
    (define cardinalidad
      (λ (L)
        (cond ((empty? L) 0)
              (else (+ 1 (cardinalidad (cdr L)))))))
    ; ==========================================================

  • Si val es un valor numérico que se utiliza en la recursión, se deben hacer 2 preguntas. (identidad? val) y "else". La función (identidad? val) retorna el valor #t si val es la identidad definida, puede ser 0, o 1 como ejemplos.
    ; ==========================================================
    ; Contrato: listaK : numero -> lista
     
    ; Propósito: Generar una lista de longitud k, conteniendo los primeros k números naturales.
     
    ; Ejemplo: (listaK 5) debe devolver '(1 2 3 4 5)
     
    (define listaK
      (λ (val)
        (cond ((= 0 val) '())
              (else (append (listaK (- val 1)) (list val))))))
    ; ==========================================================

  • Si id es una lista de R-expresiones y p es un predicado, se deben hacer 3 preguntas. (empty? id), (cumple? (car id) p); y else. Aquí hay que recordar que un predicado es una frase que tiene un valor de verdad falso o verdadero. La expresión (cumple? (car id) p) puede sustituirse por (equal? (car id) val) para algún valor específico val.
    ; ==========================================================
    ; Contrato: paraTodo : lista predicado -> booleano
     
    ; Propósito: Determinar si una lista <L-val> de R-expresiones cumple con un predicado <P>.
     
    ; Ejemplo: (paraTodo '(3 4 5) (λ (x) (> x 1))) debe devolver #t
     
    ; Definición: paraTodo devuelve un valor #t si todos los elementos de la lista <l-val> cumplen el predicado <P>, y devuelve #f en caso contrario.
     
    (define paraTodo
      (λ (l-val P)
        (cond ((empty? l-val) #t)
              ((equal? (P (car l-val)) #f) #f)
              (else (paraTodo (cdr l-val) P)))))
    ; ==========================================================

Para estudiar con más detalle el lenguaje, hay varios libros que pueden ser de gran utilidad, en Schemers.org.

Lo que sigue es una breve introducción a las redes neuronales.

2 comentarios:

  1. Excelente Abdiel, Bienvenido al blog de lenguaje de programación en Racket, seguiremos con interés tu curso!

    -jm

    ResponderEliminar
  2. Hola me pueden ayudar con la logica para un juego en racket gracias

    ResponderEliminar