Использование оператора do в Haskell - PullRequest
0 голосов
/ 25 апреля 2018

Наконец-то узнаем, как использовать монады в Haskell!

Я хочу прочитать файл testInput, удалить первую строку, применить функцию waffles к каждой другой строке и сохранить результат в видеfile output.txt.

Я написал следующий код:

main = do
    contents <- tail . fmap lines . readFile $ "testInput"
    result <- fmap waffles contents
    writeFile "output.txt" $ concat result

waffles row col = (row - 1)*(col - 1)

К сожалению, компилятор жалуется:

waffles.hs:3:41:
    Couldn't match type ‘IO String’ with ‘[String]’
    Expected type: FilePath -> [String]
      Actual type: FilePath -> IO String
    In the second argument of ‘(.)’, namely ‘readFile’
    In the second argument of ‘(.)’, namely ‘fmap lines . readFile’

waffles.hs:5:9:
    Couldn't match expected type ‘[b]’ with actual type ‘IO ()’
    Relevant bindings include program :: [b] (bound at waffles.hs:2:1)
    In a stmt of a 'do' block: writeFile "output.txt" $ concat result
    In the expression:
      do { contents <- tail . fmap lines . readFile $ "testInput";
           result <- fmap waffles contents;
           writeFile "output.txt" $ concat result }
    In an equation for ‘program’:
        program
          = do { contents <- tail . fmap lines . readFile $ "testInput";
                 result <- fmap waffles contents;
                 writeFile "output.txt" $ concat result }
Failed, modules loaded: none.

Я нахожу эту ошибку довольно пугающей.Можете ли вы помочь мне отладить его?

Я также был бы признателен за совет по стилю кода!

РЕДАКТИРОВАТЬ: я забыл разбить строки файла и преобразовать их в целые числа.Я попытался решить это следующим образом:

main = do
    contents <- tail . fmap lines . readFile $ "testInput"
    contents <- fmap read . words contents
    result <- fmap waffles contents
    writeFile "output.txt" $ concat result

waffles row col = (row - 1)*(col - 1)

Но это только привело к более запутанным ошибкам компилятора.

1 Ответ

0 голосов
/ 25 апреля 2018

Первая строка в вашем операторе do завершается неудачно, потому что вы пытаетесь использовать tail для IO [String].Вам нужно fmap функция tail:

contents <- fmap tail . fmap lines . readFile $ "testInput"
-- or....
contents <- fmap (tail . lines) . readFile $ "testInput"

Теперь вам нужен способ получить каждую вторую строку из contents.Вы можете определить простую everyOther функцию для этого:

everyOther :: [a] -> [a]
everyOther (x:_:xs) = x : everyOther xs
everyOther _        = []

И теперь вы можете добавить это в свой fmap в первой строке:

contents <- fmap (everyOther . tail . lines) . readFile $ "testInput"

Ваш wafflesФункция (row - 1)*(col - 1), похоже, не связана с тем, что, как я считаю, должно быть подписью типа.Попробуйте начать с подписи типа и построения waffles из их.Исходя из вашего описания, вы просто предоставляете каждую строку для функции, поэтому она должна иметь подпись:

waffles :: String -> String

Учитывая, что подпись типа для waffles, вы можете применить ее через:

let result = fmap waffles contents

Еще одна вещь на выходе: concat смоет все строки вместе.Вам, вероятно, нужны разрывы строк, поэтому вы можете использовать unlines.

main = do
    contents <- fmap (everyOther . tail . lines) . readFile $ "testInput"
    let result = fmap waffles contents
    writeFile "output.txt" $ unlines result
...