成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

《Haskell趣學(xué)指南》筆記之I/O

EsgynChina / 922人閱讀

摘要:生成隨機(jī)數(shù)對(duì)于一個(gè)函數(shù),如果兩次調(diào)用它時(shí)使用相同的參數(shù),它會(huì)把同樣的結(jié)果返回兩次。但是,這也使得產(chǎn)生隨機(jī)數(shù)這件事變成困難。對(duì)于同一個(gè)生成器,得到的隨機(jī)數(shù)是固定的。

系列文章

《Haskell趣學(xué)指南》筆記之基本語法

《Haskell趣學(xué)指南》筆記之類型(type)

《Haskell趣學(xué)指南》筆記之函數(shù)

《Haskell趣學(xué)指南》筆記之高階函數(shù)

《Haskell趣學(xué)指南》筆記之模塊

《Haskell趣學(xué)指南》筆記之自定義類型

《Haskell趣學(xué)指南》筆記之I/O


Hello World

    在 helloworld.hs 里寫入 main = putStrLn "hello, world!

    運(yùn)行 ghc --make helloworld

    運(yùn)行 ./helloworld

    得到輸出 hello, world!

然后來看看函數(shù)的類型

ghci> :t putStrLn 
putStrLn :: String -> IO () 
ghci> :t putStrLn "hello, world" 
putStrLn "hello, world" :: IO () 
ghci> :k IO
IO :: * -> *
ghci> :k IO()
IO() :: *

IO () 返回的類型為 (),即空元組,也叫單元。下一節(jié)會(huì)出現(xiàn)的 IO String 返回的類型為 String。

() 即使一個(gè)類型,也是對(duì)應(yīng)類型的值。

do 語法

do 語法可以將多個(gè) I/O 操作合成一個(gè)。

main = do   
    putStrLn "Hello, what" s your name");   
    name <- getLine   
    putStrLn ("Hey " ++ name ++ ", you rock!") 

getLine 的類型為 IO String

getLine 是一個(gè)產(chǎn)生字符串的 I/O 操作

注意 name <- 并沒有寫成 name =

只有在 I/O 操作的上下文中才能讀取 I/O 操作的內(nèi)容,這就是 Haskell 隔離『純代碼』和『不純的代碼』的方式

do語法會(huì)自動(dòng)將最后一個(gè)操作的值作為自己的返回值

IO String 與 String 的區(qū)別
nameTag = "Hello, my name is " ++ getLine 

++ 左邊的類型是 String,右邊的類型為 IO String,所以上面的代碼會(huì)報(bào)錯(cuò)。必須通過 name <- getLine 取出這個(gè) String,才能繼續(xù)。只能在不純的環(huán)境中處理不純的數(shù)據(jù)。不然不純的代碼會(huì)像污水那樣污染其余的代碼,保持 I/ O 相關(guān)的代碼盡可能小,這對(duì)我們的健康有好處。

myLine = getLine

如果代碼寫成了這樣

myLine = getLine 

這只是給 getLine 增加了一個(gè)別名!從 I/O 操作獲取值的唯一方法是使用 <- 。每次我們?cè)?GHCi 中按下回車鍵,GHCi 都會(huì)對(duì)返回值應(yīng)用 show,再將生成的字符串交給 putStrLn,從而輸出到終端。

return 是不一樣的

Haskell 的 return 跟其他語言不一樣,return 能夠基于一個(gè)純的值來構(gòu)造 I/O 操作。而且 return 不會(huì)中斷代碼。所以,在 I/O 上下文中,return "hi" 的類型就是 IO String。那么將一個(gè)純值轉(zhuǎn)換為一個(gè)什么都不做的 I/ O 操作又有什么意義呢?作用之一是 return () 能夠構(gòu)建一個(gè)什么都不做的 I/O 操作。

幾個(gè) I/O 函數(shù)

putStr

putStr

print (相當(dāng)于 putStrLn . show)

when $ do IO操作

sequence [getLine getLine]<- 取多個(gè) I/O 操作的結(jié)果組成一個(gè)列表

mapM print [1, 2, 3] 等價(jià)于 sequence $ map print [1, 2, 3]

mapM_ 則是不保留返回值版本的 mapM

forever $ do IO操作

forM 則是把 mapM 的參數(shù)位置對(duì)換,某些時(shí)候比較方便

getContents 從標(biāo)準(zhǔn)輸入里讀取所有的東西直到遇到一個(gè) end-of-file 字符,而且 getContents 是惰性的

interact fn 取一個(gè)類型為 String -> String 的函數(shù) fn 作為參數(shù),返回這樣一個(gè) I/ O 操作:接受輸入,把一個(gè)函數(shù)作用在輸入上,然后輸出函數(shù)運(yùn)行結(jié)果

getArgs 獲取命令行參數(shù)

getProgName 獲取程序名

讀入文件流是隨著時(shí)間連續(xù)地進(jìn)入、離開程序的一組數(shù)據(jù)片。

    創(chuàng)建文件 test.txt,內(nèi)容如下

     Hi! How are you");

    創(chuàng)建文件 capslocker.hs,內(nèi)容如下

     import Control.Monad 
     import Data.Char 
     main = forever $ do
          l <- getLine
          putStrLn $ map toUpper l 
    

    編譯:運(yùn)行 ghc --make capslocker

    將 test.txt 的內(nèi)容傳給 capslocker:運(yùn)行 ./capslocker < test.txt

    然后你就會(huì)看到所有字母變成了大寫上面代碼也能用 getContents 簡(jiǎn)化

import Data.Char 
main = do
    contents <- getContents
    putStr $ map toUpper contents 

也可以不傳入 test.txt 文件,直接運(yùn)行 ./capslocker,然后輸入一行行文本,但是注意,最終你要按 Ctrl+D 來表示內(nèi)容結(jié)束。

stdout 和 stdin

一種理解從終端讀入數(shù)據(jù)的方式是設(shè)想我們?cè)谧x取一個(gè)文件。輸出到終端也可以同樣理解——它就像在寫文件。我們可以把這兩個(gè)文件叫做 stdout 和 stdin,分別表示標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)輸入。

用 openFile 打開文件

    創(chuàng)建 gf.txt,內(nèi)容如下:

     Hey! Hey! You! You! 
     I don" t like your girlfriend! 
     No way! No way! 
     I think you need a new one! 
    

    創(chuàng)建 gf.hs,內(nèi)容如下:

     import System.IO 
     main = do
         handle <- openFile "gf. txt" ReadMode
         contents <- hGetContents handle
         putStr contents
         hClose handle
     -- 注意 openFile ReadMode hGetContents hClose 這幾個(gè)函數(shù),其中的 h 前綴表示它接收 handle
    

    編譯并運(yùn)行如何知道 openFile 的各個(gè)參數(shù)的意思呢?

λ> :t openFile
openFile :: FilePath -> IOMode -> IO Handle
λ> :info FilePath
type FilePath = String  -- Defined in ‘GHC.IO’λ> :info IOMode
data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
        -- Defined in ‘GHC.IO.IOMode’λ> :info Handle
data Handle
  = GHC.IO.Handle.Types.FileHandle FilePath...
用 withFile 打開文件
import System.IO 
main = do
     withFile "girlfriend.txt" ReadMode (handle -> do
         contents <- hGetContents handle
         putStr contents) 
bracket 函數(shù)
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c 

怎么用

bracket (openFile name mode)-- 打開文件
    (handle -> hClose handle) -- 失敗了怎么辦
    (handle -> fn handle)         -- 成功了怎么辦

用 bracket 很容易實(shí)現(xiàn) withFile。

生成隨機(jī)數(shù)對(duì)于一個(gè)函數(shù),如果兩次調(diào)用它時(shí)使用相同的參數(shù),它會(huì)把同樣的結(jié)果返回兩次。這很酷,因?yàn)樗屛覀兡芨玫乩斫獬绦?,它還讓我們能夠延遲求值。但是,這也使得產(chǎn)生隨機(jī)數(shù)這件事變成困難。
random :: (RandomGen g, Random a) => g -> (a, g) 

random 接受一個(gè)隨機(jī)數(shù)生成器,返回一個(gè)隨機(jī)數(shù)和一個(gè)新的隨機(jī)數(shù)生成器。然后你可以用新的生成器再去生成一個(gè)新的隨機(jī)數(shù)和一個(gè)新的生成器。以此類推。對(duì)于同一個(gè)生成器,得到的隨機(jī)數(shù)是固定的。

ghci>import System.Random
ghci> random (mkStdGen 100) :: (Int, StdGen) 
(-1352021624, 651872571 1655838864) 
ghci> random (mkStdGen 100) :: (Int, StdGen) 
(-1352021624, 651872571 1655838864)
ghci> random (mkStdGen 949494) :: (Int, StdGen) 
(539963926, 466647808 1655838864)
ghci> random (mkStdGen 949488) :: (Float, StdGen) 
(0. 8938442, 1597344447 1655838864) 
ghci> random (mkStdGen 949488) :: (Bool, StdGen) 
(False, 1485632275 40692) 
ghci> random (mkStdGen 949488) :: (Integer, StdGen) 
(1691547873, 1597344447 1655838864) 

randoms 接受一個(gè)生成器,返回一個(gè)無限長(zhǎng)的隨機(jī)值列表

ghci> take 5 $ randoms (mkStdGen 11) :: [Int] 
[-1807975507, 545074951,- 1015194702,- 1622477312,- 502893664] 

并沒有返回一個(gè)新的生成器,因?yàn)檫@個(gè)生成器在列表的末尾,而這個(gè)列表是無限長(zhǎng)的……

randomR 在一個(gè)范圍內(nèi)生成隨機(jī)數(shù)

ghci> randomR (1, 6) (mkStdGen 359353) 
(6, 149428957840692) 
getStdGen

之前我們每次生成隨機(jī)數(shù)都要自己先寫一個(gè)數(shù)字,這很傻……所以 System.Random 提供了 getStdGen,它會(huì)向系統(tǒng)索要初始的全局生成器。但 getStdGen 是一個(gè) IO 操作,它返回的類型是 IO stdGen。

import System.Random 
main = do
    gen <- getStdGen
    putStr $ take 20 (randomRs ("a"," z") gen) 

但是如果你調(diào)用 getStdGen 兩次,你會(huì)獲得同一個(gè) gen。第二次應(yīng)該使用 newStdGen。這個(gè)函數(shù)除了返回一個(gè) IO stdGen 類型的值,還會(huì)把全局生成器給更新了。你再調(diào)用 getStdGen 就能得到不同的隨機(jī)數(shù)這時(shí)你會(huì)疑惑,getStdGen 為什么能返回不同的結(jié)果呢?因?yàn)樗且粋€(gè) I/O 操作!

import System.Random

main = do
  gen <- getStdGen
  let a = fst ((random gen) :: (Int, StdGen))
  print a
  gen" <- newStdGen
  let b = fst ((random gen") :: (Int, StdGen))
  print b
  gen"" <- getStdGen
  let c = fst ((random gen"") :: (Int, StdGen))
  print c
  print "end"

這是我瞎寫的代碼。

字節(jié)串 bytestring

形如[ 1, 2, 3, 4] 的列表只是 1: 2: 3: 4:[] 的語法糖。當(dāng)?shù)谝粋€(gè)元素被強(qiáng)制求值時(shí)(比如說輸出它),列表的其余部分 2: 3: 4:[] 只是一個(gè)許諾將會(huì)產(chǎn)生列表的承諾。我們把這個(gè)承諾叫做 thunk。 thunk 大致上是一個(gè)延遲的計(jì)算。字節(jié)串有兩種風(fēng)格:嚴(yán)格的(strict)和惰性的(lazy)。

strict bytestring 廢除了惰性,沒有 thunk。在 Data.ByteString 中實(shí)現(xiàn)了。 lazy bytestring 是惰性的,但比列表效率高。在 Data.ByteString.Lazy 中實(shí)現(xiàn)了。惰性的字節(jié)串的數(shù)據(jù)存儲(chǔ)在一些塊(chunk)里(不要和 thunk 混淆了),每個(gè)塊的大小是 64 KB。所以,如果你要對(duì)惰性的字節(jié)串的一個(gè)字節(jié)求值(比如說輸出它),最開頭的 64 KB 都會(huì)被求值。在那之后,其余塊實(shí)現(xiàn)為一個(gè)承諾(thunk)。使用示例

import qualified Data. ByteString. Lazy as B 
import qualified Data. ByteString as S

ghci> B. pack [99, 97, 110] 
Chunk "can" Empty 
ghci> B. pack [98.. 120] 
Chunk "bcdefghijklmnopqrstuvwx" Empty 
ghci> let by = B. pack [98, 111, 114, 116] 
ghci> by 
Chunk "bort" Empty 
ghci> B. unpack by 
[98, 111, 114, 116] 
ghci> B. fromChunks [S. pack [40, 41, 42], S. pack [43, 44, 45], S. pack [46, 47, 48]] 
Chunk "()*" (Chunk "+,-" (Chunk "./0" Empty)) 
ghci> B. cons 85 $ B. pack [80, 81, 82, 84] 
Chunk "U" (Chunk "PQRT" Empty) 

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/7387.html

相關(guān)文章

  • Haskell趣學(xué)指南筆記模塊

    摘要:系列文章趣學(xué)指南筆記之基本語法趣學(xué)指南筆記之類型趣學(xué)指南筆記之函數(shù)趣學(xué)指南筆記之高階函數(shù)趣學(xué)指南筆記之模塊趣學(xué)指南筆記之自定義類型趣學(xué)指南筆記之目前我們提到的所有函數(shù)和都是模塊的一部分,默認(rèn)情況下,模塊會(huì)被自動(dòng)導(dǎo)入。系列文章 《Haskell趣學(xué)指南》筆記之基本語法 《Haskell趣學(xué)指南》筆記之類型(type) 《Haskell趣學(xué)指南》筆記之函數(shù) 《Haskell趣學(xué)指南》筆記之高階...

    187J3X1 評(píng)論0 收藏0
  • gitbook: 前端好書推薦

    摘要:它大致概述并討論了前端工程的實(shí)踐如何學(xué)習(xí)它,以及在年實(shí)踐時(shí)使用什么工具。目的是每年發(fā)布一次內(nèi)容更新。前端實(shí)踐第一部分廣泛描述了前端工程的實(shí)踐。對(duì)大多數(shù)人來說,函數(shù)式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發(fā)實(shí)踐的指南。它大致概述并...

    Ali_ 評(píng)論0 收藏0
  • gitbook: 前端好書推薦

    摘要:它大致概述并討論了前端工程的實(shí)踐如何學(xué)習(xí)它,以及在年實(shí)踐時(shí)使用什么工具。目的是每年發(fā)布一次內(nèi)容更新。前端實(shí)踐第一部分廣泛描述了前端工程的實(shí)踐。對(duì)大多數(shù)人來說,函數(shù)式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發(fā)實(shí)踐的指南。它大致概述并...

    CocoaChina 評(píng)論0 收藏0
  • gitbook: 前端好書推薦

    摘要:它大致概述并討論了前端工程的實(shí)踐如何學(xué)習(xí)它,以及在年實(shí)踐時(shí)使用什么工具。目的是每年發(fā)布一次內(nèi)容更新。前端實(shí)踐第一部分廣泛描述了前端工程的實(shí)踐。對(duì)大多數(shù)人來說,函數(shù)式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發(fā)實(shí)踐的指南。它大致概述并...

    Warren 評(píng)論0 收藏0
  • Node.js 是什么?我為什么選擇它?

    摘要:?jiǎn)尉€程使用單線程來運(yùn)行,而不是向之類的其它服務(wù)器,每個(gè)請(qǐng)求將生產(chǎn)一個(gè)線程,這種方法避免了上下文切換和內(nèi)存中的大量執(zhí)行堆棧,這也是和其它服務(wù)器為解決上一個(gè)年,著名的并發(fā)連接問題而采用的方法。 showImg(https://segmentfault.com/img/remote/1460000019968794?w=1080&h=675);當(dāng)我們學(xué)習(xí)一項(xiàng)新的事物的時(shí)候,我們首先要知道它來...

    Joyven 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<