¿Cómo reescribo una función Haskell de dos argumentos para un estilo sin puntos?
pointfree (4)
y también preferir código libre de puntos donde sea posible.
No "donde sea posible", sino "donde mejora la legibilidad (o tiene otras ventajas manifiestas)".
Para liberar tu punto
agreeLen x y = length $ takeWhile (/(a,b) -> a == b) (zip x y)
Un primer paso sería mover el ($)
derecha y reemplazar el que tiene con un (.)
:
agreeLen x y = length . takeWhile (/(a,b) -> a == b) $ zip x y
Ahora, puedes moverlo aún más a la derecha:
agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y
y allí puedes cortar inmediatamente un argumento,
agreeLen x = length . takeWhile (uncurry (==)) . zip x
Luego puede volver a escribir eso como una aplicación de prefijo del operador de composición,
agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x)
y tu puedes escribir
f (gx)
como
f . g $ x
en general, aquí con
f = (.) (length . takeWhile (uncurry (==)))
g = zip
, dando
agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x
de la cual el argumento x
es fácilmente eliminado. Luego puede transformar la aplicación de prefijo de (.)
En una sección y obtener
agreeLen = ((length . takeWhile (uncurry (==))) .) . zip
Pero, eso es menos legible que el original, así que no recomiendo hacerlo excepto para practicar la transformación de expresiones en un estilo sin puntos.
Tengo la siguiente función en Haskell
agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile (/(a,b) -> a == b) (zip x y)
Estoy tratando de aprender a escribir Haskell ''idiomático'', que parece preferir usar .
y $
lugar de paréntesis, y también para preferir el código libre de puntos cuando sea posible. Parece que no puedo deshacerme de mencionar y
explícitamente. ¿Algunas ideas?
Creo que tendría el mismo problema con el punto libre de cualquier función de dos argumentos.
Por cierto, esto es solo en búsqueda de un buen código; no un poco de "uso de lo que sea necesario para que sea un ejercicio de tarea libre de puntos".
Gracias.
(Comentario añadido) Gracias por las respuestas. Me has convencido de que esta función no se beneficia de Pointfree. Y también me has dado algunos buenos ejemplos para practicar expresiones transformadoras. Todavía es difícil para mí, y parecen ser tan esenciales para Haskell como lo son para C.
Como se señaló en la excelente respuesta de Daniel, su problema es componer f
y g
cuando f
es un argumento g
dos. esto podría ser escrito f ??? g
f ??? g
con el operador correcto (y con una firma de tipo de (c -> d) -> (a -> b -> c) -> a -> b -> d
. Esto corresponde a (.).(.)
operador (ver there ) que a veces se define como .:
. En ese caso, su expresión se convierte en
length . takeWhile (uncurry (==)) .: zip
Si está acostumbrado al operador .:
, Esta versión gratuita de punto es perfectamente legible. También puedo usar (<$$$>) = fmap fmap fmap
y obtener
length . takeWhile (uncurry (==)) <$$$> zip
Otra solución concisa, sin puntos:
agreeLen = ((length . takeWhile id) .) . zipWith (==)
Equivalentemente:
agreeLen = (.) (length . takeWhile id) . zipWith (==)
También podrías usar:
agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y
Idiomatic Haskell es lo que es más fácil de leer, no necesariamente lo que no tiene puntos.