@@ -20,6 +20,30 @@ module Intro where
20
20
-- Eso significa que las declaraciones deben arrancar en la columna 0,
21
21
-- y cualquier linea puede partirse siempre y cuando se indente un poco mas que la anterior.
22
22
23
+ import Data.List (sort )
24
+
25
+ -- Acá importamos la función `sort` del módulo Data.List.
26
+ -- Hay muchas maneras de importar cosas, pero por ahora vamos a ignorar todas.
27
+
28
+
29
+ ---------------------------------------------------------------------------
30
+ -- Consola interactiva --
31
+ ---------------------------------------------------------------------------
32
+
33
+ -- Antes que nada, vamos a abrir una consola de GHCi, el interprete dinámico que Haskell
34
+ -- nos ofrece para poder probar cosas. Esto lo hacemos con:
35
+ --
36
+ -- > stack ghci src/Intro.hs
37
+ --
38
+ -- Acá podemos probar cosas sencillas como `4 + 6` o "hola que tal".
39
+ --
40
+ -- Aparte, podemos ver el tipo de una expresión con `:t` así
41
+ -- (`[Char]` es lo mismo que `String`):
42
+ --
43
+ -- > :t "holis"
44
+ -- > "holis" :: [Char]
45
+
46
+
23
47
-- Siendo Haskell un lenguaje con tipado estático, uno de los principales elementos que vamos
24
48
-- a definir son, justamente, tipos.
25
49
@@ -30,12 +54,15 @@ module Intro where
30
54
-- En otro lenguajes `=` suele significar asignación.
31
55
-- En Haskell, `=` significa "está definido así".
32
56
33
- -- +--------------------------- Nombre del *tipo*
34
- -- | +----------------- Nombre del *constructor*
35
- -- | | +--------- Toma una `String`
36
- -- | | | +-- Y toma un `Int`
37
- -- V V V V
38
- data Persona = Persona String Int
57
+ -- +------------------------------- Nombre del *tipo*
58
+ -- | +--------------------- Nombre del *constructor*
59
+ -- | | +------------- Toma una `String`
60
+ -- | | | +------ Y toma un `Int`
61
+ -- | | | |
62
+ -- | | | | +-- Además, el tipo es "mostrable" (lo vemos depués)
63
+ -- | | | | |
64
+ -- V V V V V
65
+ data Persona = Persona String Int deriving (Show )
39
66
40
67
-- Usamos `data` para definir un nuevo tipo de datos.
41
68
-- Todos los tipos deben empezar con mayúscula, y el nombre de este tipo es `Persona`.
@@ -56,7 +83,7 @@ maria = Persona "Maria" 32
56
83
-- | | | | +------------ Otro constructor
57
84
-- | | | | | +--- Argumentos del segundo constructor
58
85
-- V V V V V V
59
- data Figura = Circulo Float | Cuadrado Float Float
86
+ data Figura = Circulo Float | Cuadrado Float Float deriving ( Show )
60
87
61
88
-- Este ejemplo define un tipo `Figura` con dos constructores,
62
89
-- cada uno con distinta cantidad de argumentos.
@@ -78,15 +105,27 @@ data Figura = Circulo Float | Cuadrado Float Float
78
105
data Animal = Perro String Int | Gato String
79
106
deriving (Eq , Ord , Show )
80
107
108
+ gatitou1 = Gato " miau1"
109
+ gatitou2 = Gato " miau2"
110
+
111
+ gatitou1_es_menor_que_gatitou2 = gatitou1 < gatitou2
112
+
113
+ gatitou1_es_igual_que_gatitou2 = gatitou1 == gatitou2
114
+
115
+ -- Si lo evaluamos en GHCi, vamos a ver que `gatito1_es_menor_que_gatitou2` es `True`
116
+ -- y `gatitou1_es_igual_que_gatitou2` es `False
117
+
118
+
81
119
-- ***********************************************
82
120
-- ****************** EJERCICIO 1 ****************
83
121
-- ***********************************************
84
122
--
85
123
-- Abrí en la consola de Haskell este archibo con `stack ghci src/Intro.hs`.
86
124
-- Ahora definí un animal y una persona en la consola.
87
125
-- > persona1 = Persona "nombre" 5
88
- -- Fijate como podés imprimir al animal y no a la persona.
126
+ -- > perrito = Perro "bobby" 7
89
127
-- ¿Cuál es mas grande, un perro llamado "bobby" de 7 años o un gato llamado "pepita"?
128
+ -- ¿Y dos perros con se llaman igual pero uno es mas grande?
90
129
--
91
130
-- ***********************************************
92
131
-- **************** FIN EJERCICIO 1 **************
@@ -122,11 +161,17 @@ nivelDeJugador1 = jugadorNivel jugador1
122
161
-- | | | | +----- "o"
123
162
-- | | | | | +--- Un constructor sin argumentos
124
163
-- V V V V V V
125
- data Opcional a = Algun a | Nada
164
+ data Opcional a = Algun a | Nada deriving ( Show )
126
165
127
166
-- Este tipo por ejemplo sería como el `Optional<T>` en Java,
128
167
-- puede "tener: un valor de tipo `a` o estar vacío.
129
168
169
+ unNumero = Algun 5
170
+
171
+ ningunString = Nada
172
+
173
+ -- ¿Cuál es el tipo de `unNumero`? ¿Y el de `ningunString`?
174
+
130
175
131
176
---------------------------------------------------------------------------
132
177
-- Valores y funciones --
@@ -140,7 +185,7 @@ vegeta = Persona "Vegeta" 9000
140
185
-- Si ponemos dos cosas juntas (o sea, separadas por un espacio),
141
186
-- Haskell va a intentar "aplicar" los valores de izquierda a derecha.
142
187
-- En este caso, como `Persona` es el constructor de tipo y es una función,
143
- -- le va a aplicar la String "Vegeta" y el Int 9001 .
188
+ -- le va a aplicar la String "Vegeta" y el Int 9000 .
144
189
145
190
-- También podemos anotar un valor con el tipo:
146
191
@@ -189,6 +234,7 @@ powerUp (Persona nombre poder) = Persona nombre (poder + 1)
189
234
-- ***********************************************
190
235
-- ¿Cómo obtendrías el nombre de una Persona?
191
236
-- ¿Y su edad?
237
+ -- ¿Cual es el poder de `goku` después de que le aplicamos `powerUp`?
192
238
-- Tip: en ghci (la consola de Haskell) podes ver el tipo de algo con:
193
239
-- > :t algo
194
240
@@ -202,17 +248,18 @@ getPoder = error "Escribime!"
202
248
-- **************** FIN EJERCICIO 2 **************
203
249
-- ***********************************************
204
250
205
- -- Las funciones que toman múltiples argumentos usan la misma flecha,
251
+ -- El tipo de las funciones que toman multiples argumentos usan la misma flecha,
206
252
-- siendo la última cosa lo que devuelve la función.
207
- -- Esta función absorbe el poder de una persona. El guión bajo en la segunda
208
- -- persona significa "no voy a usar este valor" y no les asignamos una variable.
253
+ -- Esta función absorbe el poder de una persona y se lo asigna a uno mismo.
254
+ -- El guión bajo en el segundo patrón significa "no voy a usar este valor"
255
+ -- y así no lo asignamos a una variable (total no lo necesitamos).
209
256
210
257
absorber :: Persona -> Persona -> Persona
211
258
absorber (Persona nombre poderAnterior) (Persona _ otroPoder) =
212
259
Persona nombre (poderAnterior + otroPoder)
213
260
214
- -- ¡Atención! ¿Qué pasa con la persona que le absorbimos el poder?
215
- -- ¿No tendríamos que volverlo a 0? Como Haskell es inmutable no podemos
261
+ -- ¡Atención! ¿Qué pasa con la persona que le absorvimos el poder?
262
+ -- ¿No tendríamos que hacerlo 0? Como Haskell es inmutable no podemos
216
263
-- modificarla, pero si podemos devolver las dos "nuevas" personas.
217
264
-- Para esto usamos una Tupla, que es básicamente un tipo que tiene
218
265
-- dos variables del mismo o distinto tipo.
@@ -247,18 +294,16 @@ absorber2 persona victima =
247
294
-- La misma función, con Jugador en vez de Persona y con `where` quedaría:
248
295
249
296
absorber3 :: Jugador -> Jugador -> (Jugador , Jugador )
250
- absorber3 persona victima =
251
- (nuevaPersona, victimaDrenada)
297
+ absorber3 persona victima = (nuevaPersona, victimaDrenada)
252
298
where
253
299
poderAbsorbido = jugadorNivel persona + jugadorNivel victima
254
300
nuevaPersona = Jugador (jugadorNombre persona) poderAbsorbido
255
301
victimaDrenada = Jugador (jugadorNombre victima) 0
256
302
257
303
-- Notemos que esta vez usamos los "getters" que nos regaló el record syntax.
258
304
--
259
- -- Otra funcionalidad poderosa de Haskell es el pattern matching. El mismo lo usamos
260
- -- para desconstruir Personas anteriormente. Pero también podemos hacer pattern matching
261
- -- con valores:
305
+ -- Cuando el tipo tiene varios constructores, también podemos hacer pattern matching según
306
+ -- ese constructor, ya que puede ser que tengan distinta forma (cantidad de argumentos).
262
307
263
308
calcularArea :: Figura -> Float
264
309
calcularArea (Circulo radio) = pi * radio ** 2
@@ -278,78 +323,28 @@ seLlamaBobby (Perro "bobby" _) = True
278
323
seLlamaBobby (Perro " BOBBY" _) = True
279
324
seLlamaBobby _ = False
280
325
281
- -- Pasemos a algo más interesante. Las listas o arrays en Haskell se definen así,
282
- -- siendo `:` el constructor de lista que toma un elemento y una lista
283
- -- y nos devuelve otra lista con el elemento al principio.
284
326
285
- unaListaDeEnteros :: [Int ]
286
- unaListaDeEnteros = [1 , 2 , 3 , 4 , 5 ] -- explicito
287
-
288
- otraListaDeEnteros :: [Int ]
289
- otraListaDeEnteros = [1 .. 5 ] -- generadores
290
-
291
- unaLista :: [Char ]
292
- unaLista = ' h' : ' o' : ' l' : ' a' : [] -- con el constructor
293
-
294
- -- Vieron que `:` va infijo, o sea se usa "entre" dos expresiones.
295
- -- Esto se llama función infija y son funciones definidas normalmente.
296
- -- Para haskell, las funciones cuyo nombre no empiece con una letra van a ser
297
- -- infijas, como `+`, `:` o `<>`, mientras que las demás van a ser prefijas, como
298
- -- `calcularArea` o `seLlamaBobby`.
299
-
300
- -- La librería standard de Haskell trae varias funciones para trabajar con listas,
301
- -- podemos ver la documentación aqui:
302
- -- https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-List.html
303
- -- También, en ghci podemos escribir `:browse Data.List` y nos va a mostrar todo lo que
304
- -- exporta ese módulo.
305
- -- Ahora repasemos algunas de sus operaciones mas comunes.
327
+ -- ***********************************************
328
+ -- ****************** EJERCICIO 3 ****************
329
+ -- ***********************************************
330
+ -- Ahora vamos a definir que los Jugadores son comparables y ordenables por su nivel.
331
+ -- Para esto podríamos hacer que el tipo sencillamente derive `Ord` y `Eq` como `Animal`,
332
+ -- pero esto compararía Jugadores por su nombre también, ya que por default las instancias
333
+ -- autogeneradas usan todos los elementos de un tipo para generarse.
306
334
--
307
- -- Podemos sumar listas
308
-
309
- ambasListas :: [Int ]
310
- ambasListas = unaListaDeEnteros <> otraListaDeEnteros
311
-
312
- -- darlas vuelta
313
-
314
- alReves :: [Int ]
315
- alReves = reverse ambasListas
335
+ -- El ejercicio consiste de hacer que Jugador implemente `Ord` y `Eq` pero solo comparando su poder.
316
336
317
- -- acceder a su primer elemento o a la "cola" (todos menos el primero)
337
+ instance Eq Jugador where
338
+ j1 == j2 = jugadorNivel j1 == jugadorNivel j2
318
339
319
- elPrimeroAlFinal :: [ Int ]
320
- elPrimeroAlFinal = tail unaListaDeEnteros <> [ head unaListaDeEnteros]
340
+ instance Ord Jugador where
341
+ j1 <= j2 = jugadorNivel j1 <= jugadorNivel j2
321
342
322
- -- "modificar" cada elemento
343
+ jugadoresOrdenados = sort [ Jugador " Paulina " 9 , Jugador " Marilinia " 2 , Jugador " Pepe " 5 , Jugador " Paulina " 3 ]
323
344
324
- unaListaMasUno :: [Int ]
325
- unaListaMasUno = map (+ 1 ) unaListaDeEnteros
326
-
327
- -- Acá estamos haciendo "aplicación parcial". Supongamos una función:
328
-
329
- sumar :: Int -> Int -> Int
330
- sumar a b = a + b
331
-
332
- -- si hacemos `(sumar 5)` nos devuelve una función de tipo
333
- -- `Int -> Int`
334
-
335
- sumarCinco :: Int -> Int
336
- sumarCinco = sumar 5
337
-
338
- doce :: Int
339
- doce = sumarCinco 7
340
-
341
- -- Decimos que estamos haciendo aplicación parcial cuando le pasamos menos parámetros
342
- -- a una función de los que espera recibir. Hacer esto devuelve una función diferente
343
- -- (y con diferente tipo) que espera recibir los que no le pasamos a la primera.
344
-
345
- -- ***********************************************
346
- -- ****************** EJERCICIO 3 ****************
347
- -- ***********************************************
348
- -- Dada una lista de jugadores, queremos subir el nivel de todos en un
349
- -- número dado `x`. Definir esa función.
345
+ -- Si implementamos correctamente estas funciones, la siguiente expresión deberia ser True:
350
346
351
- levelear :: Int -> [Jugador ] -> [Jugador ]
352
- levelear = error " Escribime!"
347
+ estanOrdenados = jugadoresOrdenados == [Jugador " Marilinia" 2 , Jugador " Paulina" 3 , Jugador " Pepe" 5 , Jugador " Paulina" 9 ]
353
348
354
349
-- ***********************************************
355
350
-- *************** FIN EJERCICIO 3 ***************
@@ -384,7 +379,7 @@ helloWorld = do
384
379
385
380
prompt :: IO String
386
381
prompt = do
387
- putStrLn " Enter a text plz:"
382
+ putStrLn " Enter text plz:"
388
383
line <- getLine
389
384
pure line
390
385
0 commit comments