Autor Tema: Definir una función con foldl usando composición en Haskell

0 Usuarios y 1 Visitante están viendo este tema.

06 Mayo, 2022, 01:00 am
Leído 222 veces

manooooh

  • $$\Large \color{#9c57a6}\pi\,\pi\,\pi\,\pi\,\pi\,\pi$$
  • Mensajes: 3,881
  • País: ar
  • Karma: +1/-0
  • Sexo: Masculino
Hola!

Estoy teniendo problemas con la respuesta de este ejercicio de Haskell:

Se pide: Definir la función sumarSegun utilizando la función foldl, que dada una función y una lista devuelve la suma de aplicar la función a cada uno de los elementos.

Como ejemplos de retorno:

> sumarSegun id [1, 5, 7]
13
> sumarSegun length ["hola", "que", "tal"]
10



Entiendo la solución:

sumarSegun :: Num b => (a -> b) -> [a] -> b
sumarSegun unaFuncion unaLista = foldl unaFuncionMisteriosa 0 unaLista
  where unaFuncionMisteriosa semilla unValorDeLaLista = semilla + unaFuncion unValorDeLaLista

Sin embargo en lo personal no me llevo bien con los "where" ni los lambda, prefiero definir funciones auxiliares o si el parámetro es lo suficientemente expresivo, lo escribo ahí mismo. Por eso la otra solución alternativa, usando composición, es:

sumarSegun' :: Num b => (a -> b) -> [a] -> b
sumarSegun' unaFuncion unaLista = foldl ((. unaFuncion) . (+)) 0 unaLista

Ahí no entiendo cómo trabaja la función compuesta: (. unaFuncion) . (+)

Gracias!!
Saludos

06 Mayo, 2022, 08:31 am
Respuesta #1

geómetracat

  • Moderador Global
  • Mensajes: 3,328
  • País: es
  • Karma: +0/-0
  • Sexo: Masculino
Primero hay que entender que hace (. f). Si f es una función, de tipo f :: a -> b, entonces (.f) es de tipo (b -> c) -> (a->b), y lo que hace es que actúa sobre una función componiendo por la izquierda, es decir: (.f) g = g.f

Por otro lado, (+) es una función que actúa sobre un número y te devuelve la función "suma ese número", es decir, (+) 3 es una función que toma un número n y te devuelve n+3.

Ahora ya podemos entender (. unaFuncion) . (+). Supondremos que unaFuncion :: a->b. Si aplicamos esto a un número x, tenemos (.unaFuncion).(+) x = ((+) x).unaFuncion, y si ahora aplicamos este resultado a un objeto y que sea del tipo a, tenemos
((+) x).unaFuncion y = ((+) x) (unaFuncion y) = (unaFuncion y) + x
Por tanto, (.unaFuncion).(+) es lo mismo que unaFuncionMisteriosa.
La ecuación más bonita de las matemáticas: \( d^2=0 \)

10 Mayo, 2022, 02:52 am
Respuesta #2

manooooh

  • $$\Large \color{#9c57a6}\pi\,\pi\,\pi\,\pi\,\pi\,\pi$$
  • Mensajes: 3,881
  • País: ar
  • Karma: +1/-0
  • Sexo: Masculino
Hola

Primero hay que entender que hace (. f). Si f es una función, de tipo f :: a -> b, entonces (.f) es de tipo (b -> c) -> (a->b), y lo que hace es que actúa sobre una función componiendo por la izquierda, es decir: (.f) g = g.f

Por otro lado, (+) es una función que actúa sobre un número y te devuelve la función "suma ese número", es decir, (+) 3 es una función que toma un número n y te devuelve n+3.

Ahora ya podemos entender (. unaFuncion) . (+). Supondremos que unaFuncion :: a->b. Si aplicamos esto a un número x, tenemos (.unaFuncion).(+) x = ((+) x).unaFuncion, y si ahora aplicamos este resultado a un objeto y que sea del tipo a, tenemos
((+) x).unaFuncion y = ((+) x) (unaFuncion y) = (unaFuncion y) + x
Por tanto, (.unaFuncion).(+) es lo mismo que unaFuncionMisteriosa.

Muchas gracias!

Hace rato descubrí la evaluación perezosa o lazy evaluation que tiene Haskell; viene muy bien para trabajar con listas infinitas, o por ejemplo que esto no rompa:
> fst ("a", id)
"a"

;)

Saludos

P.D. Me da intriga saber por qué esto no rompe:
*Main> length (3,4)
1

pero si pregunto por el tipo de length:
> :t length
length :: Foldable t => t a -> Int

me dice que recibe por parámetro algo que se pueda "plegar" (una lista, por ejemplo, y no hay muchas otras cosas más). ¿Una tupla pertenece a la familia de tipos de las cosas que se puedan "plegar"?

10 Mayo, 2022, 08:13 am
Respuesta #3

geómetracat

  • Moderador Global
  • Mensajes: 3,328
  • País: es
  • Karma: +0/-0
  • Sexo: Masculino
Hace rato descubrí la evaluación perezosa o lazy evaluation que tiene Haskell; viene muy bien para trabajar con listas infinitas, o por ejemplo que esto no rompa:
> fst ("a", id)
"a"

;)
Sí, es muy útil. Esencial para trabajar con objetos infinitos, vamos.

Citar
P.D. Me da intriga saber por qué esto no rompe:
*Main> length (3,4)
1

pero si pregunto por el tipo de length:
> :t length
length :: Foldable t => t a -> Int

me dice que recibe por parámetro algo que se pueda "plegar" (una lista, por ejemplo, y no hay muchas otras cosas más). ¿Una tupla pertenece a la familia de tipos de las cosas que se puedan "plegar"?
Pues no tenía ni idea. Puedes mirar por aquí: https://stackoverflow.com/questions/36460833/why-does-length-return-1-for-a-tuple-with-2-elements-and-gives-an-error-for-a-t,
Básicamente parece que es algo bastante curioso: las \( 2 \)-tuplas son Foldable, pero las \( n \)-tuplas con \( n>2 \) no lo son. Además, en las \( 2 \)-tuplas cuando se usan funciones aplicables a Foldable se ignora la primera componente y se trabaja solo con la segunda.
No era consciente de esto y me parece tremendamente confuso, la verdad.
La ecuación más bonita de las matemáticas: \( d^2=0 \)