1 Elementos Básicos
El ambiente de desarrollo (IDE) DrRacket divide inicialmente el espacio en dos partes, una para definiciones, y otra para interactuar con el lenguaje directamente, al estilo de un terminal Unix. Usaremos la forma interactiva para empezar a explorar el lenguaje.
1.1 Tipos primitivos
Números, incluyendo fracciones y números imaginarios:
> 1 1
> -3 -3
> 4.02 4.02
> 6.02e+23 6.02e+23
> 1+2i 1+2i
> 4/3 4/3
Booleanos:
> #t #t
> #f #f
Strings (incluyendo caracteres Unicode):
> "hola" "hola"
> "hola \"mundo\" feliz" "hola \"mundo\" feliz"
> "λx:(μα.α→α).xx" "λx:(μα.α→α).xx"
Símbolos:
> 'hola 'hola
Otros tipos primitivos incluyen caracteres, bytes y byte strings, void, undefined, y estructuras de datos como pares, listas, cajas, tablas de hash, y vectores. Veremos algunos más adelante.
1.2 Usar funciones predefinidas
Scheme es un lenguaje con sintáxis prefijada con paréntesis. El primer elemento después del paréntesis "(" es la función a aplicar, y todo lo demás hasta el paréntesis ")" correspondiente son los argumentos (separados por espacios). Por ejemplo, para aplicar funciones aritméticas (que reciben un número variable de argumentos):
> (+ 1 2) 3
> (* 2 4 5) 40
> (- 1 1/4) 3/4
Y similarmente para otras funciones matemáticas y lógicas:
> (+ 1 (- 3 4)) 0
> (sqrt -1) 0+1i
> (or (< 5 4) (equal? 1 (- 6 5))) #t
> (and (not (zero? 10)) (+ 1 2 3)) 6
Como se puede apreciar en el último ejemplo, en Scheme, todo lo que no es #f es verdadero.
Manipulación de strings:
> (string-append "ho" "la") "hola"
> (string-length "hola mundo") 10
> (substring "Apple" 1 3) "pp"
> (string->symbol "Apple") 'Apple
Hay varias formas de imprimir, por ejemplo printf:
> (printf "hola~n") hola
> (printf "hola ~a ~s~n" "mundo" "feliz") hola mundo "feliz"
Scheme es un lenguaje seguro (¡no hay segmentation faults!). Al invocar una función con argumentos del tipo equivocado, se genera un error:
> (+ 1 "hola") +: contract violation
expected: number?
given: "hola"
argument position: 2nd
other arguments...:
1
Existe un dialecto de Racket, llamado Typed Racket, que es estáticamente tipeado. Puede encontrar información en la documentación.
El mensaje de error explica claramente lo que pasó. Es importante notar que el error se reporta en tiempo de ejecución. Scheme no es un lenguaje estáticamente tipeado, al igual que JavaScript y Python, entre otros.
Ejercicio: imprima (con printf) la raíz cuadrada de -1/4.
1.3 Condicionales
> (if (> 4 10) "hola" "chao") "chao"
> (if (> 12 10) "hola" "chao") "hola"
Los ifs se pueden anidar:
> (if (> 2 3) (printf "hola") (if (> 6 5) (printf "chao") #f)) chao
Pero es más cómodo usar cond para este tipo de condicional múltiple:
El uso de paréntesis cuadrados es totalmente opcional, es solamente con fines de legibilidad. Veremos otros usos de esos paréntesis conformemente a las buenas prácticas de la comunidad Racket a lo largo del curso.
> (cond [(> 2 3) (printf "hola")] [(> 6 5) (printf "chao")] [else #f]) chao
1.4 Definir identificadores
Se puede definir identificadores tal que queden globalmente disponibles. Esas definiciones se pueden hacer en la parte de definiciones del IDE:
(define MAX 100)
> MAX 100
> (< 25 MAX) #t
> (define x 10)
> (+ x 1) 11
Como hemos visto, la sintáxis de los identificadores en Scheme, incluyendo para nombres de funciones, es bastante liberal. Casi cualquier secuencia de caracteres que no sea un espacio es un identificador: +, number?, pass/fail, set!, λ=>α, etc. son identificadores totalmente válidos. En realidad, solamente los paréntesis (cualquier forma) y los caracteres ,.;’‘|\ son especiales (y obviamente, las secuencias de caracteres que forman los números).
> (define !λ=>-α/β?☺ "weird")
> !λ=>-α/β?☺ "weird"
Se puede también introducir identificadores con alcance local con let:
> x 10
> (let ([x 3] [y 2]) (+ x y)) 5
> x 10
> y y: undefined;
cannot reference undefined identifier
Notar que let no permite usar un identificador en el cálculo de otro identificador introducido después en el mismo let. Para esto hay que usar let*, que es equivalente a lets anidados:
> (let ([a 3] [b (+ a a)]) b) a: undefined;
cannot reference undefined identifier
> (let* ([a 3] [b (+ a a)]) b) 6
Ejercicio: introduzca localmente dos identificadores locales, asociados a números, y retorne el máximo.
1.5 Definir funciones
Además de las funciones existentes, se pueden definir funciones propias:
(define (double x) (+ x x))
> (double 2) 4
También se puede definir una función directamente en la parte de interacciones del IDE:
> (define (foo x) (if (< x 10) (printf "menor") (printf "mayor")))
> (foo 4) menor
> (foo 11) mayor
Más adelante veremos herramientas metodológicas para Definir Funciones.
Ejercicio: defina la función (sum a b) que suma sus dos parámetros.
Ejercicio: defina la función (pick-random x y) que retorna en forma aleatoria uno de sus argumentos. (La función (random) genera un número aleatorio entre 0 y 1).
1.6 ¿Dónde encuentro información sobre...?
La mejor manera de explorar Racket es usar el Help Desk (menú Help en DrRacket), que abre la documentación de Racket en local (la documentación también está disponible en la web). Las partes más importantes son el Racket Guide para una explicación al estilo "tutorial", y la Racket Reference para una descripción exhaustiva del lenguaje Racket y de todas las funciones y librerías incluidas.
¡La documentación de Racket es sumamente útil y completa! Acostúmbrese a usarla desde ya.