2011-12-17 22 views
16

estoy buscando una función que toma una función (a -> a -> a) y una lista de [Tal vez a] y devuelve Tal vez a. Hoogle no me dio nada útil. Esto parece un patrón bastante común, entonces estoy preguntando si hay una mejor práctica para este caso.Mejor práctica de cómo evaluar una lista de Maybes

>>> f (+) [Just 3, Just 3] 
Just 6 
>>> f (+) [Just 3, Just 3, Nothing] 
Nothing 

Gracias de antemano, Chris

+0

¿Qué tal un "producto de mónada"? – adamse

+2

No lo sé, es por eso que estoy preguntando. – Chris

+0

He ampliado mi respuesta para que coincida con su pregunta actualizada. – ehird

Respuesta

25

Usted debe primero gire el [Maybe a] en un Maybe [a] con todos los elementos Just (lo que supondría Nothing si alguno de ellos es Nothing). Esto se puede hacer usando sequence, utilizando lo mejor es ejemplo Mónada:

GHCi> sequence [Just 1, Just 2] 
Just [1,2] 
GHCi> sequence [Just 1, Just 2, Nothing] 
Nothing 

El definition of sequence es equivalente a la siguiente:

sequence [] = return [] 
sequence (m:ms) = do 
    x <- m 
    xs <- sequence ms 
    return (x:xs) 

para que podamos ampliar este último ejemplo, como:

do x <- Just 1 
    xs <- do 
     y <- Just 2 
     ys <- do 
      z <- Nothing 
      zs <- return [] 
      return (z:zs) 
     return (y:ys) 
    return (x:xs) 

Usando el do-notation expression of the monad laws, podemos reescribir esto de la siguiente manera:

do x <- Just 1 
    y <- Just 2 
    z <- Nothing 
    return [x, y, z] 

Si sabe cómo funciona Maybe Monada, ahora debe comprender cómo funciona sequence para lograr el comportamiento deseado. :)

A continuación, puede componer esto con foldr usando (<$>) (de Control.Applicative; equivalentemente, fmap o liftM) a veces su función binaria sobre la lista:

GHCi> foldl' (+) 0 <$> sequence [Just 1, Just 2] 
Just 3 

Por supuesto, se puede utilizar cualquier usted doblar desee, como foldr, etc. foldl1

como extra, si desea que el resultado sea Nothing cuando la lista está vacía, y así poder omitir el valor cero del pliegue sin worr ying acerca de los errores en las listas vacías, a continuación, puede utilizar esta función doble:

mfoldl1' :: (MonadPlus m) => (a -> a -> a) -> [a] -> m a 
mfoldl1' _ [] = mzero 
mfoldl1' f (x:xs) = return $ foldl' f x xs 

y lo mismo para foldr, foldl, etc. Tendrá que importar Control.Monad para esto.

Sin embargo, esto tiene que ser utilizado de forma ligeramente diferente:

GHCi> mfoldl1' (+) =<< sequence [Just 1, Just 2] 
Just 3 

o

GHCi> sequence [Just 1, Just 2] >>= mfoldl1' (+) 
Just 3 

Esto es porque, a diferencia de los otros pliegues, el tipo de resultado se ve como m a en lugar de a; es un vincular en lugar de un mapa.

14

Según tengo entendido, quiere obtener la suma de un grupo de maybes o Nothing si alguno de ellos es Nothing.Esto es en realidad bastante simple:

maybeSum = foldl1 (liftM2 (+)) 

Puede generalizar esto a algo como:

f :: Monad m => (a -> a -> a) -> [m a] -> m a 
f = foldl1 . liftM2 

Cuando se utiliza con el Maybe mónada, f funciona exactamente de la manera deseada.

Si se preocupan por listas vacías, puede utilizar esta versión:

f :: MonadPlus m => (a -> a -> a) -> [m a] -> m a 
f _ []  = mzero 
f fn (x:xs) = foldl (liftM2 fn) x xs 
+0

Buena solución, aunque prefiero la mía ya que también se puede usar fácilmente para no pliegues. Sin embargo, el tuyo es más simple si el único uso es plegable. – ehird

+1

Por otro lado, hay algo lindo alrededor de 1 seguido de 2. Pero tal vez la ternura no es la mejor medida de la calidad del código ... –

+0

Desafortunadamente, tengo la sensación de que el comportamiento deseado podría ser 'Nada' cuando la lista está vacío, en cuyo caso se pierde la yuxtaposición divertida :( – ehird

6

¿Qué pasa con algo tan simple como:

λ Prelude > fmap sum . sequence $ [Just 1, Just 2] 
Just 3 
λ Prelude > fmap sum . sequence $ [Just 1, Just 2, Nothing] 
Nothing 

O, mediante el uso de (+):

λ Prelude > fmap (foldr (+) 0) . sequence $ [Just 1, Just 2] 
Just 3 
λ Prelude > fmap (foldr (+) 0) . sequence $ [Just 1, Just 2, Nothing] 
Nothing 

Entonces, maybeSum = fmap sum . sequence.

+2

¡Me acabo de dar cuenta de que mi tercera respuesta ya contiene esta versión! Al leer la respuesta de ehird, tan pronto como vi la palabra "MonadPlus", pensé: "Debe haber una manera más fácil" y no me molesté en continuar: ¡Lo siento! De todos modos, he votado la respuesta de ehird. – lbolla

+1

Jaja, no es gran cosa :) Buen punto sobre 'MonadPlus', he intentado aclararlo. (Accidentalmente borré mi comentario anterior manteniendo presionada una tecla, uy ...) – ehird

Cuestiones relacionadas