tuplas tipos sobre listas funciones ejercicios ejemplos contador ciclos basico haskell combinators pointfree

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. =)