一对一读取所有文件时出现多个错误

时间:2019-05-19 09:44:59

标签: file haskell directory

我正在尝试学习Haskell。我想创建一个小程序,该程序从当前目录中获取文件列表,逐个读取它们并打印其第一行。我尝试过:

import System.Directory (getDirectoryContents)
import System.FilePath ((</>))

getAbsoluteDirContents :: String -> IO [FilePath]

getAbsoluteDirContents dir = do
  contents <- getDirectoryContents dir
  return $ map (dir </>) contents

readALine x = do 
  print $ "Filename: " ++ x
  f  <- hGetLine x
  print $ "First line: " ++ f 

main = do 
  files <- getAbsoluteDirContents "."
  print files
  map (readALine f) files

但是,此代码给出了以下详细错误:

$ ghc -o firstline.hsx firstline.hs
[1 of 1] Compiling Main             ( firstline.hs, firstline.o )

firstline.hs:13:9: error:
    • Variable not in scope: hGetLine :: [Char] -> IO [Char]
    • Perhaps you meant ‘getLine’ (imported from Prelude)

firstline.hs:19:3: error:
    • Couldn't match type ‘[]’ with ‘IO’
      Expected type: IO b
        Actual type: [b]
    • In a stmt of a 'do' block: map (readALine f) files
      In the expression:
        do { files <- getAbsoluteDirContents ".";
             print files;
             map (readALine f) files }
      In an equation for ‘main’:
          main
            = do { files <- getAbsoluteDirContents ".";
                   print files;
                   map (readALine f) files }

firstline.hs:19:8: error:
    • Couldn't match expected type ‘FilePath -> b’
                  with actual type ‘IO ()’
    • Possible cause: ‘readALine’ is applied to too many arguments
      In the first argument of ‘map’, namely ‘(readALine f)’
      In a stmt of a 'do' block: map (readALine f) files
      In the expression:
        do { files <- getAbsoluteDirContents ".";
             print files;
             map (readALine f) files }
    • Relevant bindings include
        main :: IO b (bound at firstline.hs:16:1)

firstline.hs:19:18: error: Variable not in scope: f :: [Char]

我认为主要问题是hGetLine功能不正确。我看到了此功能here

map (readALine f) files表达式也有问题。

问题出在哪里,如何解决?感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

您忘记了导入文件中的功能,例如:

import Data.Text.IO(hGetLine)

但是这里仍然存在很多错误,正弦hGetLine期望Handle不是文件路径。您需要先使用openFile :: FilePath -> IOMode -> IO Handle打开文件,并且在读取行之后需要使用hClose :: Handle -> IO ()关闭句柄。

在您的main中写:

map (readALine f) files

但此处未定义f。如果要应用readALine,请编写readALine。由于readALine的类型为String -> IO (),因此您应该使用mapM_ :: Monad m => (a -> m b) -> [a] -> m ()

最后,由于hGetLine :: Handler -> IO Text返回IO Text而不是IO String,因此您无法将"first line: " :: String连接到它,我们可以使用OverloadedStrings编译指示,这样"first line: "在这里将被解释为Text,并使用`(<>) :: Semigroup s => s -> s -> s来进行串联。

通常,最好也指定顶级功能的签名,尽管并非严格要求,但它会显示一些有关该功能期望作为输入以及其功能的信息。

{-# LANGUAGE OverloadedStrings #-}

import Data.Semigroup((<>))
import Data.Text.IO(hGetLine)

import System.Directory (getDirectoryContents)
import System.FilePath ((</>))
import System.IO(IOMode(ReadMode), openFile, hClose)

getAbsoluteDirContents :: String -> IO [FilePath]
getAbsoluteDirContents dir = do
  contents <- getDirectoryContents dir
  return $ map (dir </>) contents

readALine :: FilePath -> IO ()
readALine x = do 
  print $ "Filename: " ++ x
  h <- openFile x ReadMode
  f  <- hGetLine h
  hClose h
  print $ "First line: " <> f

main :: IO ()
main = do 
  files <- getAbsoluteDirContents "."
  print files
  mapM_ readALine files

话虽如此,该程序在语义上仍然无法正常运行,因为您的程序将旨在打开目录(也像...这样)。因此,您还需要实现适当的过滤机制以防止打开目录。您可以使用isFile :: FilePath -> IO Bool,我将其保留为练习。