摘要:生成隨機數對于一個函數,如果兩次調用它時使用相同的參數,它會把同樣的結果返回兩次。但是,這也使得產生隨機數這件事變成困難。對于同一個生成器,得到的隨機數是固定的。
系列文章
《Haskell趣學指南》筆記之基本語法
《Haskell趣學指南》筆記之類型(type)
《Haskell趣學指南》筆記之函數
《Haskell趣學指南》筆記之高階函數
《Haskell趣學指南》筆記之模塊
《Haskell趣學指南》筆記之自定義類型
《Haskell趣學指南》筆記之I/O
在 helloworld.hs 里寫入 main = putStrLn "hello, world!
運行 ghc --make helloworld
運行 ./helloworld
得到輸出 hello, world!
然后來看看函數的類型
ghci> :t putStrLn
putStrLn :: String -> IO ()
ghci> :t putStrLn "hello, world"
putStrLn "hello, world" :: IO ()
ghci> :k IO
IO :: * -> *
ghci> :k IO()
IO() :: *
IO () 返回的類型為 (),即空元組,也叫單元。下一節會出現的 IO String 返回的類型為 String。
() 即使一個類型,也是對應類型的值。
do 語法do 語法可以將多個 I/O 操作合成一個。
main = do
putStrLn "Hello, what" s your name");
name <- getLine
putStrLn ("Hey " ++ name ++ ", you rock!")
getLine 的類型為 IO String
getLine 是一個產生字符串的 I/O 操作
注意 name <- 并沒有寫成 name =
只有在 I/O 操作的上下文中才能讀取 I/O 操作的內容,這就是 Haskell 隔離『純代碼』和『不純的代碼』的方式
do語法會自動將最后一個操作的值作為自己的返回值
IO String 與 String 的區別nameTag = "Hello, my name is " ++ getLine
++ 左邊的類型是 String,右邊的類型為 IO String,所以上面的代碼會報錯。必須通過 name <- getLine 取出這個 String,才能繼續。只能在不純的環境中處理不純的數據。不然不純的代碼會像污水那樣污染其余的代碼,保持 I/ O 相關的代碼盡可能小,這對我們的健康有好處。
myLine = getLine如果代碼寫成了這樣
myLine = getLine
這只是給 getLine 增加了一個別名!從 I/O 操作獲取值的唯一方法是使用 <- 。每次我們在 GHCi 中按下回車鍵,GHCi 都會對返回值應用 show,再將生成的字符串交給 putStrLn,從而輸出到終端。
return 是不一樣的Haskell 的 return 跟其他語言不一樣,return 能夠基于一個純的值來構造 I/O 操作。而且 return 不會中斷代碼。所以,在 I/O 上下文中,return "hi" 的類型就是 IO String。那么將一個純值轉換為一個什么都不做的 I/ O 操作又有什么意義呢?作用之一是 return () 能夠構建一個什么都不做的 I/O 操作。
幾個 I/O 函數putStr
putStr
print (相當于 putStrLn . show)
when
sequence [getLine getLine] 用 <- 取多個 I/O 操作的結果組成一個列表
mapM print [1, 2, 3] 等價于 sequence $ map print [1, 2, 3]
mapM_ 則是不保留返回值版本的 mapM
forever $ do IO操作
forM 則是把 mapM 的參數位置對換,某些時候比較方便
getContents 從標準輸入里讀取所有的東西直到遇到一個 end-of-file 字符,而且 getContents 是惰性的
interact fn 取一個類型為 String -> String 的函數 fn 作為參數,返回這樣一個 I/ O 操作:接受輸入,把一個函數作用在輸入上,然后輸出函數運行結果
getArgs 獲取命令行參數
getProgName 獲取程序名
讀入文件流是隨著時間連續地進入、離開程序的一組數據片。
創建文件 test.txt,內容如下
Hi! How are you");
創建文件 capslocker.hs,內容如下
import Control.Monad import Data.Char main = forever $ do l <- getLine putStrLn $ map toUpper l
編譯:運行 ghc --make capslocker
將 test.txt 的內容傳給 capslocker:運行 ./capslocker < test.txt
然后你就會看到所有字母變成了大寫上面代碼也能用 getContents 簡化
import Data.Char
main = do
contents <- getContents
putStr $ map toUpper contents
也可以不傳入 test.txt 文件,直接運行 ./capslocker,然后輸入一行行文本,但是注意,最終你要按 Ctrl+D 來表示內容結束。
stdout 和 stdin一種理解從終端讀入數據的方式是設想我們在讀取一個文件。輸出到終端也可以同樣理解——它就像在寫文件。我們可以把這兩個文件叫做 stdout 和 stdin,分別表示標準輸出和標準輸入。
用 openFile 打開文件
創建 gf.txt,內容如下:
Hey! Hey! You! You! I don" t like your girlfriend! No way! No way! I think you need a new one!
創建 gf.hs,內容如下:
import System.IO main = do handle <- openFile "gf. txt" ReadMode contents <- hGetContents handle putStr contents hClose handle -- 注意 openFile ReadMode hGetContents hClose 這幾個函數,其中的 h 前綴表示它接收 handle
編譯并運行如何知道 openFile 的各個參數的意思呢?
λ> :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 函數
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
怎么用
bracket (openFile name mode)-- 打開文件
(handle -> hClose handle) -- 失敗了怎么辦
(handle -> fn handle) -- 成功了怎么辦
用 bracket 很容易實現 withFile。
生成隨機數對于一個函數,如果兩次調用它時使用相同的參數,它會把同樣的結果返回兩次。這很酷,因為它讓我們能更好地理解程序,它還讓我們能夠延遲求值。但是,這也使得產生隨機數這件事變成困難。random :: (RandomGen g, Random a) => g -> (a, g)
random 接受一個隨機數生成器,返回一個隨機數和一個新的隨機數生成器。然后你可以用新的生成器再去生成一個新的隨機數和一個新的生成器。以此類推。對于同一個生成器,得到的隨機數是固定的。
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 接受一個生成器,返回一個無限長的隨機值列表
ghci> take 5 $ randoms (mkStdGen 11) :: [Int]
[-1807975507, 545074951,- 1015194702,- 1622477312,- 502893664]
并沒有返回一個新的生成器,因為這個生成器在列表的末尾,而這個列表是無限長的……
randomR 在一個范圍內生成隨機數
ghci> randomR (1, 6) (mkStdGen 359353)
(6, 149428957840692)
getStdGen
之前我們每次生成隨機數都要自己先寫一個數字,這很傻……所以 System.Random 提供了 getStdGen,它會向系統索要初始的全局生成器。但 getStdGen 是一個 IO 操作,它返回的類型是 IO stdGen。
import System.Random
main = do
gen <- getStdGen
putStr $ take 20 (randomRs ("a"," z") gen)
但是如果你調用 getStdGen 兩次,你會獲得同一個 gen。第二次應該使用 newStdGen。這個函數除了返回一個 IO stdGen 類型的值,還會把全局生成器給更新了。你再調用 getStdGen 就能得到不同的隨機數這時你會疑惑,getStdGen 為什么能返回不同的結果呢?因為它是一個 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"
這是我瞎寫的代碼。
字節串 bytestring形如[ 1, 2, 3, 4] 的列表只是 1: 2: 3: 4:[] 的語法糖。當第一個元素被強制求值時(比如說輸出它),列表的其余部分 2: 3: 4:[] 只是一個許諾將會產生列表的承諾。我們把這個承諾叫做 thunk。 thunk 大致上是一個延遲的計算。字節串有兩種風格:嚴格的(strict)和惰性的(lazy)。
strict bytestring 廢除了惰性,沒有 thunk。在 Data.ByteString 中實現了。 lazy bytestring 是惰性的,但比列表效率高。在 Data.ByteString.Lazy 中實現了。惰性的字節串的數據存儲在一些塊(chunk)里(不要和 thunk 混淆了),每個塊的大小是 64 KB。所以,如果你要對惰性的字節串的一個字節求值(比如說輸出它),最開頭的 64 KB 都會被求值。在那之后,其余塊實現為一個承諾(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)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7387.html
摘要:系列文章趣學指南筆記之基本語法趣學指南筆記之類型趣學指南筆記之函數趣學指南筆記之高階函數趣學指南筆記之模塊趣學指南筆記之自定義類型趣學指南筆記之目前我們提到的所有函數和都是模塊的一部分,默認情況下,模塊會被自動導入。系列文章 《Haskell趣學指南》筆記之基本語法 《Haskell趣學指南》筆記之類型(type) 《Haskell趣學指南》筆記之函數 《Haskell趣學指南》筆記之高階...
摘要:它大致概述并討論了前端工程的實踐如何學習它,以及在年實踐時使用什么工具。目的是每年發布一次內容更新。前端實踐第一部分廣泛描述了前端工程的實踐。對大多數人來說,函數式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發實踐的指南。它大致概述并...
摘要:它大致概述并討論了前端工程的實踐如何學習它,以及在年實踐時使用什么工具。目的是每年發布一次內容更新。前端實踐第一部分廣泛描述了前端工程的實踐。對大多數人來說,函數式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發實踐的指南。它大致概述并...
摘要:它大致概述并討論了前端工程的實踐如何學習它,以及在年實踐時使用什么工具。目的是每年發布一次內容更新。前端實踐第一部分廣泛描述了前端工程的實踐。對大多數人來說,函數式編程看起來更加自然。 1 Front-End Developer Handbook 2017 地址:https://frontendmasters.com/b... 這是任何人都可以用來了解前端開發實踐的指南。它大致概述并...
摘要:單線程使用單線程來運行,而不是向之類的其它服務器,每個請求將生產一個線程,這種方法避免了上下文切換和內存中的大量執行堆棧,這也是和其它服務器為解決上一個年,著名的并發連接問題而采用的方法。 showImg(https://segmentfault.com/img/remote/1460000019968794?w=1080&h=675);當我們學習一項新的事物的時候,我們首先要知道它來...
閱讀 3361·2021-11-11 16:54
閱讀 3514·2021-10-11 10:58
閱讀 1258·2021-08-30 09:41
閱讀 1807·2019-08-30 15:54
閱讀 2030·2019-08-30 14:00
閱讀 2701·2019-08-29 17:13
閱讀 1668·2019-08-29 15:19
閱讀 611·2019-08-29 15:14