¡Algunas explicaciones están en orden!
¿Para qué sirve la función id? ¿Cuál es el papel de? ¿Por qué deberíamos necesitarlo aquí?
id
es la función de identidad , id x = x
y se utiliza como el equivalente de cero cuando la construcción de una cadena de funciones con la composición de funciones , (.)
. Puede encontrarlo definido en el Preludio .
En el ejemplo anterior, ¿la función id es el acumulador en la función lambda?
El acumulador es una función que se crea mediante la aplicación de funciones repetidas. No hay lambda explícita, ya que nombramos el acumulador, step
. Puedes escribirlo con una lambda si quieres:
foldl f a bs = foldr (\b g x -> g (f x b)) id bs a
O como escribiría Graham Hutton :
5.1 El foldl
operador
Ahora generalicemos del suml
ejemplo y consideremos el operador estándar foldl
que procesa los elementos de una lista en orden de izquierda a derecha usando una función f
para combinar valores y un valorv
como valor inicial:
foldl :: (β → α → β) → β → ([α] → β)
foldl f v [ ] = v
foldl f v (x : xs) = foldl f (f v x) xs
Con este operador, suml
se puede redefinir simplemente con suml = foldl (+) 0
. Muchas otras funciones se pueden definir de forma sencilla utilizando foldl
. Por ejemplo, la función estándar se reverse
puede redefinir usando foldl
lo siguiente:
reverse :: [α] → [α]
reverse = foldl (λxs x → x : xs) [ ]
Esta definición es más eficiente que nuestra definición original usando fold, porque evita el uso del operador de adición ineficiente (++)
para listas.
Una simple generalización del cálculo en la sección anterior para la función suml
muestra cómo redefinir la función foldl
en términos de fold
:
foldl f v xs = fold (λx g → (λa → g (f a x))) id xs v
Por el contrario, no es posible redefinir fold
en términos de foldl
, debido al hecho de que
foldl
es estricto en la cola de su argumento de lista pero fold
no lo es. Hay una serie de 'teoremas de dualidad' útiles sobrefold
y foldl
, y también algunas pautas para decidir qué operador se adapta mejor a aplicaciones particulares (Bird, 1998).
El prototipo de foldr es foldr :: (a -> b -> b) -> b -> [a] -> b
Un programador de Haskell diría que el tipo de foldr
es(a -> b -> b) -> b -> [a] -> b
.
y el primer parámetro es una función que necesita dos parámetros, pero la función de paso en la implementación de myFoldl usa 3 parámetros, estoy completamente confundido
¡Esto es confuso y mágico! Jugamos una mala pasada y reemplazamos el acumulador con una función, que a su vez se aplica al valor inicial para obtener un resultado.
Graham Hutton explica el truco para convertirse foldl
en foldr
en el artículo anterior. Comenzamos por escribir una definición recursiva de foldl
:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v [] = v
foldl f v (x : xs) = foldl f (f v x) xs
Y luego refactorice a través de la transformación de argumento estático en f
:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v xs = g xs v
where
g [] v = v
g (x:xs) v = g xs (f v x)
Ahora reescribamos g
para flotar v
hacia adentro:
foldl f v xs = g xs v
where
g [] = \v -> v
g (x:xs) = \v -> g xs (f v x)
Que es lo mismo que pensar g
en función de un argumento, que devuelve una función:
foldl f v xs = g xs v
where
g [] = id
g (x:xs) = \v -> g xs (f v x)
Ahora tenemos g
una función que recorre recursivamente una lista, aplica alguna función f
. El valor final es la función de identidad, y cada paso también da como resultado una función.
Pero , ya tenemos a mano una función recursiva muy similar en las listas foldr
,!
2 El operador de plegado
El fold
operador tiene su origen en la teoría de la recursividad (Kleene, 1952), mientras que el uso de fold
como concepto central en un lenguaje de programación se remonta al operador de reducción de APL (Iverson, 1962), y más tarde al operador de inserción de FP (Backus , 1978). En Haskell, el fold
operador de listas se puede definir de la siguiente manera:
fold :: (α → β → β) → β → ([α] → β)
fold f v [ ] = v
fold f v (x : xs) = f x (fold f v xs)
Es decir, dada una función f
de tipo α → β → β
y un valor v
de tipo β
, la función
fold f v
procesa una lista de tipo [α]
para dar un valor de tipo β
reemplazando el constructor nil []
al final de la lista por el valor v
, y cada constructor cons (:)
dentro de la lista por la función f
. De esta manera, el fold
operador encapsula un patrón simple de recursividad para procesar listas, en el que los dos constructores de listas simplemente se reemplazan por otros valores y funciones. Varias funciones familiares en listas tienen una definición simple usando fold
.
Esto parece un esquema recursivo muy similar a nuestra g
función. Ahora el truco: usando toda la magia disponible (también conocida como Bird, Meertens y Malcolm) aplicamos una regla especial, la propiedad universal de fold , que es una equivalencia entre dos definiciones para una función g
que procesa listas, expresada como:
g [] = v
g (x:xs) = f x (g xs)
si y solo si
g = fold f v
Entonces, la propiedad universal de los pliegues establece que:
g = foldr k v
donde g
debe ser equivalente a las dos ecuaciones, para algunas k
y v
:
g [] = v
g (x:xs) = k x (g xs)
De nuestros diseños de pliegues anteriores, lo sabemos v == id
. Sin embargo, para la segunda ecuación, necesitamos calcular la definición de k
:
g (x:xs) = k x (g xs)
<=> g (x:xs) v = k x (g xs) v
<=> g xs (f v x) = k x (g xs) v
<= g' (f v x) = k x g' v
<=> k = \x g' -> (\a -> g' (f v x))
Lo cual, sustituyendo nuestras definiciones calculadas de k
y v
produce una definición de foldl como:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v xs =
foldr
(\x g -> (\a -> g (f v x)))
id
xs
v
El recursivo g
se reemplaza con el combinador plegable, y el acumulador se convierte en una función construida a través de una cadena de composiciones def
en cada elemento de la lista, en orden inverso (por lo que doblamos a la izquierda en lugar de a la derecha).
Esto definitivamente es algo avanzado, por lo que para comprender profundamente esta transformación, la propiedad universal de los pliegues , que hace posible la transformación, recomiendo el tutorial de Hutton, vinculado a continuación.
Referencias
step = curry $ uncurry (&) <<< (flip f) *** (.)