Skip to content

Commit a0d618b

Browse files
committed
Sacando listas y agregando Show + ejercicio
1 parent a7c385d commit a0d618b

File tree

3 files changed

+84
-107
lines changed

3 files changed

+84
-107
lines changed

README.md

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,10 @@ funcional Haskell, es decir que el único pre-requisito para hacerlo es saber pr
99

1010
El taller consiste de dos partes:
1111

12-
1. Vamos a tener una introducción super veloz al lenguaje, su sintaxis y libreria estandard
12+
1. Vamos a tener una [introducción](#intro) super veloz al lenguaje, su sintaxis y libreria estandard
1313
siguiendo el archivo `Intro.hs` en la carpeta `src/`.
1414
2. Formando grupos de 2 o 3 (aunque no es obligatorio, recomendamos hacerlo de al menos dos) vamos a implementar
15-
un cliente super simple de linea de comandos para la API de [TheMovieDB](https://themoviedb.org).
16-
17-
Para empezar, queremos que si hacemos
18-
19-
```bash
20-
stack run buscar "la batalla de argelia"
21-
```
22-
23-
se consulte al endpoint de búsquedas documentado [aquí](https://developers.themoviedb.org/3/search/search-movies)
24-
y se imprima en pantalla la lista de peliculas que el mismo devuelve.
25-
26-
Luego, vamos a usar el [endpoint de recomendaciones](https://developers.themoviedb.org/3/movies/get-movie-recommendations)
27-
y queremos que al hacer
28-
29-
```bash
30-
stack run recomendar 917
31-
```
32-
33-
nos imprima las recomendaciones para la peli con ID `917`.
15+
un [cliente](#cliente) super simple de linea de comandos para la API de [TheMovieDB](https://themoviedb.org).
3416

3517
### API key
3618

haskell-workshop.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ build-type: Simple
1212
cabal-version: >=1.10
1313

1414
library
15-
ghc-options: -Wall -Wno-unused-imports -Wno-missing-signatures
15+
ghc-options: -Wall -Wno-unused-imports -Wno-missing-signatures -Wno-type-defaults
1616
hs-source-dirs: src
1717
exposed-modules: Pelis
1818
, Intro

src/Intro.hs

Lines changed: 81 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ module Intro where
2020
-- Eso significa que las declaraciones deben arrancar en la columna 0,
2121
-- y cualquier linea puede partirse siempre y cuando se indente un poco mas que la anterior.
2222

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+
2347
-- Siendo Haskell un lenguaje con tipado estático, uno de los principales elementos que vamos
2448
-- a definir son, justamente, tipos.
2549

@@ -30,12 +54,15 @@ module Intro where
3054
-- En otro lenguajes `=` suele significar asignación.
3155
-- En Haskell, `=` significa "está definido así".
3256

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)
3966

4067
-- Usamos `data` para definir un nuevo tipo de datos.
4168
-- Todos los tipos deben empezar con mayúscula, y el nombre de este tipo es `Persona`.
@@ -56,7 +83,7 @@ maria = Persona "Maria" 32
5683
-- | | | | +------------ Otro constructor
5784
-- | | | | | +--- Argumentos del segundo constructor
5885
-- V V V V V V
59-
data Figura = Circulo Float | Cuadrado Float Float
86+
data Figura = Circulo Float | Cuadrado Float Float deriving (Show)
6087

6188
-- Este ejemplo define un tipo `Figura` con dos constructores,
6289
-- cada uno con distinta cantidad de argumentos.
@@ -78,15 +105,27 @@ data Figura = Circulo Float | Cuadrado Float Float
78105
data Animal = Perro String Int | Gato String
79106
deriving (Eq, Ord, Show)
80107

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+
81119
-- ***********************************************
82120
-- ****************** EJERCICIO 1 ****************
83121
-- ***********************************************
84122
--
85123
-- Abrí en la consola de Haskell este archibo con `stack ghci src/Intro.hs`.
86124
-- Ahora definí un animal y una persona en la consola.
87125
-- > persona1 = Persona "nombre" 5
88-
-- Fijate como podés imprimir al animal y no a la persona.
126+
-- > perrito = Perro "bobby" 7
89127
-- ¿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?
90129
--
91130
-- ***********************************************
92131
-- **************** FIN EJERCICIO 1 **************
@@ -122,11 +161,17 @@ nivelDeJugador1 = jugadorNivel jugador1
122161
-- | | | | +----- "o"
123162
-- | | | | | +--- Un constructor sin argumentos
124163
-- V V V V V V
125-
data Opcional a = Algun a | Nada
164+
data Opcional a = Algun a | Nada deriving (Show)
126165

127166
-- Este tipo por ejemplo sería como el `Optional<T>` en Java,
128167
-- puede "tener: un valor de tipo `a` o estar vacío.
129168

169+
unNumero = Algun 5
170+
171+
ningunString = Nada
172+
173+
-- ¿Cuál es el tipo de `unNumero`? ¿Y el de `ningunString`?
174+
130175

131176
---------------------------------------------------------------------------
132177
-- Valores y funciones --
@@ -140,7 +185,7 @@ vegeta = Persona "Vegeta" 9000
140185
-- Si ponemos dos cosas juntas (o sea, separadas por un espacio),
141186
-- Haskell va a intentar "aplicar" los valores de izquierda a derecha.
142187
-- 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.
144189

145190
-- También podemos anotar un valor con el tipo:
146191

@@ -189,6 +234,7 @@ powerUp (Persona nombre poder) = Persona nombre (poder + 1)
189234
-- ***********************************************
190235
-- ¿Cómo obtendrías el nombre de una Persona?
191236
-- ¿Y su edad?
237+
-- ¿Cual es el poder de `goku` después de que le aplicamos `powerUp`?
192238
-- Tip: en ghci (la consola de Haskell) podes ver el tipo de algo con:
193239
-- > :t algo
194240

@@ -202,17 +248,18 @@ getPoder = error "Escribime!"
202248
-- **************** FIN EJERCICIO 2 **************
203249
-- ***********************************************
204250

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,
206252
-- 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).
209256

210257
absorber :: Persona -> Persona -> Persona
211258
absorber (Persona nombre poderAnterior) (Persona _ otroPoder) =
212259
Persona nombre (poderAnterior + otroPoder)
213260

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
216263
-- modificarla, pero si podemos devolver las dos "nuevas" personas.
217264
-- Para esto usamos una Tupla, que es básicamente un tipo que tiene
218265
-- dos variables del mismo o distinto tipo.
@@ -247,18 +294,16 @@ absorber2 persona victima =
247294
-- La misma función, con Jugador en vez de Persona y con `where` quedaría:
248295

249296
absorber3 :: Jugador -> Jugador -> (Jugador, Jugador)
250-
absorber3 persona victima =
251-
(nuevaPersona, victimaDrenada)
297+
absorber3 persona victima = (nuevaPersona, victimaDrenada)
252298
where
253299
poderAbsorbido = jugadorNivel persona + jugadorNivel victima
254300
nuevaPersona = Jugador (jugadorNombre persona) poderAbsorbido
255301
victimaDrenada = Jugador (jugadorNombre victima) 0
256302

257303
-- Notemos que esta vez usamos los "getters" que nos regaló el record syntax.
258304
--
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).
262307

263308
calcularArea :: Figura -> Float
264309
calcularArea (Circulo radio) = pi * radio ** 2
@@ -278,78 +323,28 @@ seLlamaBobby (Perro "bobby" _) = True
278323
seLlamaBobby (Perro "BOBBY" _) = True
279324
seLlamaBobby _ = False
280325

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.
284326

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.
306334
--
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.
316336

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
318339

319-
elPrimeroAlFinal :: [Int]
320-
elPrimeroAlFinal = tail unaListaDeEnteros <> [head unaListaDeEnteros]
340+
instance Ord Jugador where
341+
j1 <= j2 = jugadorNivel j1 <= jugadorNivel j2
321342

322-
-- "modificar" cada elemento
343+
jugadoresOrdenados = sort [Jugador "Paulina" 9, Jugador "Marilinia" 2, Jugador "Pepe" 5, Jugador "Paulina" 3]
323344

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:
350346

351-
levelear :: Int -> [Jugador] -> [Jugador]
352-
levelear = error "Escribime!"
347+
estanOrdenados = jugadoresOrdenados == [Jugador "Marilinia" 2, Jugador "Paulina" 3, Jugador "Pepe" 5, Jugador "Paulina" 9]
353348

354349
-- ***********************************************
355350
-- *************** FIN EJERCICIO 3 ***************
@@ -384,7 +379,7 @@ helloWorld = do
384379

385380
prompt :: IO String
386381
prompt = do
387-
putStrLn "Enter a text plz:"
382+
putStrLn "Enter text plz:"
388383
line <- getLine
389384
pure line
390385

0 commit comments

Comments
 (0)