лучший способ реализовать функцию, которая принимает [Maybe a] и возвращает Maybe [a]

Мне нужна функция, которая принимает список [Maybe a] в качестве входных данных, выбирает каждое значение, обрабатывает его и возвращает Maybe [a]. Я хочу вернуть Nothing, если в списке ввода есть Nothing.

func [Just 1,Just 2,Just 3,Just 4,Just 5] => this returns Just [1,2,3,4,5]
func [Just 1,Nothing,Just 3,Just 4,Just 5] => this returns Nothing

я написал это

func mlist = if elem Nothing mlist
             then Nothing
             else Just $ map (\(Just e) -> e) mlist

Это работает, но мне интересно, могу ли я сделать это лучше. Мне не нравится та часть, где я сначала делаю elem Nothing mlist и снова сопоставляю mlist.


person eii0000    schedule 20.09.2018    source источник
comment


Ответы (1)


Эта функция уже существует в sequence :: Monad m => [m a] -> m [a]< /strong> функция:

Prelude> import Control.Monad
Prelude Control.Monad> sequence [Just 3]
Just [3]
Prelude Control.Monad> sequence [Just 3, Nothing]
Nothing
Prelude Control.Monad> sequence [Just 3, Just 2]
Just [3,2]
Prelude Control.Monad> sequence [Just 1,Just 2,Just 3,Just 4,Just 5]
Just [1,2,3,4,5]
Prelude Control.Monad> sequence [Just 1,Nothing,Just 3,Just 4,Just 5]
Nothing

По сути, это просто mapM id :: (Monad m, Traversable t) => t (m a) -> m (t a), так как, например, для 3-списка он равен:

-- special case for 3 elements to demonstrate how it works
func3 [a, b, c] = do
    ya <- a
    yb <- b
    yc <- c
    return [ya, yb, yc]

или таким образом:

func3 [a, b, c] = a >>= \ya -> b >>= \yb -> c >>= yc -> return [ya, yb, yc]

(Здесь я использую особый случай, так как mapM вводит некоторые дополнительные функции, затрудняющие отслеживание)

Поскольку для Maybe Monad Maybe реализовано так:

instance Monad Maybe where
    return = Just
    Nothing >>= _ = Nothing
    (Just x) >>= f = f x

так что это означает, что с момента, когда один из элементов (a, b или c) равен Nothing, результатом будет Nothing, если все значения равны Justs, мы «соберем» их с помощью лямбда-выражения и в конечном итоге получим список с элементы.

Вы можете видеть список [] как обобщение Maybe (где Nothing — пустой список, а Just — одноэлементный список), и можно наблюдать такое же поведение:

Prelude Control.Monad> sequence [[1,2], [4,3]]
[[1,4],[1,3],[2,4],[2,3]]
Prelude Control.Monad> sequence [[1,2], [4,3], []]
[]

Здесь sequence создаст перекрестное произведение, но если один из списков, содержащих элементы одного из множества, к которому применяется перекрестное произведение, пуст, то и результат будет пустым.

person Willem Van Onsem    schedule 20.09.2018
comment
Спасибо Виллем!! Теперь я понимаю, что последовательность делает mapM id [Может быть] - person eii0000; 21.09.2018