lunes, 29 de mayo de 2017

Cómo programar: datos simples (21) - Enumeración

Diseñar una definición de datos para representar las letras de calificaciones en un curso.  Estas letras son A, B y C, que significan bueno, regular y malo respectivamente.

Esta definición corresponde a una enumeración, debido a que tiene un número fijo de valores diferentes. Son datos de texto (strings) usados para presentar cada uno de los distintos casos.

El código para esta definición es el siguiente:

; LetterGrade es uno de:
; - "A"
; - "B"
; - "C"
;interpretación: la letra de calificación en un curso.

;Ejemplos: son redundantes en enumeraciones.

En la plantilla orientada por datos buscar por "one of", para tomar de allí la plantilla correspondiente:






Se toma la plantilla correspondiente:

;(define (fn-for-letter-grade lg)
;(cond [ Q  A]
;          [Q   A] ) )

;Regla de plantilla utilizada:
;- Uno de (one of): 3 casos
; - valor atómico distinto (atomic distinct value):  "A"
; - valor atómico distinto (atomic distinct value):  "B"
; - valor atómico distinto (atomic distinct value):  "C"

Finalmente, se refina un poco la plantilla:

(define (fn-for-letter-grade lg)
 (cond [(string=? lg "A") (...) ]
           [(string=? lg "B") (...) ]
           [(string=? lg "C") (...) ]))

Más adelante se vera como se sigue refinando esta plantilla para desarrollar la funciòn completa.

Anterior
Siguiente

Cómo programar: datos simples (20) - Intervalos

El intervalo es un tipo de datos que representa un rango de números.

Por ejemplo, se requiere diseñar una definición de datos que represente un número de silla en una fila, cada fila con 32 sillas.

1 2 3 ... 32

Recordando un poco, una definición de datos consta de cuatro o cinco partes:

1) Una posible definición de una estructura (no visto hasta ahora, más adelante se verá en detalle)
2) Un comentario de tipo que define un nuevo nombre para el tipo de datos y describe como formar datos.
3) Una interpretación que describe la correspondencia entre información y datos
4) Uno o más ejemplos de los datos.
5) Una plantilla de una función que opera sobre el tipo de datos antes definido.

Ahora bien, el ejemplo planteado corresponde a un tipo atomic non-distinct, el cual tambien tiene un "subtipo" llamado interval (intervalo). Si se busca en la plantilla orientada por datos, es el siguiente:




El tipo de datos tendrá el nombre SeatNum (Número de silla):

;SeatNum is Natural [1, 32]
;Interpretación: SeatNum es un número natural que representa los números de silla
;en una fila, desde la uno hasta la 32. 1 y 32 son sillas que dan al pasillo (sillas ubicadas 
;en los extremos de la fila).

[1,32] indica que el rango incluye tanto el 1 como el 32

Si se cambia la notación, tendría un significado diferente:

(1,32) indica que se excluyen 1 y 32, es decir el rango incluye 2 y va hasta 31.
[1,32) indica que incluye 1 pero excluye 32, por lo que el rango va desde 1 hasta 31.

Volviendo al ejemplo, la notación será [1,32]

Ahora se definen los ejemplos, los cuales deben incluir al menos: los extremos y un número cercano a la mitad:

;Ejemplos:
;(define SN 1) ; pasillo
;(define SN 12) ; mitdad de la fila
;(define SN 32); pasillo

Luego la plantilla asociada a los tipos de datos atomic non-distinct:

;plantillla de datos
;(define (fn-for-seat-num sn)
;  (... sn))

En adelante se indicará la plantilla de datos utilizada:

;Regla de plantilla utilizada:
; - Atomic non-distinct
;   Natural [1,32]  ; intervalo que representa un número natural entre 1 y 32.

Al final el código completo de la definición de datos quedará así:

;SeatNum is Natural [1, 32]
;Interpretación: SeatNum es un número natural que representa los números de silla
;en una fila, desde la uno hasta la 32. 1 y 32 son sillas que dan al pasillo (sillas ubicadas 
;en los extremos de la fila).

;Ejemplos:
;(define SN 1) ; pasillo
;(define SN 12) ; mitdad de la fila
;(define SN 32); pasillo

plantillla de datos
;(define (fn-for-seat-num sn)
;  (... sn))

;Regla de plantilla utilizada:
; - Atomic non-distinct
;   Natural [1,32]  ; intervalo que representa un número natural entre 1 y 32.

Anterior
Siguiente



sábado, 27 de mayo de 2017

Cómo programar: datos simples (19) - Plantillas orientadas por datos (2)

En el artículo anterior, se identificó la plantilla asociada al tipo de datos atomic non-distinct para propositos de diseñar una función asociada a una forma de datos llamada CityName.  En esta sección se desarrollará una función que produce true (verdadero) si la ciudad es la mejor del mundo:

;Firma (un tipo de datos llamado CityName produce un valor booleano):
; CityName -> Boolean

;Propósito:  producir true (verdadero) si la ciudad es "Hogmeade".

;(define (best? cn ) false) ; colilla (stub)

;Ejemplos:
(check-expect (best? "Boston") false)
(check-expect (best? "Hogmeade") true)

; Plantilla tomada de la definición de datos CityName (ver artículo anterior):
; (define (fn-for-city-name cn)
;   (... cn))

; Desarrollo de la función:
;(define (best? cn)
 ;(if (string=? cn "Hogmeade")
 ;     true
 ;     false))

;Simplificando la anterior función, quedaría así:
(define (best? cn)
  (string=? cn "Hogmeade"))

Las plantillas muestran lo que se puede usar, no lo que se debe usar.  En algunos momentos es posible que uno borre o ajuste parte de las plantillas.

Anterior
Siguiente

viernes, 26 de mayo de 2017

Cómo programar: datos simples (18) - Plantillas orientadas por datos

El curso proporciona unas plantillas de funciones que operan sobre distintos tipos de datos. Dependiendo del tipo de datos utilizado en la resolución del problema, se escoge la plantilla asociada a ese tipo de datos.

Ejemplo:

Se requiere diseñar la definición de datos para representar el nombre de una ciudad y así obtener la plantilla de la función que opera sobre dicho tipo de datos.

El dominio del problema es el siguiente:

Nombre de una ciudad

Información:
Vancouver
Boston

Datos:
"Vancouver"
"Boston"

Basados en lo anterior, se comienza haciendo un comentario para formar el nuevo tipo de datos. En este ejemplo el tipo de datos se llamará CityName (nombre de la ciudad), el cual es una cadena de texto (string):

; CityName is String
; interpretación: El nombre de una ciudad

Luego, se crean dos constantes que representan ejemplos:

(define CN1 "Boston")
(define CN2 "Vancouver")

Una vez creados los ejemplos, se ingresa al banco de plantillas orientadas por datos (data driven templates).

Allí se busca el siguiente cuadro:











El anterior cuadro proporciona una plantilla genérica  con el código:

(define (fn-for-type-name x)
  <body> )


Este código define una función con un solo argumento "x" y un cuerpo <body> . Dicho cuerpo se ajusta de acuerdo al tipo de datos que deseamos trabajar.

Ahora bien, para este caso, una ciudad como "Vancouver" corresponde a un tipo de datos atómico no-distinto (atomic non-distinct), es decir, es un tipo de dato que no puede descomponerse en otras piezas más pequeñas (irreducible).

Este tipo de datos se busca en la plantilla antes mencionada, y de allí se toma la plantilla del cuerpo de la función  (ver la tercer columna "body or cond answer"): 



De acuerdo a lo anterior, el cuerpo de la función para un tipo de datos atomic non-distinct es el siguiente:

(...x)


Ese código ahora debe incluirse en el formato genérico antes señalado:

(define (fn-for-type-name x)
  <body> )

Para ello, se reemplaza <body> con el cuerpo de la función para un tipo de datos atomic non-distinct, que es  (... x) , para asi obtener la siguiente plantilla de código asociada al tipo de datos atomic non-distinct, según se indica abajo:


(define (fn-for-type-name x)
(... x)  ) 

Se reemplaza "fn-for-type-name" por el nombre del tipo de datos definido, que en este caso es city-name. De igual manera el argumento "x" puede reemplazarse por cn (city name). Conforme a lo anterio, el código quedaría asi:


(define (fn-for-city-name cn)
(... cn)  )


En resumen, se puede decir que una definición de datos consta de cuatro o cinco partes:

1) Una posible definición de una estructura (no visto hasta ahora, más adelante se verá en detalle)
2) Un comentario de tipo que define un nuevo nombre para el tipo de datos y describe como formar datos.
3) Una interpretación que describe la correspondencia entre información y datos
4) Uno o más ejemplos de los datos.
5) Una plantilla de una función que opera sobre el tipo de datos antes definido.

Anterior
Siguiente

domingo, 21 de mayo de 2017

Cómo programar: datos simples (17) - Definición de datos

Se comenzará con un problema mas o menos planteado, el cual se refinará posteriormente:

El siguiente código sobre la simulación de las luces de un semáforo:

;Natural --> Natural
;Produce el siguiente color de un semáforo

;define (next-color c) 0) ; stub (colilla)

;Plantilla
#;(define (next-color c )
   (... c))

;Función
(define (next-color c)
  (cond [ (= c 0) 2 ]
            [ (= c 1) 0 ]
            [ (= c 2) 1 ] ))

;Ejemplos
(check-expect (next-color 0) 2)
(check-expect (next-color 1) 0)
(check-expect (next-color 2) 1)

Al ejecutarse el anterior código todas las tres pruebas pasan satisfactoriamente. Sin embargo, es poco lo que se entiende del mismo. Al examinar cada parte del código se observa lo siguiente:

- La firma indica que con un número natural se produce un número natural.
- El próposito dice que se produce el siguiente color de un semáforo. ¿que tiene que ver un color con - un número natural?
- La función "next-color" con un argumento 0 produce 2, con un argumento 1 produce 0, y con un argumento 2 produce 1. ¿que significa eso?

Para entender lo anterior, hay que conocer el dominio del problema.   El dominio se refiere al campo de aplicación, en este caso, la simulación de las luces de un semáforo.

El dominio tambien se compone de la información y de los datos que representarán esta información.

Por ejemplo:

Información:  Una luz roja de un semáforo
Datos: El cero representa  la luz roja del semáforo

La representación de la información se conoce tambien como definición de datos. En la definición de datos se colocan los comentarios que definen un nuevo tipo de datos. Estos comentarios muestran como formar tipos de datos.

En la definición de datos tambien se incluye la interpretación, la cual explica como interpretar los tipos de datos creados. Esto ayuda a crear una asociación entre la información y los datos que la representan.

De acuerdo a lo anterior, el código quedaría así:

; Definición de datos

;La siguiente línea es un comentario del tipo de datos llamado "TLColor" (traffic light color o color de luz del semáforo). TLColor es un tipo de datos "atomic non-distinct", es decir, un valor simple que representa información:

;TLColor es uno de:
; - 0
; - 1
; - 2

; Interpretación: TLColor representa un solo color de luz de un semáforo: 0 es rojo, 1 es amarillo y 2 es verde.

;Plantilla tomada de la definición de datos simples (data driven templates/atomic non-distinct):
#; (define (fn-for-tlcolor c)
   (cond  [ (= c 0) (....) ]
              [ (= c 1)  (...) ]
              [ (= c 2) (...) ] ) 

;Definición de la función
;La función ahora se alimenta del tipo de datos creado anteriormente:

;TLColor--> TLColor
; Produce el siguiente color de un semáforo.

;define (next-color c) 0) ; stub
;Plantilla tomada de TLColor: se cambia el nombre de "fn-for-tlcolor c" a "next-color c":
#; (define (next-color c)
   (cond  [ (= c 0) (....) ]
              [ (= c 1)  (...) ]
              [ (= c 2) (...) ] )

;Función completa:
(define (next-color c)
(cond  [ (= c 0) 2 ]
              [ (= c 1) 0 ]
              [ (= c 2) 1 ]))

;Ejemplos
(check-expect (next-color 0) 2) ;con un argumento 0 (luz roja), produce 2 (luz verde)
(check-expect (next-color 1) 0); con un argumento 1 (luz amarilla), produce 0 (luz roja)
(check-expect (next-color 2) 1); con un argumento 2 (luz verde), produce 1 (luz amarilla)


Esta función no puede utilizar el 3 como argumento!  Por ahora, dejémosla así.

En resumen:

 La definición de datos describe:
- Como formar datos de un nuevo tipo
- Como representar información como datos
- Como interpretar los datos como información
- Las plantillas que se utilizaran para operar sobre tipos de datos

La definición de datos simplifica la función:

- restringe los datos consumidos
- restringe los datos producidos
- Ayuda a generar ejemplos
- Proporciona plantillas


Anterior
Siguiente

sábado, 20 de mayo de 2017

Cómo programar: datos simples (16) - evaluación de la expresión COND

La expresión COND se evalua de la siguiente manera:

1) Si no hay parejas de pregunta / respuesta, señalar que hay error
2)  Si la primera pregunta no es un valor, evaluarla  y reemplazarla con el valor correspondiente. Es decir, reemplazar la expresión COND con una nueva COND en el que la primera pregunta sea reemplazada por su valor (falso o verdadero).
3) Si la primera pregunta es "true" o "else", reemplace toda la expresión COND con la primera respuesta.
4) Si la primera pregunta es "false", deje a un lado la pregunta / respuesta y siga con el siguiente condicional.
5) Si la primera pregunta no es falsa o verdadera, entonces señale un error.

Ejemplo:

(cond [ (> 1 2) "bigger"]
          [ (= 1 2) "equal"]
          [ (< 1 2) "smaller"] )

Evaluación:

La primera pregunta es si 1 es mayor que 2. De acuerdo al paso 2, hay que reemplazar la pregunta por su valor, es decir, por "false", puesto que es falso que 1 sea mayor que 2. El código queda así:

(cond [ false "bigger"]
          [ (= 1 2) "equal"]
          [ (< 1 2) "smaller"] )

Dado que la primera pregunta era falsa, el paso 4 indica que se debe avanzar hacia el siguiente condicional [ (= 1 2) "equal"]. El nuevo código quedaría así:

(cond  [ (= 1 2) "equal"]
          [ (< 1 2) "smaller"] )

Ahora la primera pregunta es si 1 es igual a 2. El paso 2 indica que hay que reemplazar la pregunta por su valor, es decir, por "false", dado que es falso que 1 sea igual a 2. El nuevo código queda así:

(cond  [ false "equal"]
          [ (< 1 2) "smaller"] )

Como la primera pregunta era falsa, el paso 4 indica que se debe avanzar hacia el siguiente condicional [ (< 1 2) "smaller] ). Despues de este cambio, el nuevo código es el siguiente:

(cond  [ (< 1 2) "smaller"] )

Ahora la  primera pregunta es si 1 es menor que 2, lo cual es verdadero.  El paso 3 indica que si la pregunta es verdadera, entonces reemplace toda la expresión COND con la primera respuesta, lo cual queda así:

"smaller"

Como se mencionó en un artículo anterior, no es necesario hacer esta evaluación de forma manual.  Esto es simplemente para ilustrar como DrRacket evalua las expresiones COND. Recuérdese que se puede evaluar esta expresión paso a paso utilizando el boton "paso" o "stepper".  

Anterior
Siguiente
 

Cómo programar: datos simples (15) - condiciones multiples con la expresión COND

Hay ocasiones en que la expresión IF no resulta suficiente, dado que algunas expresiones tienen multiples opciones. Para estos casos se utiliza la expresión COND.

El formato de la expresión COND es el siguiente:

(cond [ pregunta1 respuesta1 ]
          [ pregunta2 respuesta2 ]
          [ pregunta n respuesta n ] )

En el siguiente ejemplo se evalua si la imagen es alta, cuadrada o ancha. Para poner comentario en un bloque completo de código se utiliza #;

(require 2htdp/image)

(define I1 (rectangle 10 20 "solid" "red")) ;se crea la constante I1 que representa un rectángulo de 10x20
(define I2 (rectangle 20 20 "solid" "red")) ;se crea la constante I2 que representa un rectángulo de 20x20
(define I3 (rectangle 20 10 "solid" "red")); se crea la constante I3 que representa un rectángulo de 20x210

;colilla (stub): se crea la función "aspect ratio", cuyo argumento es una imagen (img) y su resultado es un texto.
;(define (aspect-ratio img) " ")

;Plantilla
#;(define (aspect-ratio img)
    (... img))

;creación de la función completa:
(define (aspect-ratio img)
 (cond [ (> (image-height img) (image-width img) ) "tall"]
           [ (= (image-height img) (image-width img) ) "square"]
           [ else "wide"] ))

;Firma: Image --> String 
;Próposito: produce la forma de una imagen, que es una de las siguientes: "alta" (tall), "cuadrada" (square) o "ancha" (wide)
;ejemplos:
(check-expect (aspect-ratio I1) "tall")
(check-expect (aspect-ratio I2) "square")
(check-expect (aspect-ratio I3) "wide")






miércoles, 17 de mayo de 2017

Cómo programar: datos simples (14) - Ejemplo función con condicional IF

Para este ejemplo, se quiere crear una función que produzca "verdadero" (true) si la imagen es alta. Entiéndase por alto cuando el alto es mayor que el ancho.

al utilizar la receta de diseño de funciones se tiene lo siguiente:

(require 2htdp/image)

;Firma (signature)
;Image --> Boolean (a partir de una imagen se produce un dato booleano)

;Próposito (purpose):
;Producir "verdadero" si el alto de la imagen es mayor que su ancho.

;colilla (stub). Se hace un borrador de la función, no tiene que ser la soluciòn completa
; en este ejemplo, se crea una función llamada "tall?" con argumento "img"(imagen) que por ahora produce false
(define (tall? img) false)

;Ejemplos:
(check-expect (tall? (rectangle 2 3 "solid" "red")) true)

;Al utilizar la función tall? cuyo argumento es un rectangulo de ancho 2 pixeles y alto 3 pixeles
; se deberia producir "true", pues es verdadero que 3 es mayor que 2.

(check-expect (tall? (rectangle 3 2 "solid" "red")) false)

;Al utilizar la función tall? cuyo argumento es un rectangulo de ancho 3 pixeles y alto 2 pixeles
; se deberia producir "false", pues es falso que 2 es mayor que 3.

(check-expect (tall? (rectangle 3 3 "solid" "red")) false)

;Al utilizar la función tall? cuyo argumento es un rectangulo de ancho 3 pixeles y alto 3 pixeles
; se deberia producir "false", pues es falso que 3 es mayor que 3.

Por ahora, se ejecuta este código para verificar que este bien planteado, asi sea que los resultados esperados sean diferentes a lo que produce la función.




 
Posteriormente se inserta la plantilla de la función dentro del código, asi como la función completa:

(require 2htdp/image)

;Firma (signature)
;Image --> Boolean (a partir de una imagen se produce un dato booleano)

;Próposito (purpose):
;Producir "verdadero" si el alto de la imagen es mayor que su ancho.

;colilla (stub). Se hace un borrador de la función, no tiene que ser la soluciòn completa
; en este ejemplo, se crea una función llamada "tall?" con argumento "img"(imagen) que por ahora produce false
;(define (tall? img) false)

;plantilla (template)
;(define (tall? img )
; (... img)

;Función
(define (tall? img)
(if (> (image-height img) (image-width img))
      true
      false))

;Ejemplos:
(check-expect (tall? (rectangle 2 3 "solid" "red")) true)

;Al utilizar la función tall? cuyo argumento es un rectangulo de ancho 2 pixeles y alto 3 pixeles
; se deberia producir "true", pues es verdadero que 3 es mayor que 2.

(check-expect (tall? (rectangle 3 2 "solid" "red")) false)

;Al utilizar la función tall? cuyo argumento es un rectangulo de ancho 3 pixeles y alto 2 pixeles
; se deberia producir "false", pues es falso que 2 es mayor que 3.

(check-expect (tall? (rectangle 3 3 "solid" "red")) false)

;Al utilizar la función tall? cuyo argumento es un rectangulo de ancho 3 pixeles y alto 3 pixeles
; se deberia producir "false", pues es falso que 3 es mayor que 3.

Al ejecutar este código, las tres pruebas pasan satisfactoriamente:



Es de señalar que la función creada anteriormente puede simplificarse, teniendo en cuenta que en este caso, el condicional IF cuando es verdadero produce "true" y cuando es falso produce "false", lo cual es redundante:

(define (tall? img)
(if (> (image-height img) (image-width img))
      true
      false))

Por lo tanto, la función anterior puede reducirse a lo siguiente:

(define (tall? img)
(> (image-height img) (image-width img))



Anterior
Siguiente

Cómo programar: datos simples (13) - Fallas en las pruebas

Si una prueba falla (check-expect falla), puede ser que:

- La definición de la función está mal.
- La prueba esta mal planteada.
- Tanto la función como la prueba estan mal.

Se debe revisar la prueba antes de arreglar la definición de la función.

El diseño es el proceso de ir desde un problema pobremente definido hacia una solución bien estructurada.  Por lo tanto, haciendo el problema más específico es parte del proceso de diseño.

Anterior
Siguiente

martes, 16 de mayo de 2017

Cómo programar: datos simples (12) - Función "yell" (grito)

Este es un ejemplo del uso de la receta de diseño de funciones.  Se quiere crear una función llamada yell (grito), la cual añade el simbolo ! al final de cualquier palabra introducida por el usuario:

;String --> String   -->  consume texto y produce texto --> Firma (signature)
; Añade "!" al final de una palabra  --> Próposito (purpose)
;se crea un borrador de la función (stub):
(define (yell s) " ")

; Ejemplos:
(check-expect (yell "hello") "hello!") ; la función yell con un argumento "hello" debería producir "hello!"
(check-expect (yell "bye") "bye!") ; la función yell con un argumento "bye" debería producir "bye!"

Al ejecutar este código se tiene lo siguiente:














La función creada anteriormente solo produce un espacio " ", el cual es diferente a los valores esperados "hello!" y "bye!". No obstante, no produce ningún error de escritura, por lo que hasta el momento la función esta bien planteada.

Si hubiese omitido el paréntesis en el segundo ejemplo, se habría generado un error:


Por lo tanto, es importante ejecutar el código de forma seguida, para ir detectando los errores.

Siguiendo con el ejemplo y habiendo visto que no hay errores de escritura, se pone el stub como comentario (colocando un punto y coma al comienzo), para deshabilitarlo y crear posteriormente la función completa:

;(define (yell s) " ")

El código completo quedaría así:

;String --> String   -->  consume texto y produce texto --> Firma (signature)
; Añade "!" al final de una palabra  --> Próposito (purpose)
;se crea un borrador de la función (stub):
;(define (yell s) " ")

; Ejemplos:
(check-expect (yell "hello") "hello!") ; la función yell con un argumento "hello" debería producir "hello!"
(check-expect (yell "bye") "bye!") ; la función yell con un argumento "bye" debería producir "bye!"

Ahora se añade la plantilla al código. para este curso existe un documento con las plantillas disponibles, por ahora, se utiliza esta plantilla para datos simples:

(define (yell s )
 (... s))

El código completo sigue así:

;String --> String   -->  consume texto y produce texto --> Firma (signature)
; Añade "!" al final de una palabra  --> Próposito (purpose)
;se crea un borrador de la función (stub):
;(define (yell s) " ")

;Plantilla (template)
(define (yell s )
 (... s))

; Ejemplos:
(check-expect (yell "hello") "hello!") ; la función yell con un argumento "hello" debería producir "hello!"
(check-expect (yell "bye") "bye!") ; la función yell con un argumento "bye" debería producir "bye!"


Para construir la función completa, se observan los ejemplos antes planteados, de los cuales se puede deducir que basta con añadir el simbolo ! al argumento de la función. La unión de texto se realiza con la función string-append.

Conforme a lo anterior, se procede a copiar la plantilla y modificarla, para dejarla así:

(define (yell s )
 (string-append s "!") )

El código completo quedaría asi:

;String --> String   -->  consume texto y produce texto --> Firma (signature)
; Añade "!" al final de una palabra  --> Próposito (purpose)
;se crea un borrador de la función (stub):
;(define (yell s) " ")

;Plantilla (template)
;(define (yell s )  -> se pone punto y coma al comienzo para dejarla como comentario
 ;(... s))

;Función
(define (yell s )
 (string-append s "!") )

; Ejemplos:
(check-expect (yell "hello") "hello!") ; la función yell con un argumento "hello" debería producir "hello!"
(check-expect (yell "bye") "bye!") ; la función yell con un argumento "bye" debería producir "bye!"


y el resultado final es el siguiente:



Anterior
Siguiente


miércoles, 10 de mayo de 2017

Cómo programar: datos simples (11) - Receta de como diseñar funciones

El curso original plantea una metodología para crear funciones (how to design functions recipe). Es una "receta" que va a ser utilizada a lo largo de todo el curso.

A modo de introducción, la receta se compone de los siguientes pasos:

1) Firma, propósito y "colilla" (stub)
2) Definición de ejemplos, cada uno contenido dentro de expresiones check-expect
3) Plantilla e inventario
4) Códificación del cuerpo de la función
5) Pruebas y depuración hasta que sea correcto

Para problemas "fáciles", puede que sea tedioso hacer la receta, por lo cual hay que tener algo de paciencia.  La idea de la receta es atacar problemas complejos, de forma que los mismos sean descompuestos en trozos más pequeños que sean fáciles de manejar.

Cada paso se escribe como comentario dentro del código, a manera de guía.  Como tal, los pasos no hacen parte de las expresiones de DrRacket.

El detalle de los pasos es el siguiente:

Firma

Indica el tipo de dato que entra como insumo de la función  y el tipo de dato que debe generarse.

Por ahora, los tipos de datos son:  Number (número), Integer (número entero), Natural (número natural), String (texto), Image (imágen), Boolean (booleano, es decir falso o verdadero).

Propósito

Indica lo que la función produce según lo que consume.

Colilla (stub)


Es un pequeño código de la función que se quiere hacer.  En este paso, apenas se define el nombre de la función, el argumento y como cuerpo de la función se pone cualquier resultado que se desea obtener. La finalidad es verificar si el borrador de la función y los ejemplos están escritos correctamente, aunque el resultado produzca un error.  Este paso es importante para detectar que no haga falta algún parentesis, que la constante haya sido definida previamente, etc.  Si bien parecen detalles sin importancia, cuando no se detectan a tiempo pueden generar dolores de cabeza posteriormente.

Ejemplos / pruebas


Los ejemplos ayudan a entender que debe hacer la función.  Varios ejemplos ayudan a ilustrar un comportamiento.   Cuando los ejemplos se incluyen en expresiones check-expect, significa que los mismos van a servir como pruebas unitarias para la totalidad de la función.   Tan pronto como se encuentra un error, se corrige con prontitud.

Plantilla / inventario

Es un modelo predeterminado, el cual posteriormente se ajusta según la funciòn que se quiera diseñar.

Codificación del cuerpo de la función:

Es el proceso de considerar todos los pasos anteriores para establecer como completar la función.

El siguiente ejemplo ilustra rápidamente la receta:

; Number --> Number  (firma)
; Produce el doble  del número recibido  (propósito)

(define (doble n) 0) ; colilla (stub)

;ejemplos:

(check-expect (doble 3) 6)  ;la función "doble" con un argumento 3 deberia producir 6
(check-expect (doble 4.2) 8.4) ; la función "doble" con un argumento de 4.2 debería producir 8.4
(check-expect (doble 4.2) (* 2  4.2)) ; el doble de 4.2 debería ser 2 X 8.4, que es lo mismo que 8.4

Si se ejecuta hasta aquí se obtiene:


Los resultados de la prueba indican que el cero que produce la función difiere de los resultados esperados.  Si bien  la funciòn difiere de los esperado, se observa que el borrador de la función y los ejemplos estan bien escritos, por lo cual se puede avanzar con el proceso.

Retomando nuevamente se tiene:

; Number --> Number  (firma)
; Produce el doble  del número recibido  (propósito)

;(define (doble n) 0)  colilla (stub) - se coloca como comentario cuando ya se hizo la prueba

;ejemplos:

(check-expect (doble 3) 6)  ;la función "doble" con un argumento 3 deberia producir 6
(check-expect (doble 4.2) 8.4) ; la función "doble" con un argumento de 4.2 debería producir 8.4
(check-expect (doble 4.2) (* 2  4.2)) ; el doble de 4.2 debería ser 2 X 4.2, que es lo mismo que 8.4

;plantilla
;(define (doble n)
;  (... n))

;Cuerpo de la funciòn
(define (doble n) (*2 n))

Al final, DrRacket indica que todas las tres pruebas fueron correctas:


Más adelante se verá con mayor detalle esta receta.

Anterior
Siguiente



Cómo programar: datos simples (10) - operadores lógicos

En Racket tambien se utilizar los operadores lógicos AND, NOT, OR o similares.

El operador AND produce verdadero solo cuando todas las expresiones son verdaderas. Si hay alguna expresión falsa, el operador AND automáticamente termina la evaluación.  Todas las expresiones que se evaluan dentro del operador AND deben producir un valor booleano.

La estructura de la expresión AND es la siguiente:

(and  <expresión 1> <expresión 2> .... <expresión n> )

De forma similar puede utilizarse la expresión OR, la cual es verdadera cuando alguna de las expresiones es verdadera. Tambien está la expresión NOT, la cual convierte en verdadero lo que es falso y viceversa.

Un ejemplo del operador AND es el siguiente:

(require 2htdp/image)
(define I1 (rectangle 10 20 "solid" "red"))
(define I2 (rectangle 20 10 "solid" "blue"))

; la expresión and es la siguiente:

(and  (>  (image-height I1) (image-height I2))
         (<   (image-width I1) (image-width I2)))

; reemplazando las expresiones se obtiene:

(and (>   20 10)
        (<   10 20))

(and true true)

true

Por supuesto que no es necesario hacer cada paso, pues Drracket lo hace automaticamente.  Para ver como Drracket evalua cada paso, se pulsa el boton "paso":



El paso a paso es el siguiente:

Código original:


Pasos:






































Cómo programar: datos simples (9) - Evaluar una expresión "IF"

La evaluación de una expresión if consiste en hacer una ejecución paso a paso de forma manual. Esto puede ser útil al momento de realizar alguna depuración cuando las cosas no resultan como se esperaban.

Dicha evaluación tiene las siguientes reglas:

1) Si la pregunta contenida en if no es un valor, evaluar la expresión if y reemplazarla con un valor.
2) Si la pregunta es verdadera (true), reemplazar la totalidad de la expresión if con la respuesta asociada a verdadero.
3) Si la pregunta es falsa (falsa), reemplazarla la totalidad de la expresión if con la respuesta asociada a falso.
4) Si la pregunta es un valor distinto a falso o verdadero, entonces se produce un error.

En el siguiente ejemplo se añadiran algunos comentarios en el código.  Para crear comentarios en una línea, se puede usar el punto y coma seguido del comentario:

(require 2htdp/image)
(define I1 (rectangle 10 20 "solid" "red"))
(define I2 (rectangle 20 10 "solid" "blue"))

; la siguiente expresión if es la que se va a evaluar:

(if (< (image-width I2)   ; dado que esta expresión no es un valor, se reemplazará por 20 en el paso 1)
         (image-height I2)) ; dado que esta expresión no es un valor, se reemplazará por 10 en el paso 1)
         (image-width I2) ; respuesta si es verdadero
         (image-height I2)) ; respuesta si es falso

Evaluación:

Paso 1) 

(if (< 20 10)         ; dado que la pregunta aún no es un valor, se reemplazará por "false" en el paso 2)
      (image-width I2) ; respuesta si es verdadero
      (image-height I2)) ; respuesta si es falso

Paso 2)

(if    false
      (image-width I2)
      (image-height I2)) ; dado que la pregunta es falsa, en el paso 3) se reemplazará la expresión completa con esta expresión

Paso 3)

(image-height I2) ; la expresión completa se reemplazo por la respuesta asociada a falso. En el siguiente paso se reducirá al valor 10, que representa el alto del rectángulo definido en la constante I2.

Paso 4)

10

Si se pone todo este código en Drracket, al final se va a obtener el mismo resultado en todos los pasos:




Anterior
Siguiente

martes, 9 de mayo de 2017

Cómo programar: datos simples (8) - Condicional "IF"

La expresión if evalua una pregunta de tipo falso/verdadero y dependiendo del resultado, ejecuta una acción.

La estructura de la expresión if es la siguiente:

(if  <expresión>
     <expresión si la respuesta es verdadera o true>
     <expresión si la respuesta es falsa o false>  )

En el artículo anterior, teniamos el siguiente código en el cual se creaban dos constantes que representaban dos rectángulos:

(require 2htdp/image)
(define I1 (rectangle 10 20 "solid" "red"))
(define I2 (rectangle 20 10 "solid" "blue"))
A modo de repaso, los parametros de la función rectangle son: ancho, alto,modo y color.

Ahora queremos saber si el rectángulo representado en I1 es alto o ancho.  Se considera alto si el ancho es menor que el alto. Por el contrario, se considera ancho si el ancho es mayor que el alto. Mediante una expresión if se escribirá la palabra "tall" cuando sea alto y la palabra "wide" cuando sea ancho:

(if  (< (image-width I1)
           (image-height I1))
          "tall"
           "wide" )

En este caso, el resultado será "tall", dado que es verdadero que el ancho (10) es menor que el alto (20):


Anterior
Siguiente

lunes, 8 de mayo de 2017

Cómo programar: datos simples (7) - Valores booleanos

Los valores booleanos representan respuestas del tipo falso o verdadero. Por ejemplo, es falso que 4 es menor que 2.

Racket cuenta con predicados, los cuales sirven para producir valores booleanos.  Un ejemplo de predicado es el signo ">" que significa "mayor que..".

Suponga que define dos constantes, el ancho (width) y el alto (height):

(define width 100)
(define height 100)

Ahora utiliza el predicado ">" para producir un valor booleano:
(> width height)

Dado que width no es mayor que height, entonces se produce  "falso" (false).

En cambio si se utiliza el predicado ">="  (mayor o igual que), entonces se produce "verdadero" (true)





Tambien se puede usar el predicado "=" (igual que) para evaluar dos números, asi como "<" (menor que) o "<=" (menor o igual que).

Para comparar si dos cadenas de texto son iguales, se utiliza el predicado string=?

(string=? "foo" "bar")

Dado que "foo" y "bar" no son iguales, entonces se produce falso (false)



Tambien puede compararse el ancho de dos images. En el siguiente ejemplo, se crean dos constantes llamadas I1 e I2. Cada unas de estas representa un rectángulo. Para crear los rectángulos se usa la función rectangle, cuyos parametros son: ancho, alto, modo y color:

(require 2htdp/image)
(define I1 (rectangle 10 20 "solid" "red"))
(define I2 (rectangle 20 10 "solid" "blue"))

Posteriormente se compara si el ancho de I1 es menor que el ancho de I2. Para seleccionar el ancho de una imágen se utiliza la función image-width, cuyo parametro es la imágen a la cual se le desea conocer el ancho:

(<  (image-width I1)
     (image-width I2) )


Lo anterior produce verdadero (true), ya que el ancho de I1 es 10, el cual es menor al ancho de I2 que es 20.


 


domingo, 7 de mayo de 2017

Cómo programar: datos simples (6) - Evaluación de funciones.

La evaluación consiste en revisar cada paso en la ejecución de la función.

Código a evaluar:

(require 2htdp/image)
(define (bulb c)
   (circle 40 "solid" c))

(bulb (string-append "re" "d"))

El primer paso en la evaluación es reducir los argumentos u operandos a valores:

En la función
(bulb (string-append "re" "d"))
La función (string-append "re" "d) se reduce a "red", con lo cual queda:
(bulb "red")

Una vez que el argumento de la función ya esta reducido, el segundo paso es reemplazar la llamada a la función bulb con el cuerpo de dicha función:

La llamada a la función bulb
(buld "red")
Se convierte en:
(circle 40 "solid" "red")

Finalmente, la expresión (circle 40 "solid" "red") se convierte en el círculo rojo.

En Dracket se deberan obtener los mismos resultados en cada paso de la evaluación:





Anterior
Siguiente

viernes, 5 de mayo de 2017

Cómo programar: datos simples (5) - Aspectos básicos de las Funciones

En términos simples, las funciones son algo que toman unos datos de entrada (argumentos), los procesan y generan un resultado.  Por ejemplo, Racket tiene una función ya definida llamada circle, con la cual se genera un círculo a partir de unos argumentos suministrados por el usuario como lo son el radio del circulo, el modo (relleno "solid" o sin relleno "outline") y el color.

Los programadores pueden tambien definir sus propias funciones para luego utilizarlas cuando las necesiten.

A continuación se plantea el caso en el que un código repetido puede convertirse en una función.

Análisis de caso

Un semáforo tiene tres luces: una roja, otra amarilla y una verde.  Cada luz es representada  con un círculo y su respectivo color. Para dibujar los círculos, se utiliza la función circle. Asi mismo, los círculos se colocan uno encima del otro mediante la función above. Tanto circle como above son funciones que vienen incluidas en Racket:

(require 2htdp/image)
(above   (circle 40 "solid" "red")
              (circle 40 "solid" "yellow")
              (circle 40 "solid" "green") )


De lo anterior,  se observa que circle 40 "solid"  permanece fijo y se repite varias veces.   Por el contrario, el valor de cada  color es variable.

Para definir una función que reemplace el código repetido (circle 40 "solid"), lo primero es darle un nombre a dicha función. En el curso original, la función ha sido nombrada "bulb".

Luego se procede a identificar cuales son los argumentos de la función.  En este caso, se toma como argumento el dato variable, es decir, el color. Dicho argumento puede representarse mediante una letra o palabra, la cual será  "c".

Luego se determina cual es el cuerpo de la función, es decir, la operación que se va a realizar sobre el argumento (el color). En este caso, se toma como cuerpo de la función el dato fijo, es decir, el código repetido circle 40 "solid". Dicho código se encargara de dibujar el círculo de 40 pixeles de radio y rellenado con el color especificado en el argumento.

Para definir la función en Racket, se utiliza la expresión define, tal y como se indica a continuación:

(define (nombre_función  argumento) (operación argumento) )

El código de la definición de la función quedaría así:

(define (bulb c)
    (circle 40 "solid" c))

Téngase en cuenta que el argumento (c) que aparece al lado del nombre de la función y de la operación es el mismo.

Ahora bien, en este paso puede probarse la función con el color amarillo. Para ello se "llama" a la función utilizando el argumento "yellow" como se indica abajo:

(bulb "yellow")

El código completo de la prueba es el siguiente:

(require 2htdp/image)
(define (bulb c)
     (circle 40 "solid" c))

(bulb "yellow")
















Siguiendo con el desarrollo de la tarea, se deben colocar los círculos uno encima del otro. Esto se logra llamando a la función above, tal y como estaba antes, solo que esta vez la función above va a tener como argumentos varias funciones bulb, las cuales a su vez tienen como argumento un color:

(above  (bulb "red")  (bulb "yellow") (bulb "green") )

El código completo quedaría así:

(require 2htdp/image)

(define (bulb c)
     (circle 40 "solid" c))

(above  (bulb "red")  (bulb "yellow") (bulb "green") )


























Anterior
Siguiente

miércoles, 3 de mayo de 2017

Cómo programar: datos simples (4) - Constantes

Las constantes representan información que va a ser utilizada varias veces dentro de un programa. Para definir una constante se utiliza la expresión define.

Por ejemplo, el ancho de una ventana de 400 pixeles puede representarse por una constante llamada "Width":

(define width 400)

De igual manera, un alto de ventana de 600 pixeles puede representarse en una constante denominada "height":

(define height 600)

Ahora bien, si varias funciones de un programa requieren procesar el area de la ventana, es decir, el ancho por el alto, entonces solo tendrían que hacer esta operación:

(* width height) --> 240.000











En ese caso, si se requiere modificar el ancho o el alto, solo tendría que modificarse el código donde estan definidas las constantes. Esto es muy útil cuando un programa es extenso en código.

Las constantes tambien pueden representar imágenes.  Basta con utilizar la expresión define, indicar el nombre de la constante y enseguida pegar la imagen. Por ejemplo, la imagén de un gato puede representarse mediante una constante llamada "cat":

 (define cat

)



En Drracket se visualizaría así:










El gato puede rotarse 10 grados a la derecha mediante la función rotate. Dado que esta función procesa imágenes, debe utilizarse primero la expresión (require 2htdp/image). El código completo es el siguiente:

(require 2htdp/image)
 (define cat

)



(rotate -10 cat)

Con lo cual se obtiene:


















Ahora bien, para no tener que repetir la función rotate cada vez que se requiera girar al gato, se puede crear una constante llamada "rcat" que represente al gato con la inclinación de 10 grados a la derecha. Para ello se comienza con la expresión define y dentro de la misma se incluye la función rotate, la cual a su vez incluye la constante cat que se creo anteriormente:

(define rcat (rotate -10 cat) )

El código completo es el siguiente:

(require 2htdp/image)
 (define cat

)



(define rcat (rotate -10 cat))

Al ejecutar este código en DrRacket no sale nada, dado que solo se han creado las constantes. Si se desea ver al gato inclinado, se escribe el nombre de la constante "rcat" en la parte de abajo de DrRacket, seguido de la tecla enter:




































Esto es todo por ahora. Más adelante se veran más ejemplos con el gato, incluida una animación.

Anterior
Siguiente

Cómo programar: datos simples (21) - Enumeración

Diseñar una definición de datos para representar las letras de calificaciones en un curso.  Estas letras son A, B y C, que significan bueno,...