simbolos - pattern matching haskell
Aplicando mĂșltiples funciones al mismo valor de estilo sin puntos en Haskell (4)
Un día me aburrí y quería ejercitar mi cerebro, así que decidí resolver los 99 problemas de Haskell, pero me limité a hacerlo en un estilo sin puntos. Un problema que parece surgir mucho cuando estoy haciendo cosas en un estilo sin puntos es el siguiente: ¿cómo se aplican varias funciones al mismo valor y se mantiene cada resultado como una entidad independiente? Usando notación apuntada:
foobar x = [id x, reverse x]
Y lo que he encontrado hasta ahora en notación sin puntos:
foobar'' = `map` [id, reverse] ($ x)
Parece que no puedo sacar esa x
al final de allí.
Hay algunos combinadores idiomáticos básicos que aparecen repetidamente y se reimplementan con varios conceptos y bibliotecas superiores, pero que son esencialmente muy simples. Los nombres pueden variar, y algunos son implementables en términos de otros:
fork (f,g) x = (f x, g x) -- == (f &&& g)
prod (f,g) x = (f $ fst x, g $ snd x) -- == (f *** g)
pmap f (x,y) = (f x, f y) -- == (f *** f)
dup x = (x,x)
Por supuesto, uncurry f (x,y) == fxy
se usa mucho con estos también.
&&&
y ***
se definen en Control.Arrow
, así como first
y second
. Luego prod (f,id) == first f
, prod(id,g) == second g
etc. etc.
Así se convierte tu foobar
foobar = (/(a,b)->[a,b]) . fork (id,reverse)
= (/(a,b)->[a,b]) . (id &&& reverse)
= (/(a,b)->[a,b]) . (id *** reverse) . dup
= join $ curry ( (/(a,b)->[a,b]) . second reverse)
Para el último también necesita importar Control.Monad
y Control.Monad.Instances
. Véase también esta pregunta .
edición tardía: también, usando Control.Applicative
como se indica en la respuesta de ertes ,
= (:) <*> ((:[]) . reverse)
Le interesará la instancia de Applicative
de la mónada lectora:
instance Applicative (e ->)
Usándolo puedes distribuir fácilmente un argumento:
liftA2 (+) sin cos 3
Aquí, sin
y cos
son funciones, las cuales reciben el valor 3. Los resultados individuales se combinan usando (+)
. Puede combinar esto aún más con la instancia de Category
de (->)
, pero las versiones especializadas de cource de (.)
Y id
ya están definidas en Prelude
.
Antecedentes: la instancia de Applicative
para (e ->)
realmente representa el cálculo de SKI, donde (<*>)
es el combinador S y pure
es el combinator K. S se usa precisamente para distribuir un argumento a dos funciones:
S f g x = f x (g x)
Toma una aplicación de función ( fg ) y hace que ambos dependan del valor x ( (fx) (gx) ).
Otros ya han publicado cómo puede hacerlo utilizando la mónada Reader
, pero esa no es la única manera. Resulta que tu segunda función está bastante cerca. Creo que querías publicar
foobar'' x = (`map` [id, reverse]) ($ x)
Como la x
ya está cerca de la posición más a la derecha, ya casi estás allí. Primero, transforme la sección ($ x)
en una función, porque es un poco más fácil trabajar con:
-- by the definition of a right operator section
foobar''2 x = (`map` [id, reverse]) (/y -> ($) y x)
Luego elimine la x
del cuerpo lambda poniendo una nueva variable en su alcance y aplicando la función a x
-- lambda abstraction I think...
foobar''2 x = (`map` [id, reverse]) $ (/z y -> ($) y z) x
Reescriba esta aplicación como una composición de función, y luego puede eta reducir:
-- by definition of ''.''
foobar''3 x = (`map` [id, reverse]) . (/z y -> ($) y z) $ x
-- eta reduction
foobar''4 = (`map` [id, reverse]) . (/z y -> ($) y z)
Finalmente, note que podemos reemplazar la lambda con una función.
-- by definition of `flip`
foobar''5 = (`map` [id,reverse]) . flip ($)
y tienes una forma libre de puntos.
Usa sequence :
> let foobar'' = sequence [id, reverse]
> foobar'' "abcde"
["abcde","edcba"]