《Real World Haskell》笔记(7):I/O

2019-02-09  本文已影响0人  Mexplochin
经典I/O
--file callingpure.hs
name2reply::String->String
name2reply name="Hello "++name++"\n"++"Your name contains "++charcount++" characters."
  where charcount=show (length name)

main::IO ()
main=do
    putStrLn "What's your name?"
    inp<-getLine
    let out=name2reply inp
    putStrLn out

关于IO,

关于do代码块,

Pure vs Impure
Pure Impure
输入相同时总是产生相同结果 相同的参数可能产生不同的结果
不会有副作用 可能有副作用
不修改状态 可能修改程序、系统或者世界的全局状态
文件和句柄(Handle)
--file toupper-imp.hs
import System.IO
import Data.Char (toUpper)
main::IO ()
main=do
    inh<-openFile "input.txt" ReadMode--创建输入文件句柄
    outh<-openFile "output.txt" WriteMode--创建输出文件句柄
    mainloop inh outh
    hClose inh
    hClose outh--关闭文件句柄
 
mainloop::Handle->Handle->IO ()
mainloop inh outh=
    do ineof<-hIsEOF inh--检查是否在输入文件的结尾(EOF)
       if ineof
       then return ()
       else do inp<-hGetLine inh  
               hPutStrLn outh (map toUpper inp)
               mainloop inh outh
IOMode 可读 可写 开始位置 备注
ReadMode 文件开头 文件必须存在
WriteMode 文件开头 如果存在,文件内容会被完全清空
ReadWriteMode 文件开头 如果不存在会新建文件,如果存在不会损害原来的数据
AppendMode 文件结尾 如果不存在会新建文件,如果存在不会损害原来的数据
Seek and Tell

SeekMode的三个可选项:

System.IO 中预定义句柄

IO非句柄函数就是IO句柄函数的标准快捷方式,如getLine = hGetLine stdin

删除和重命名文件

System.Directory中,

临时文件

临时文件可以用来存储大量需要计算的数据或其他程序要使用的数据。

下面是一个函数式IO的例子,

--file tempfile.hs
import System.IO
import System.Directory(getTemporaryDirectory,removeFile)
import System.IO.Error(catchIOError)
import Control.Exception(finally)

main::IO ()
main=withTempFile "mytemp.txt" myAction

myAction::FilePath->Handle->IO ()
myAction tempname temph=do
    putStrLn "Welcome to tempfile.hs"
    putStrLn $ "I have a temporary file at "++tempname
    pos<-hTell temph
    putStrLn $ "My initial position is "++show pos

    let tempdata=show [1..10]
    putStrLn $ "Writing one line containing "++show (length tempdata)++" bytes: "++tempdata
    hPutStrLn temph tempdata
--hPutStrLn 总是在结束一行的时候在结尾处写上一个 \n
    pos<-hTell temph
    putStrLn $ "After writing, my new position is "++show pos
--Windows使用两个字节序列 \r\n 作为行结束标记 pos==24
    hSeek temph AbsoluteSeek 0
    putStrLn "The file content is:"
    c<-hGetContents temph
    putStrLn c
--数据使用 hPutStrLn写 c结尾处有一个换行符 putStrLn添加第二个换行符 结果会多显示一条空行
    putStrLn $ "Which could be expressed as this Haskell literal:"
    print c

withTempFile::String->(FilePath->Handle->IO a)->IO a
withTempFile pattern func=do
   tempdir<-catchIOError (getTemporaryDirectory) (\_->return ".")
   (tempfile,temph)<-openTempFile tempdir pattern

   finally (func tempfile temph) 
           (do hClose temph
               removeFile tempfile)
惰性IO处理方法
The IO Monad

在Haskell中,纯函数(Pure Function)指不会被外部所影响的纯粹计算过程,动作(Action)可以理解为在被调用时执行相应IO操作、造成磁盘或网络副作用的过程。

--file actions.hs
str2action :: String -> IO ()
str2action input = putStrLn ("Data: " ++ input)

list2actions :: [String] -> [IO ()]
list2actions = map str2action

numbers :: [Int]
numbers = [1..10]

strings :: [String]
strings = map show numbers

actions :: [IO ()]
actions = list2actions strings

printitall :: IO ()
printitall = runall actions
-- printall的操作在其他地方被求值的时候才会执行
--实际上,因为Haskell惰性求值,操作直到执行时才会被生成
runall :: [IO ()] -> IO ()
runall [] = return ()
runall (firstelem:remainingelems) =
    do firstelem
       runall remainingelems

main = do str2action "Start of the program"
          printitall
          str2action "Done!"

简化写法如下,

str2message :: String -> String
str2message input = "Data: " ++ input

str2action :: String -> IO ()
str2action = putStrLn . str2message

numbers :: [Int]
numbers = [1..10]

main = do str2action "Start of the program"
          mapM_ (str2action . show) numbers
          str2action "Done!"
-- mapM_ 在 numbers . show 每个元素上应用 (str2action . show) 
-- number . show 把每个数字转换成一个 String , str2action 把每个 String 转换成一个操作
-- mapM_ 把这些单独的操作组合成一个更大的操作,然后打印出这些行
-- map 是返回一个列表的纯函数,它不能执行操作
串联化(Sequencing)

do实际上是把操作连接在一起的快捷记号,可用于代替do的运算符有,

{-
main = do
       putStrLn "Greetings!  What is your name?"
       inpStr <- getLine
       putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!"
-}
main =
    putStrLn "Greetings!  What is your name?" >>
    getLine >>=
    (\inpStr -> putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!")
Return

在Haskell中, return 用来包装在Monad里面的数据。 在I/O时, return用来拿到纯数据并把它带入IO Monad。

import Data.Char(toUpper)

isYes :: String -> Bool
isYes inpStr = (toUpper . head $ inpStr) == 'Y'

isGreen :: IO Bool
isGreen =
    do putStrLn "Is green your favorite color?"
       inpStr <- getLine
       return (isYes inpStr)

在上例中,

--return 测试
returnTest :: IO ()
returnTest =
-- <-把东西从Monad里面拿出来,是 return 的反作用
    do one <- return 1
       let two = 2
       putStrLn $ show (one + two)
上一篇下一篇

猜你喜欢

热点阅读