#haskell
#хаскелл
Вопрос:
У меня есть функция, которая считывает содержимое файла и пытается проанализировать его Int
.
readIntFromFile :: String -gt; IO (Maybe Int) readIntFromFile path = do s lt;- readFile path return $ readMaybe s
Допустим, я несколько раз вызывал эту функцию и в итоге получил [IO (Maybe Int)]
. Имея этот список, я теперь хочу отфильтровать Nothing
значения, чтобы список содержал только успешно проанализированные Int
значения. Чтобы это произошло, мне нужна функция, которая выглядит так fltr :: [IO (Maybe Int)] -gt; [IO Int]
. И вот здесь я застрял.
Я могу использовать sequence :: (Traversable t, Monad m) =gt; t (m a) -gt; m (t a)
, чтобы сделать IO [Maybe Int]
из [IO (Maybe Int)]
, а затем функция фильтрации будет:
fltr2 :: IO [Maybe Int] -gt; IO [Int] fltr2 k = k gt;gt;= onlyJust where onlyJust xl = return [y | Just y lt;- xl]
К сожалению, я не думаю fltr2
, что всегда можно использовать вместо оригинала fltr
. Что делать, если один из файлов, с которыми я читаю, readIntFromFile
не существует?
Я новичок в Хаскелле и подозреваю, что делаю здесь что-то в корне неправильное. Есть ли какой-либо способ реализовать фильтрацию через [IO a]
(в моем случае [IO (Maybe Int)]
)? И если можно обобщить, каким должен быть подход к спискам фильтров (Monad m) =gt; [m a]
?
Ответ №1:
Мое первое замечание будет заключаться [IO (Maybe Int)]
в том, что в большинстве случаев это не очень полезный тип, и, как правило, вы предпочитаете заканчивать с IO [Maybe Int]
ним . Предположим, что у вас есть [String]
где-то, где вы сопоставляете эту функцию, вы можете использовать traverse
(или ее более старый синоним mapM
), чтобы получить более полезный тип:
map readIntFromFile fileNames :: [IO (Maybe Int)] traverse readIntFromFile fileNames :: IO [Maybe Int]
Сделав это, вы можете использовать fmap
его для доступа IO
и выполнения операций с его содержимым. В этом случае вам нужна функция типа [Maybe Int]
-gt; gt; [Int]
. Если мы поищем на Хугле [a]» rel=»nofollow noreferrer»> [Maybe a] -gt; [a]
, то сразу найдем catMaybes
. Итак, fmap catMaybes
имеет нужный вам тип, и, таким образом,
fmap catMaybes . traverse readIntFromFile $ fileNames :: IO [Int]
Комментарии:
1. Спасибо вам за ответ. Еще одна вещь, которую я четко не описал в своем первоначальном вопросе, — это когда обрабатывать ошибки ввода-вывода? Например, что делать, если один из файлов, с которыми мы читаем,
readIntFromFile
не существует, и мы не хотим останавливать всю операцию? Единственное, что приходит на ум, — это переключитьсяreadIntFromFile
на что-то подобноеreadIntFromFile2 :: String -gt; IO (Either SomeException (Maybe Int))
и обработать исключение внутриfmap
. Есть ли в этом какой-то смысл? Или есть лучший подход?