Перебор файлов с использованием Haskell - PullRequest
2 голосов
/ 20 декабря 2011

У меня есть функция Haskell, которая работает с одним файлом для создания карты.Я хочу перебрать все файлы в каталоге и применить эту функцию для создания одной карты.

Я пытаюсь подойти к этому следующим образом:

perFileFunc :: Int -> FilePath -> IO (Map.Map [Char] Double)

allFilesIn dir =  filter (/= "..")<$>(filter(/= ".")<$>(getDirectoryContents dir)

Это дает мне списоквсе имена файлов в каталоге, кроме.и ..

Теперь, когда я пытаюсь сделать

myFunc dir n = map (perFileFunc n) <$> allFilesIn dir

Он проверяет, но ничего не делает.Я ожидал список карт, к которым я присоединюсь, используя unionWith (+), возможно.

Похоже, это неправильный способ сделать это.

Ответы [ 2 ]

6 голосов
/ 20 декабря 2011

Ваш код не работает должным образом, потому что (<$>) предназначен для поднятия pure действий в монадический (фактически аппликативный) контекст, поэтому ваш myFunc dir n имеет тип IO [IO (Map.Map [Char] Double)]; действие ввода-вывода, которое при выполнении находит список файлов в каталоге и сопоставляет каждое из них с другим действием ввода-вывода, которое при выполнении создает желаемый Map без фактического выполнения какого-либо из них. Это, вероятно, не то, что вы хотите:)

Вы хотите выполнить функцию, возвращающую монадическое действие над каждым элементом списка, и возвращать список результирующих значений. Вот что mapM делает:

mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]

Итак, что вы действительно хотите:

myFunc dir n = allFilesIn dir >>= mapM (perFileFunc n)

Вы используете (>>=), потому что allFilesIn dir само является монадическим действием, и вы хотите передать его функции, ожидающей тип результата и возвращающей другое действие (в данном случае mapM).

Обратите внимание, что mapM отличается от map в том, что в IO (не каждая монада ведет себя так, как большинство, но делают), она выполнит каждое действие перед возвратом списка; это означает, что результат каждого действия должен все вместе помещаться в память, и вы не сможете обрабатывать результаты постепенно. Если вы хотите этого, вам понадобится что-то отличное от mapM, например, итерации.

2 голосов
/ 20 декабря 2011

Сложно понять, что такое Haskell, как распознать и составить действия ввода-вывода.Давайте посмотрим на некоторые типы подписей.

dir :: FilePath
allFilesIn :: FilePath -> IO [FilePath]
perFileFunc :: Int -> FilePath -> IO (Map.Map [Char] Double)    

Итак, вы сказали, что для myFunc:

я ожидал список карт

Итак, для этой функции вам нужна сигнатура типа

myFunc :: Int -> FilePath -> [Map.Map String Double]

Конечно, тип возвращаемого значения не может быть просто [Map.Map String Double], потому что нам нужно выполнить некоторый IO вДля оценки myFunc.Таким образом, учитывая Int и FilePath, мы фактически хотим, чтобы тип возвращаемого значения был IO action , который производит a [Map.Map String Double]:

myFunc :: Int -> FilePath -> IO [Map.Map String Double]

Теперь давайте посмотрим на действия ввода-вывода, которые мы будем составлять для создания этой функции.

allFilesIn dir :: IO [FilePath]
perFileFunc n  :: FilePath -> IO (Map.Map String Double)

perFileFunc на самом деле не является действием ввода-вывода, но является функциейэто, учитывая FilePath, производит действие ввода-вывода.Итак, давайте посмотрим ... если мы запустим действие allFilesIn, тогда мы сможем работать с этим списком и запускать perFileFunc n для каждого из его элементов.

myFunc dir n = do
  files <- allFilesIn dir
  ???

Так что же происходит в ???место?В нашем распоряжении [FilePath], так как мы использовали <- для запуска действия allFilesIn dir.И у нас есть функция, perFileFunc n :: FilePath -> IO (Map.Map String Double).И мы хотим, чтобы результат имел тип IO [Map.Map String Double].

Стоп ... Hoogle time!Обобщая имеющиеся у нас компоненты (a = FilePath, b = Map.Map String Double), мы hoogle для [a] -> (a -> IO b) -> IO [b] (делая вид, что мы еще не видели ответа ehird).И вот, mapM - это волшебное решение, которое мы искали!(или forM, то есть просто flip mapM)

myFunc dir n = do
  files <- allFilesIn dir
  mapM (perFileFunc n) files

Если вы откажетесь от этой записи do, вы обнаружите, что она сводится к ответу ehird:

myFunc dir n = allFilesIn dir >>= (\files -> mapM (perFileFunc n) files)
-- eta reduce (\x -> f x) ==> f
myFunc dir n = allFilesIn dir >>= mapM (perFileFunc n)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...