tipos - if en haskell ejemplos
En Haskell ejecutando `y` y` o` para funciones booleanas (7)
Además de lo que dijo Don, las versiones de liftA2/liftM2
pueden no ser lo suficientemente flojas:
> let a .&&. b = liftA2 (&&) ab in pure False .&&. undefined
let a .&&. b = liftA2 (&&) ab in pure False .&&. undefined
*** Exception: Prelude.undefined
¡Woops!
Entonces, en su lugar, es posible que desee una función ligeramente diferente. Tenga en cuenta que esta nueva función requiere una restricción Monad
: la Applicative
es insuficiente.
> let a *&&* b = a >>= /a'' -> if a'' then b else return a'' in pure False *&&* undefined
False
Eso es mejor.
En cuanto a la respuesta que sugiere la función on
, esto es para cuando las funciones son las mismas pero los argumentos son diferentes. En su caso dado, las funciones son diferentes pero los argumentos son los mismos. Aquí está su ejemplo alterado para que on
sea una respuesta adecuada:
(fx) && (fy)
que se puede escribir:
on (&&) fxy
PD: los paréntesis son innecesarios.
Acabo de escribir las siguientes dos funciones:
fand :: (a -> Bool) -> (a -> Bool) -> a -> Bool
fand f1 f2 x = (f1 x) && (f2 x)
f_or :: (a -> Bool) -> (a -> Bool) -> a -> Bool
f_or f1 f2 x = (f1 x) || (f2 x)
Se pueden usar para combinar los valores de dos funciones booleanas, como:
import Text.ParserCombinators.Parsec
import Data.Char
nameChar = satisfy (isLetter `f_or` isDigit)
Después de observar estas dos funciones, me di cuenta de que son muy útiles. tanto que ahora sospecho que están incluidos en la biblioteca estándar, o que es más probable que exista una forma clara de hacerlo utilizando las funciones existentes.
¿Cuál fue la forma "correcta" de hacer esto?
Es más feo si siempre quieres dos funciones, pero creo que lo generalizaría:
mapAp fs x = map ($x) fs
fAnd fs = and . mapAp fs
fOr fs = or . mapAp fs
> fOr [(>2), (<0), (== 1.1)] 1.1
True
> fOr [(>2), (<0), (== 1.1)] 1.2
False
> fOr [(>2), (<0), (== 1.1)] 4
True
Esto ha sido mencionado, pero de una manera más compleja. Podría usar cosas aplicativas.
Para las funciones, básicamente, lo que hace es pasar el mismo argumento a una serie de funciones que puedes combinar al final.
Entonces podrías implementarlo así:
(&&) <$> aCheckOnA <*> anotherCheckOnA $ a
Para cada <*>
cadena, se obtiene otra función que se aplica a a y luego se junta toda la salida usando fmap
escrito alternativamente como <$>
. La razón por la que esto funciona con &&
es porque toma dos argumentos y tenemos 2 funciones juntas. Si hubiera una estrella adicional allí y otro cheque, tendría que escribir algo como:
(/a b c -> a && b && c) <$> aCheckOnA <*> anotherCheckOnA <*> ohNoNotAnotherCheckOnA $ a
mira esto para ver más ejemplos
Esto también se puede hacer usando Arrows :
import Control.Arrow ((&&&), (>>>), Arrow(..))
split_combine :: Arrow cat => cat (b, c) d -> cat a b -> cat a c -> cat a d
split_combine h f g = (f &&& g) >>> h
letter_or_digit = split_combine (uncurry (||)) isLetter isDigit
&&&
(no relacionado con &&
) divide la entrada; >>>
es composición de flecha / categoría.
Aquí hay un ejemplo:
> map letter_or_digit "aQ_%8"
[True,True,False,False,True]
Esto funciona porque las funciones - ->
- son instancias de Categoría y Flecha. La comparación de las firmas de tipo con los liftA2
de liftA2
y liftM2
Don muestra las similitudes:
> :t split_combine
split_combine :: Arrow cat => cat (b, c) d -> cat a b -> cat a c -> cat a d
> :t liftA2
liftA2 :: Applicative f => (b -> c -> d) -> f b -> f c -> f d
Además del currying, tenga en cuenta que casi puede convertir el primer tipo en el segundo sustituyendo cat a ---> f
y Arrow ---> Applicative
(la otra diferencia es que split_combine
no se limita a tomar funciones puras en su 1ra. argumento, aunque probablemente no sea importante).
Si f1 y f2 son lo mismo, puedes usar ''on'':
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
en base Data.Function
fand1 f = (&&) `on` f
for1 f = (||) `on` f
Uso típico:
Data.List.sortBy (compare `on` fst)
(de Hoogle )
Una simplificación,
f_and = liftM2 (&&)
f_or = liftM2 (||)
o
= liftA2 (&&)
= liftA2 (||)
en el ((->) r)
aplicativo.
Versión aplicativa
¿Por qué? Tenemos:
instance Applicative ((->) a) where
(<*>) f g x = f x (g x)
liftA2 f a b = f <$> a <*> b
(<$>) = fmap
instance Functor ((->) r) where
fmap = (.)
Asi que:
/f g -> liftA2 (&&) f g
= /f g -> (&&) <$> f <*> g -- defn of liftA2
= /f g -> ((&&) . f) <*> g -- defn of <$>
= /f g x -> (((&&) . f) x) (g x) -- defn of <*> - (.) f g = /x -> f (g x)
= /f g x -> ((&&) (f x)) (g x) -- defn of (.)
= /f g x -> (f x) && (g x) -- infix (&&)
Versión de Monad
O para liftM2
, tenemos:
instance Monad ((->) r) where
return = const
f >>= k = / r -> k (f r) r
asi que:
/f g -> liftM2 (&&) f g
= /f g -> do { x1 <- f; x2 <- g; return ((&&) x1 x2) } -- defn of liftM2
= /f g -> f >>= /x1 -> g >>= /x2 -> return ((&&) x1 x2) -- by do notation
= /f g -> (/r -> (/x1 -> g >>= /x2 -> return ((&&) x1 x2)) (f r) r) -- defn of (>>=)
= /f g -> (/r -> (/x1 -> g >>= /x2 -> const ((&&) x1 x2)) (f r) r) -- defn of return
= /f g -> (/r -> (/x1 ->
(/r -> (/x2 -> const ((&&) x1 x2)) (g r) r)) (f r) r) -- defn of (>>=)
= /f g x -> (/r -> (/x2 -> const ((&&) (f x) x2)) (g r) r) x -- beta reduce
= /f g x -> (/x2 -> const ((&&) (f x) x2)) (g x) x -- beta reduce
= /f g x -> const ((&&) (f x) (g x)) x -- beta reduce
= /f g x -> ((&&) (f x) (g x)) -- defn of const
= /f g x -> (f x) && (g x) -- inline (&&)
Totalmente estafando a TomMD, vi el and . map
and . map
y or . map
or . map
y no pudo evitar querer modificarlo:
fAnd fs x = all ($x) fs
fOr fs x = any ($x) fs
Estos leen muy bien, creo. fAnd
: ¿todas las funciones en la lista son True
cuando x
se aplica a ellas? fOr
: ¿hay alguna función en la lista True
cuando x
se aplica a ellas?
ghci> fAnd [even, odd] 3
False
ghci> fOr [even, odd] 3
True
Aunque es una extraña elección de nombre. Sin duda, es una buena opción para lanzar esos imperativos programadores. =)