Как поднять найти, чтобы использовать -> IO Bool - PullRequest
2 голосов
/ 15 января 2012

Я в основном ищу find аналог filterM:

findM :: (a -> m Bool) -> [a] -> m (Maybe a)

, но я не смог написать его сам, используя find и какую-то функцию подъема.В настоящее время я делаю это через Data.Maybe.listToMaybe:

x <- filterM f list
return $ listToMaybe x

, что работает, но, похоже, я должен быть в состоянии сделать это напрямую с find.

Редактировать: Найдены некоторыематериал по взлому: Control.Monad.Loops.firstM и Control.Monad.TM.findM оба делают то, что я хочу

Ответы [ 2 ]

1 голос
/ 15 января 2012

Вот скучная прямая реализация:

findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a)
findM _ []     = return Nothing
findM f (x:xs) = do
    found <- f x
    if found
        then return $ Just x
        else findM f xs

Обратите внимание, что это findM не применяется f ни к каким предметам после найденного.Таким образом, он семантически отличается от реализации filterM, которую вы опубликовали.

Вы не можете написать это на основе find, не выходя из своего пути.У вас уже есть чистый список, но предикат (a -> m Bool) не соответствует (a -> Bool), необходимому для find.Если вы используете mapM, чтобы применить предикат к каждому элементу, все, что вы получите, это [Bool], который вам нужно будет сопоставить с исходным списком.

1 голос
/ 15 января 2012

Ну, вы могли бы написать это "с нуля", как

findM p ls =
    case ls of
         []   -> return Nothing
         x:xs -> do
            ok <- p x
            if ok
               then return (Just x)
               else findM p xs

хотя я понимаю, что это гораздо менее элегантно, чем то, что вы просите. Я не мог понять, как это сделать с помощью какой-то функции подъема. Мне кажется, что подъем должен произойти на рекурсивном шаге find, который скрыт внутри функции и недоступен для подъема. Хотя я бы хотел, чтобы кто-то доказал, что я не прав.

...