Haskell 中的条件标准句柄重定向

conditional standard handle redirection in Haskell

我想读取一个文件,处理它,并将结果写入另一个文件;输入文件名将通过控制台参数提供,输出文件名从输入文件名生成。

问题是如果没有提供任何参数,我希望它透明地“故障转移”到 stdin/stdout;本质上,如果提供了文件名,我将 stdin/stdout 重定向到相应的文件名,这样无论是否提供文件名,我都可以透明地使用 interact

这是在多余的 else 中与虚拟输出一起破解的代码。这样做的正确、惯用的形式是什么?

它可能与 Control.Monad 的 whenguard 有关,正如在类似的问题,但也许有人已经写过了。

import System.IO
import Data.Char(toUpper)
import System.Environment
import GHC.IO.Handle

main :: IO ()
main = do
       args <- getArgs
       if(not $ null args) then
        do
           print $ "working with "++ (head args)
           finHandle <- openFile (head args) ReadMode --open the supplied input file
           hDuplicateTo finHandle stdin --bind stdin to finName's handle
           foutHandle <- openFile ((head args) ++ ".out") WriteMode --open the output file for writing
           hDuplicateTo foutHandle stdout --bind stdout to the outgoing file
        else print "working through stdin/redirect" --get to know 
        interact ((++) "Here you go---\n" . map toUpper)

这对我来说已经相当地道了。我有一个注意事项是避免 head,因为它是一个不安全的函数(它会引发运行时错误)。在这种情况下,通过使用 case 进行模式匹配很容易做到这一点。

main :: IO ()
main = do
  args <- getArgs
  case args of
    fname:_ -> do
      print $ "working with " ++ fname
      finHandle <- openFile fname ReadMode
      hDuplicateTo finHandle stdin
      foutHandle <- openFile (fname ++ ".out") WriteMode
      hDuplicateTo foutHandle stdout
    [] -> do
      print "working through stdin/redirect"
  interact ((++) "Here you go---\n" . map toUpper)

interact 没有什么特别之处 - 这是它的定义:

interact        ::  (String -> String) -> IO ()
interact f      =   do s <- getContents
                       putStr (f s)

这样的事情怎么样:

 import System.Environment
 import Data.Char

 main = do
   args <- getArgs
   let (reader, writer) =
        case args of
          []         -> (getContents, putStr)
          (path : _) -> let outpath = path ++ ".output"
                        in (readFile path, writeFile outpath)
   contents <- reader
   writer (process contents)

 process :: String -> String
 process = (++) "Here you go---\n" . map toUpper

根据命令行参数,我们将 readerwriter 设置为将读取输入并写入输出的 IO 操作。