3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人文社科 > 生活经验 >内容正文

生活经验

Real World Haskell 第七章 I/O

發(fā)布時(shí)間:2023/11/27 生活经验 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Real World Haskell 第七章 I/O 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
幾乎所有程序都是用來(lái)從外部世界收集數(shù)據(jù),處理數(shù)據(jù),并把處理結(jié)果返回給外部世界的。也就是說(shuō),輸入和輸出對(duì)于程序設(shè)計(jì)來(lái)說(shuō)相當(dāng)關(guān)鍵。
Haskell的I/O系統(tǒng)很強(qiáng)大,表達(dá)能力很強(qiáng)也很容易使用,理解它的原理對(duì)于學(xué)習(xí)Haskell來(lái)說(shuō)非常重要。Haskell把純函數(shù)式代碼和那些會(huì)對(duì)外部世界產(chǎn)生影響的代碼嚴(yán)格區(qū)分了開來(lái)。也就是說(shuō)它把副作用完全隔離在了純函數(shù)式的代碼之外。這樣不僅可以幫助程序員更容易驗(yàn)證程序的正確性,也能讓編譯器自動(dòng)進(jìn)行優(yōu)化和并行化。
本章先從Haskell簡(jiǎn)單的標(biāo)準(zhǔn)I/O開始。然后我們?cè)賮?lái)討論一些其他更強(qiáng)大的做法,以及更詳細(xì)地探討I/O是如何與純粹、惰性、函數(shù)式的Haskell世界相融合的。
Haskell中的經(jīng)典I/O
我們先來(lái)看一個(gè)程序,它和其他語(yǔ)言如 C 或 Perl中操作I/O的方式非常像。
-- file: ch07/basicio.hs
main = do
putStrLn "Greetings! What is your name?"
inpStr <- getLine
putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!"
可以把這個(gè)程序編譯成獨(dú)立可執(zhí)行文件,或者用runghc執(zhí)行它,也可以在 ghci中調(diào)用它的main函數(shù)。這里是一個(gè)用 runghc 的例子:
$ runghc basicio.hs
Greetings! What is your name?
John
Welcome to Haskell, John!
輸出的結(jié)果還是相當(dāng)明了的,可以看到 putStrLn 輸出了一個(gè) String 和一個(gè)換行符。getLine從標(biāo)準(zhǔn)輸入中讀入一行。你可能還不太了解 <- 的語(yǔ)法。簡(jiǎn)單地說(shuō),它就是把執(zhí)行I/O動(dòng)作的結(jié)果綁定到變量名上。然后我們用列表連接操作符 ++ 來(lái)把輸入的字符串和程序自己的文本連接起來(lái)。
我們來(lái)看一下 putStrLn 和 getLine 的類型,你可以從庫(kù)參考文檔中找到這個(gè)信息,或者直接問(wèn)ghci:
ghci> :type putStrLn
putStrLn :: String -> IO ()
ghci> :type getLine
getLine :: IO String
注意這兩個(gè)類型的返回值里都有IO類型。這樣我們就可以判斷出他們要么是具有副作用,要么是用相同參數(shù)調(diào)用時(shí)返回結(jié)果可能不同,也可能是二者皆有。putStrLn 的類型看上去像個(gè)函數(shù)。接受一個(gè)String類型的參數(shù)并返回一個(gè) IO () 類型的值。那么到底什么是 IO () 呢?
類型為 IO 某某 的就是一個(gè)I/O動(dòng)作。你可以保存它們,但是不會(huì)產(chǎn)生任何影響。你可以寫一句 writefoo = putStrLn "foo",但這句話不會(huì)做任何有用的事。但是如果之后在另一個(gè) I/O 動(dòng)作中使用到了 writefoo,那么當(dāng)父動(dòng)作被調(diào)用的時(shí)候,writefoo就會(huì)被執(zhí)行 -- I/O動(dòng)作可以通過(guò)更大的 I/O動(dòng)作結(jié)合在一起。()是一個(gè)空的元組(發(fā)音為 "unit"),表示從 putStrLn中沒(méi)有返回值。與 Java或C中的void類似。
[Tip] Tip
動(dòng)作可以在任何地方進(jìn)行創(chuàng)建,賦值,和傳遞。但是,他們只有從另一個(gè)I/O動(dòng)作中才能被執(zhí)行。
讓我們?cè)趃hci中看一下:
ghci> let writefoo = putStrLn "foo"
ghci> writefoo
foo
在這個(gè)例子里,foo被輸出并不是因?yàn)閜utStrLn返回了它。而是putStrLn的副作用導(dǎo)致foo被寫到終端。
還有一件事需要注意:ghci實(shí)際“執(zhí)行”了writefoo。這意味著,給ghci一個(gè)I/O動(dòng)作,它就會(huì)立即執(zhí)行它。
[Note] I/O動(dòng)作是什么?
I/O動(dòng)作 (Action):
* 類型為 IO t
* 是 Haskell 中的一等公民,并與Haskell的類型系統(tǒng)無(wú)縫的結(jié)合
* 執(zhí)行時(shí)產(chǎn)生副作用,但求值時(shí)不會(huì)。也就是說(shuō),只有在某個(gè) I/O環(huán)境下被調(diào)用時(shí)才會(huì)產(chǎn)生副作用。
* 任何表達(dá)式都可以返回I/O動(dòng)作,但是這個(gè)I/O動(dòng)作只有在另一個(gè) I/O動(dòng)作(或者main)內(nèi)才會(huì)被執(zhí)行。
* 執(zhí)行一個(gè)類型為 IO t 的動(dòng)作會(huì)執(zhí)行相應(yīng)的 I/O動(dòng)作,并返回一個(gè) t 類型的值。
getLine 的類型看上去可能有些奇怪。看起來(lái)它更像一個(gè)值而不是函數(shù)。實(shí)際上,可以這么看它:getLine存儲(chǔ)了一個(gè)I/O動(dòng)作。當(dāng)該動(dòng)作被執(zhí)行時(shí),返回一個(gè)String。<-操作符用來(lái)把結(jié)果從被執(zhí)行的動(dòng)作中“拉出來(lái)”,并存儲(chǔ)到一個(gè)變量里。
main本身是一個(gè)類型為 IO () 的動(dòng)作。你只能從另一個(gè) I/O動(dòng)作中執(zhí)行I/O動(dòng)作。所以Haskell程序中所有的 I/O動(dòng)作最終都是由main驅(qū)動(dòng)的,那是每個(gè)Haskell程序開始執(zhí)行的地方。這就是Haskell提供的隔離副作用的機(jī)制:在I/O動(dòng)作中執(zhí)行I/O動(dòng)作,并從那里調(diào)用純函數(shù)式的(非I/O)函數(shù)。大部分Haskell代碼是純的;I/O動(dòng)作執(zhí)行I/O的動(dòng)作同時(shí)調(diào)用這些純的代碼。
要執(zhí)行一組動(dòng)作, do是很方便的一個(gè)方法。后面會(huì)看到,還有其他方法。當(dāng)使用do時(shí),縮進(jìn)變得很重要;你需要要保證所有的動(dòng)作代碼都對(duì)齊了。
只有當(dāng)你需要執(zhí)行多于一個(gè)動(dòng)作的時(shí)候才需要使用 do,整個(gè)do程序塊的返回值就是最后一個(gè)被執(zhí)行的動(dòng)作的返回值。在“剖析do程序塊”一節(jié)有對(duì)do語(yǔ)法的完整解釋。
我們來(lái)看一個(gè)在I/O動(dòng)作中調(diào)用純函數(shù)式代碼的例子:
-- file: ch07/callingpure.hs
name2reply :: String -> String
name2reply name =
"Pleased to meet you, " ++ name ++ ".\n" ++
"Your name contains " ++ charcount ++ " characters."
where charcount = show (length name)
main :: IO ()
main = do
putStrLn "Greetings once again. What is your name?"
inpStr <- getLine
let outStr = name2reply inpStr
putStrLn outStr
注意這個(gè)例子中的 name2reply 函數(shù)。它是一個(gè)普通的 Haskell 函數(shù),遵從我們已經(jīng)說(shuō)過(guò)的所有規(guī)則:當(dāng)給出相同輸入時(shí)總是返回相同的結(jié)果,沒(méi)有副作用,惰性求值。它用到了一些其他的Haskell函數(shù): (++), show, 和 length。
到后面的main中,我們把 name2reply inpStr 的值綁定到 outStr 變量。在do程序塊中時(shí),用 <- 獲得IO動(dòng)作的返回值,用 let 獲得純函數(shù)式代碼的返回值。在do程序塊中,你不能在let語(yǔ)句后面使用 in 。
在這段代碼中你可以看到如何從鍵盤上讀取一個(gè)人的名字。然后這個(gè)名字被傳遞給一個(gè)純函數(shù),然后其結(jié)果被輸出。實(shí)際上,main的最后兩行也可以用 putStrLn (name2reply inpStr) 來(lái)代替的。這樣盡管main 確實(shí)具有副作用(它讓終端上出現(xiàn)了一些字符),而name2reply 沒(méi)有并且不可能有副作用。因?yàn)閚ame2reply是純函數(shù),不是一個(gè)動(dòng)作。
我們可以ghci中驗(yàn)證一下:
ghci> :load callingpure.hs
[1 of 1] Compiling Main ( callingpure.hs, interpreted )
Ok, modules loaded: Main.
ghci> name2reply "John"
"Pleased to meet you, John.\nYour name contains 4 characters."
ghci> putStrLn (name2reply "John")
Pleased to meet you, John.
Your name contains 4 characters.
字符串中的\n 是換行符,讓終端在輸出時(shí)開始一個(gè)新行。在ghci中直接調(diào)用 name2reply "John"的話會(huì)在字面上顯示 \n ,因?yàn)樗怯胹how來(lái)顯示函數(shù)返回值的。但是使用putStrLn 的話,會(huì)把字符串發(fā)送給終端,終端則會(huì)把 \n 翻譯成換行符。
你覺(jué)得要是直接在ghci中輸入main 會(huì)發(fā)生什么呢?你可以自己試一下。
看過(guò)了這些例子程序之后,你可能會(huì)想Haskell其實(shí)是命令式的而不是純粹的、惰性的,函數(shù)式的。這些例子有些看上去好像就是一系列動(dòng)作按順序執(zhí)行。但是還里面確實(shí)還有更深刻的含義。我們將在本章后面部分的"Haskell真的是命令式的么?"和"惰性I/O"兩節(jié)中繼續(xù)探討這個(gè)問(wèn)題。
Pure vs. I/O
為了幫助理解純函數(shù)式代碼和I/O之間究竟有何不同,這里給出一個(gè)對(duì)照表。當(dāng)我們說(shuō)純函數(shù)式代碼時(shí),我們說(shuō)的是那些對(duì)相同輸入總是返回相同結(jié)果,并且沒(méi)有副作用的Haskell函數(shù)。在Haskell里,只有I/O動(dòng)作的執(zhí)行不適用于這些規(guī)則。
Table 7.1. Pure vs. Impure
Pure Impure
純 非純
給定相同參數(shù)總是返回相同值 對(duì)相同參數(shù)可能返回不通值
永遠(yuǎn)沒(méi)有副作用 可以有副作用
用于不改變狀態(tài) 可以改變程序,系統(tǒng)或外界的全局狀態(tài)
為什么純粹性如此重要
這一節(jié)中我們已經(jīng)探討了Haskell是如何將純函數(shù)式代碼與 I/O動(dòng)作清楚的分離開來(lái)的。大多數(shù)語(yǔ)言并不會(huì)這樣區(qū)分。像 C 或 Java這樣的語(yǔ)言里,編譯器不能保證某一個(gè)函數(shù)對(duì)相同的參數(shù)總是返回相同的值,或者保證一個(gè)函數(shù)永遠(yuǎn)沒(méi)有副作用。要想知道一個(gè)函數(shù)是否有副作用,唯一的辦法就是去讀它的文檔,而這文檔還不一定準(zhǔn)確。
程序中很多bug都是由一些出乎意料的副作用導(dǎo)致的。還有一些就是因?yàn)楸灰粋€(gè)函數(shù)對(duì)相同的輸入返回不同的結(jié)果給搞糊涂了。隨著多線程和其他形式的并行變得越來(lái)越平常,要管理全局的副作用就變得愈加困難了。
Haskell這種把副作用隔離進(jìn)I/O動(dòng)作的方法提供了一個(gè)清楚的邊界。你總是可以清楚地知道系統(tǒng)的哪一部分可能會(huì)修改狀態(tài),哪些不會(huì)。你總是可以確信程序中純函數(shù)式的那部分代碼不會(huì)產(chǎn)生出人意料的結(jié)果。這可以幫助你編寫程序。同樣也可以幫助編譯器來(lái)理解你的程序。例如最近一些版本的ghc就可以對(duì)代碼中純函數(shù)式的部分——這部分代碼可說(shuō)是計(jì)算中的圣杯——提供一定程度的自動(dòng)的并行處理。
關(guān)于這個(gè)主題的更多討論,見(jiàn) “惰性I/O的副作用”一節(jié)。
操作文件和句柄
現(xiàn)在你已經(jīng)看過(guò)如何通過(guò)計(jì)算機(jī)終端與用戶進(jìn)行交互了。當(dāng)然,你經(jīng)常會(huì)需要操作一些特定的文件。這個(gè),同樣很容易做到。
Haskell為I/O定義了很多基本函數(shù),他們中的許多都與其他編程語(yǔ)言類似。System.IO 的庫(kù)參考文檔提供了所有基本I/O函數(shù)的概述,如果你需要某個(gè)在本文中沒(méi)有涉及到的函數(shù),可以到參考哪里。
操作文件,一般從 openFile 開始,它會(huì)返回給你一個(gè)文件句柄。然后你就可以用這個(gè)句柄對(duì)那個(gè)文件進(jìn)行操作。Haskell提供了諸如hPutStrLn 這樣的函數(shù),它類似putStrLn,不過(guò)需要多傳一個(gè)文件句柄參數(shù),指定要操作的文件。操作完用 hClose來(lái)關(guān)閉句柄。這些函數(shù)都定義在 System.IO 中,因此在操作文件之前要先導(dǎo)入這個(gè)模塊。差不多所有非"h"開頭的函數(shù)都有與之相對(duì)的 "h"開頭的函數(shù);例如有一個(gè) print 用來(lái)向屏幕輸出,就有一個(gè)hPrint用來(lái)向文件輸出。
我們先來(lái)用命令式的方式來(lái)對(duì)文件進(jìn)行讀寫,應(yīng)該和其他語(yǔ)言里面的while循環(huán)有些類似。不過(guò)這并不是Haskell里最好的寫法;后面你還會(huì)看到更多更Haskell的做法。
-- file: ch07/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
if ineof
then return ()
else do inpStr <- hGetLine inh
hPutStrLn outh (map toUpper inpStr)
mainloop inh outh
所有Haskell程序都是從main開始執(zhí)行。首先打開兩個(gè)文件:input.txt 以讀模式打開,output.txt 以寫模式打開。之后調(diào)用mainloop對(duì)文件進(jìn)行處理。
mainloop首先檢查是否已經(jīng)到達(dá)文件的末尾(EOF)。如果不是就從輸入中讀入一行。把它轉(zhuǎn)換成大寫后寫入到輸出文件。之后遞歸的調(diào)用mainloop繼續(xù)處理文件。
注意這里對(duì)return的調(diào)用。這與C或Python中的return不一樣。在那些語(yǔ)言里,return用來(lái)立即中止當(dāng)前函數(shù)的執(zhí)行,并把值返回給調(diào)用者。在Haskell里,return與 <- 恰好相反。也就是說(shuō)return把一個(gè)純的值包裝成一個(gè)IO類型。因?yàn)槊恳粋€(gè)I/O動(dòng)作必須返回IO類型,如果你的結(jié)果來(lái)自純的計(jì)算,必須把它包裝成IO類型再返回。比如說(shuō),對(duì)于7這個(gè)Int,return 7就會(huì)創(chuàng)建一個(gè)類型為 IO Int的動(dòng)作。當(dāng)該動(dòng)作被執(zhí)行的時(shí)候,這個(gè)動(dòng)作會(huì)返回7.關(guān)于return更詳細(xì)探討,請(qǐng)看“return的本質(zhì)”一節(jié)。
讓我們嘗試運(yùn)行一下這個(gè)程序。假設(shè)我們已經(jīng)有了一個(gè) input.txt 文件,內(nèi)容如下:
This is ch08/input.txt
Test Input
I like Haskell
Haskell is great
I/O is fun
123456789
執(zhí)行 runghc toupper-imp.hs ,之后會(huì)在目錄中找到 output.txt 文件。其內(nèi)容如下:
THIS IS CH08/INPUT.TXT
TEST INPUT
I LIKE HASKELL
HASKELL IS GREAT
I/O IS FUN
123456789
openFile詳解
讓我們用ghci來(lái)檢查下 openFile的類型:
ghci> :module System.IO
ghci> :type openFile
openFile :: FilePath -> IOMode -> IO Handle
FilePath 只是String的一個(gè)別名。在I/O函數(shù)中使用它而非String是為了指明該參數(shù)是特別用來(lái)作文件名用的,而不是一個(gè)常規(guī)的數(shù)據(jù)。
IOMode指定文件如何管理。IOMode可能的取值列在表 7-2 中。
IOMode的取值
IOMode Can read? Can write? Starting position Notes
IOMode 可讀? 可寫? 開始位置 附注
ReadMode Yes No 文件開頭 文件必須已經(jīng)存在
WriteMode No Yes 文件開頭 文件如果已經(jīng)存在將會(huì)完全清空
ReadWriteMode Yes Yes 文件開頭 如果文件不存在將會(huì)創(chuàng)建;否則已經(jīng)存在的數(shù)據(jù)不會(huì)動(dòng)
AppendMode No Yes 文件結(jié)尾 文件如果不存在將會(huì)創(chuàng)建;否則已經(jīng)存在的數(shù)據(jù)不會(huì)動(dòng)
雖然本章大部分例子處理文本文件,但是Haskell也是可以處理二進(jìn)制文件的。如果要處理二進(jìn)制文件,就要用 openBinaryFile 代替 openFile。把文件當(dāng)作二進(jìn)制打開與作為文本打開,在Windows上處理時(shí)會(huì)有所不同。在Linux一類的操作系統(tǒng)上, openFile 和 openBinaryFile執(zhí)行的是完全相同的操作。不管怎樣,即使出于移植性的考慮,處理二進(jìn)制文件時(shí)也應(yīng)該總是使用openBinaryFile。
關(guān)閉句柄
你已經(jīng)看到hClose是用來(lái)關(guān)閉文件句柄的。讓我們花點(diǎn)時(shí)間來(lái)探討下為什么關(guān)閉句柄很重要。
在“緩沖”一節(jié)你將會(huì)看到,Haskell為文件維護(hù)了內(nèi)部的緩沖區(qū)。這帶來(lái)了很關(guān)鍵的性能提升。但是,這樣一來(lái)的話,以寫入模式打開的文件,可能要到調(diào)用hClose的時(shí)候,有些數(shù)據(jù)才會(huì)真正被寫到操作系統(tǒng)上去。
要確保對(duì)打開的文件調(diào)用hClose的另一個(gè)原因是它會(huì)占用系統(tǒng)資源。如果你的程序執(zhí)行很長(zhǎng)時(shí)間,并且打開了很多文件但是沒(méi)有關(guān)閉他們,你的程序很可能因?yàn)橘Y源耗盡而崩潰。這一點(diǎn)上Haskell與其他語(yǔ)言沒(méi)什么區(qū)別。
當(dāng)程序退出時(shí),Haskell一般會(huì)把仍然打開著的文件關(guān)閉。然而在某些情況下卻不一定,因此再次提醒大家,作為一個(gè)負(fù)責(zé)人的程序員,永遠(yuǎn)不能忘記調(diào)用hClose。
Haskell還提供了一些工具,可以幫助你不論是否有錯(cuò)誤發(fā)生都能輕松確保打開的文件被關(guān)閉。你可以在“擴(kuò)展實(shí)例:函數(shù)式I/O和臨時(shí)文件”一節(jié)了解關(guān)于finally,在“獲取使用釋放循環(huán)”一節(jié)了解bracket。
Seek 和 Tell
當(dāng)通過(guò)句柄從磁盤讀寫文件的時(shí)候,操作系統(tǒng)內(nèi)部會(huì)記錄文件當(dāng)前操作所在的位置。每次讀取,操作系統(tǒng)會(huì)返回從當(dāng)前位置開始的一塊數(shù)據(jù),并根據(jù)讀取的數(shù)據(jù)將位置相應(yīng)地遞增。
可以用hTell獲得文件當(dāng)前的位置。當(dāng)文件剛剛創(chuàng)建時(shí),它是空的,位置為0。寫入了5個(gè)字節(jié)后,它的位置變?yōu)?,等等。hTell取一個(gè)句柄做參數(shù),返回一個(gè) IO Integer 表示位置。
與hTell相伴的是hSeek。hSeek可以讓你修改文件的位置。它接受三個(gè)參數(shù):文件句柄,偏移模式(SeekMode)和偏移量。
偏移模式(SeekMode)有三種,用來(lái)表示如何對(duì)給出的偏移量進(jìn)行解釋。AbsoluteSeek 意思是給定的偏移是文件中的精確位置,這與hTell給出的信息是一致的。RelativeSeek 意思是以當(dāng)前位置為原點(diǎn)進(jìn)行偏移,一個(gè)正數(shù)的偏移量表示向前偏移,而負(fù)數(shù)表示向后偏移。最后SeekFromEnd將從文件末尾向前偏移指定數(shù)量的字節(jié)。 hSeek handle SeekFromEnd 0 將把你帶到文件的末尾。“擴(kuò)展實(shí)例:函數(shù)式I/O和臨時(shí)文件”一節(jié)有一個(gè)hSeek的例子。
并不是所有的句柄都是可以進(jìn)行偏移的。一般來(lái)說(shuō)句柄都是指向文件,但是它也能夠指向其他一些不能進(jìn)行偏移操作的東西,例如網(wǎng)絡(luò)連接,磁帶驅(qū)動(dòng)器,或者終端。可以用hIsSeekable 來(lái)檢查一個(gè)給定的句柄是否支持偏移。
標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯(cuò)誤
之前我們提到過(guò)每一個(gè)非"h"的函數(shù),一般都有一個(gè) "h"函數(shù)與之對(duì)應(yīng),可以用來(lái)處理任何句柄。實(shí)際上,非"h"的函數(shù)只不過(guò)是他們的"h"函數(shù)的一種快捷方式而已。
在System.IO中有三個(gè)預(yù)定義的句柄。這些句柄總是可用的。它們就是標(biāo)準(zhǔn)輸入 stdin ;標(biāo)準(zhǔn)輸出 stdout; 和標(biāo)準(zhǔn)錯(cuò)誤 stderr。標(biāo)準(zhǔn)輸入一般指鍵盤,標(biāo)準(zhǔn)輸出指顯示屏,標(biāo)準(zhǔn)錯(cuò)誤一般也指向顯示屏。
我們可以這樣來(lái)定義getLine一類的函數(shù):
getLine = hGetLine stdin
putStrLn = hPutStrLn stdout
print = hPrint stdout
[Tip] Tip
這里使用了部分函數(shù)。如果不清楚,回顧下“部分函數(shù)應(yīng)用和柯里化”一節(jié)。
剛才我們對(duì)三個(gè)標(biāo)準(zhǔn)文件句柄的解釋的只是它們“通常”都指向什么,有的操作系統(tǒng)還允許你在啟動(dòng)的時(shí)候把這些文件句柄重定向到其他的地方——文件,設(shè)備,甚至是其他程序。這個(gè)特性在POSIX系統(tǒng)(Linux,BSD, Mac)上的shell腳本中被廣泛應(yīng)用,在Windows上也可以使用。
一般來(lái)說(shuō)使用標(biāo)準(zhǔn)輸入輸出而非顯示指定文件是有好處的,這樣你可以通過(guò)終端和用戶進(jìn)行交互。同時(shí)也允許你操作輸入輸出文件,如果需要的話甚至還可以和其他程序組合在一起。
舉個(gè)例子來(lái)說(shuō),你可以這種方式給 callingpure.hs 提供輸入:
$ echo John|runghc callingpure.hs
Greetings once again. What is your name?
Pleased to meet you, John.
Your name contains 4 characters.
當(dāng)執(zhí)行 callingpure.hs 時(shí),它不需要等待鍵盤輸入,而是從 echo 程序接收到 John。同時(shí)注意到和用鍵盤輸入時(shí)不同,輸出中沒(méi)有John的那一行。終端把你鍵入的內(nèi)容回顯給你,但這是通過(guò)另一個(gè)程序進(jìn)行輸入,不會(huì)把輸入包含在輸出流里。
刪除和重命名文件
本章前面部分探討了如何操作文件的內(nèi)容。現(xiàn)在讓我們來(lái)關(guān)心下如何操作文件本身。
System.Directory 模塊里有兩個(gè)函數(shù)還是挺有用的。一個(gè)是removeFile,它只接受一個(gè)文件名作為參數(shù),執(zhí)行的操作就是刪除這個(gè)文件。renameFile取兩個(gè)文件名作參數(shù):第一個(gè)是舊的文件名,第二個(gè)是新文件名。如果兩個(gè)文件名處于不同的目錄中,你也把他當(dāng)成是移動(dòng)操作。調(diào)用renameFile前舊文件名必須已經(jīng)存在。如果新的文件名已經(jīng)存在了,會(huì)先把它刪了,然后再進(jìn)行改名操作。
像其他取文件名做參數(shù)的函數(shù)一樣,renameFile在舊文件名不存在的情況下會(huì)拋出異常。第19章《錯(cuò)誤處理》將會(huì)介紹更多異常處理的信息。
System.Directory模塊中還有很多函數(shù)用來(lái)進(jìn)行目錄的創(chuàng)建和刪除,獲取目錄中的文件列表,檢測(cè)文件是否存在。在“目錄和文件信息”一節(jié)將會(huì)對(duì)這些話題進(jìn)行探討。
臨時(shí)文件
程序員經(jīng)常需要?jiǎng)?chuàng)建臨時(shí)文件。這些文件可以用來(lái)存儲(chǔ)計(jì)算需要的大量數(shù)據(jù),或者是供給其他程序或其他用戶使用的數(shù)據(jù)。
你可以手動(dòng)為創(chuàng)建的文件取一個(gè)獨(dú)一無(wú)二的文件名,但是在不同的平臺(tái)上安全地做到這一點(diǎn)還是需要處理一些細(xì)節(jié)上的不同。Haskell提供了一個(gè)方便的函數(shù)叫 openTempFile (和對(duì)應(yīng)的openBinaryTempFile),可以幫你處理這個(gè)問(wèn)題。
openTempFile 需要兩個(gè)參數(shù):要?jiǎng)?chuàng)建文件的目錄和文件名命名的“模板”。目錄可以直接用 "."表示當(dāng)前工作目錄。或者使用System.Directory.getTemporaryDirectory得到機(jī)器上的臨時(shí)目錄。文件名模板作為創(chuàng)建文件名的基礎(chǔ),然后再添加一些隨機(jī)字符上去,以確保產(chǎn)生的文件名是真正獨(dú)一無(wú)二的。
openTempFile 的返回類型是 IO (FilePath, Handle)。元組的第一部分是創(chuàng)建文件的文件名,第二部分是以 ReadWriteMode 模式打開的文件句柄。當(dāng)操作完文件句柄后,要用 hClose 將它關(guān)閉,并調(diào)用 removeFile 刪除該臨時(shí)文件。下面舉個(gè)例子:
擴(kuò)展實(shí)例:函數(shù)式I/O和臨時(shí)文件
這里有一個(gè)比較龐大的例子,它融合了本章以及之前章節(jié),甚至一些還沒(méi)見(jiàn)過(guò)的概念。嘗試閱讀本程序,看看能否看出它是做什么的,以及是如何去做的。
-- file: ch07/tempfile.hs
import System.IO
import System.Directory(getTemporaryDirectory, removeFile)
import System.IO.Error(catch)
import Control.Exception(finally)
-- 主程序入口。在myAction中使用臨時(shí)文件
main :: IO ()
main = withTempFile "mytemp.txt" myAction
{-
程序核心部分。傳遞一個(gè)文件路徑和一個(gè)臨時(shí)文件句柄進(jìn)行調(diào)用。
myAction 是從 withTempFile 中調(diào)用的,所以當(dāng) myAction 函數(shù)退出時(shí),臨時(shí)文件將會(huì)被自動(dòng)關(guān)閉并刪除,。
-}
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
-- 向臨時(shí)文件中寫入一些數(shù)據(jù)
let tempdata = show [1..10]
putStrLn $ "Writing one line containing " ++
show (length tempdata) ++ " bytes: " ++
tempdata
hPutStrLn temph tempdata
-- 查看新的位置,實(shí)際上這并不改變pos在內(nèi)存中的值,
-- 但是它讓 "pos" 變量在 "do" 程序塊后面的部分中指向了另一個(gè)值。
pos <- hTell temph
putStrLn $ "After writing, my new position is " ++ show pos
-- 轉(zhuǎn)移到文件起始位置并開始顯示
putStrLn $ "The file content is: "
hSeek temph AbsoluteSeek 0
-- hGetContents 惰性讀取整個(gè)文件
c <- hGetContents temph
-- 把文件一個(gè)字節(jié)一個(gè)字節(jié)輸出,后跟 \n
putStrLn c
-- 以 Haskell 字面量形式顯示
putStrLn $ "Which could be expressed as this Haskell literal:"
print c
{-
本函數(shù)接收兩個(gè)參數(shù):臨時(shí)文件名模式和一個(gè)函數(shù)。它會(huì)創(chuàng)建一個(gè)臨時(shí)文件,并將文件名和文件句柄傳遞給那個(gè)函數(shù)。
臨時(shí)文件用通過(guò) openTempFile 創(chuàng)建的。目錄是 getTemporaryDirectory 所指定的,如果系統(tǒng)沒(méi)有臨時(shí)目錄概念,則用 "." 。
給定的文件名模式被傳遞給了 openTempFile。
當(dāng)給定函數(shù)中止,即使是異常中止,文件句柄也會(huì)被關(guān)閉,同時(shí)臨時(shí)文件被刪除。
-}
withTempFile :: String -> (FilePath -> Handle -> IO a) -> IO a
withTempFile pattern func =
do -- The library ref says that getTemporaryDirectory may raise on
-- exception on systems that have no notion of a temporary directory.
-- So, we run getTemporaryDirectory under catch. catch takes
-- two functions: one to run, and a different one to run if the
-- first raised an exception. If getTemporaryDirectory raised an
-- exception, just use "." (the current working directory).
庫(kù)參考手冊(cè)里說(shuō)如果系統(tǒng)不支持臨時(shí)目錄概念的話,getTemporaryDirectory 將會(huì)拋出異常。
因此我們?cè)?catch 之下來(lái)執(zhí)行 getTemporaryDirectory。catch 接受兩個(gè)函數(shù)參數(shù):一個(gè)會(huì)直接運(yùn)行,另一個(gè)會(huì)在第一個(gè)函數(shù)拋出異常時(shí)運(yùn)行。如果getTemporaryDirectory拋出了異常,就使用"."
tempdir <- catch (getTemporaryDirectory) (\_ -> return ".")
(tempfile, temph) <- openTempFile tempdir pattern
-- 調(diào)用 (func tempfile temph) 對(duì)臨時(shí)文件進(jìn)行操作。 finally 需要兩個(gè)操作作為參數(shù)。第一個(gè)會(huì)被直接執(zhí)行。
-- 第一個(gè)操作執(zhí)行之后,不論是否拋出異常,第二個(gè)操作都會(huì)被執(zhí)行。
-- 這樣,我們就能確保臨時(shí)文件總是會(huì)被刪除。finally 返回第一個(gè)操作的返回值。
finally (func tempfile temph)
(do hClose temph
removeFile tempfile)
我們來(lái)看下程序的結(jié)尾。從withTempFile這個(gè)函數(shù)可以看出Haskell在引入I/O的時(shí)候并沒(méi)有忘記它自己函數(shù)式的本質(zhì)。這個(gè)函數(shù)取一個(gè)String和另一個(gè)函數(shù)做參數(shù)。withTempFile使用臨時(shí)文件的文件名和句柄傳入的函數(shù)進(jìn)行調(diào)用。當(dāng)傳入的函數(shù)退出時(shí),臨時(shí)文件被關(guān)閉和刪除。因此即使在處理I/O時(shí),我們依然能夠看到傳遞函數(shù)作為參數(shù)的用法。Lisp程序員大概會(huì)發(fā)現(xiàn)這個(gè) withTempFile 函數(shù)跟Lisp里的 with-open-file 函數(shù)很類似。
適當(dāng)?shù)漠惓L幚砜梢宰屇愕某绦蛟谟龅藉e(cuò)誤時(shí)更加健壯。通常情況下對(duì)臨時(shí)處理完成后都需要把臨時(shí)文件刪除,即使發(fā)生了錯(cuò)誤也不例外。我們需要保證這一點(diǎn)。異常處理更多信息見(jiàn)第19章《錯(cuò)誤處理》。
回到程序的開始,main的定義只是簡(jiǎn)單的 withTempFile "mytemp.txt" myAction 。 之后使用臨時(shí)文件的文件名和文件句柄調(diào)用 myAction。
然后myAction在終端上顯示一些信息,向文件中寫入一些數(shù)據(jù),再移動(dòng)到文件開頭用 hGetContents 讀取數(shù)據(jù),再把文件內(nèi)容一個(gè)字節(jié)一個(gè)字節(jié)的顯示出來(lái)。之后又通過(guò) print c 來(lái)按Haskell的字面量格式輸出出來(lái),最后這個(gè)操作等價(jià)于 putStrLn (show c) 相同。
讓我們來(lái)看看輸出:
$ runhaskell tempfile.hs
Welcome to tempfile.hs
I have a temporary file at /tmp/mytemp8572.txt
My initial position is 0
Writing one line containing 22 bytes: [1,2,3,4,5,6,7,8,9,10]
After writing, my new position is 23
The file content is:
[1,2,3,4,5,6,7,8,9,10]
Which could be expressed as this Haskell literal:
"[1,2,3,4,5,6,7,8,9,10]\n"
每次運(yùn)行此程序,臨時(shí)文件名都會(huì)稍有不同,因?yàn)樗须S機(jī)生成的部分。觀察這些輸出,你可能會(huì)有如下一些疑問(wèn):
1、為什么寫入了22個(gè)字節(jié)后,位置是23?
2、為什么文件內(nèi)容最后顯示有一個(gè)空行?
3、為什么在Haskell字面格式顯示的末尾有一個(gè) \n
你可能已經(jīng)猜到這三個(gè)問(wèn)題的互相之間都是有關(guān)聯(lián)的。你可以先自己想一會(huì),看能否找出答案。如果需要幫助,這里也為你提供一些解釋:
1、因?yàn)槲覀兪怯玫?hPutStrLn 而不是 hPutStr 來(lái)寫入數(shù)據(jù)。hPutStrLn 總是會(huì)在行末尾添加 \n,這個(gè) \n 在 tempdate 中并不存在。
2、這里是用 putStrLn c 來(lái)顯示文件內(nèi)容 c 。因?yàn)槲募?nèi)容本來(lái)就是通過(guò) hPutStrLn 寫入的,所以 c 的末尾是個(gè)換行符,而 putStrLn 在輸出的時(shí)候又在末尾添加了一個(gè)換行符,結(jié)果就顯示出了一個(gè)空行。
3、\n 就是一開始hPutStrLn輸出的換行符。
最后說(shuō)明下,在不同的操作系統(tǒng)上字節(jié)數(shù)計(jì)算方式會(huì)有所不同。例如在Windows上使用\r\n的雙子節(jié)序列作為行尾標(biāo)記,因此在不同的平臺(tái)上可能看上去會(huì)不太一樣。
惰性I/O
本章到目前為止,你看到的例子都是比較傳統(tǒng)的 I/O 處理方式。每一行或每一塊數(shù)據(jù)都是單獨(dú)讀取的,并且也需要單獨(dú)進(jìn)行處理。
Haskell還提供另一種方式。因?yàn)镠askell是惰性語(yǔ)言,也就是說(shuō)只有到最后關(guān)頭才會(huì)對(duì)數(shù)據(jù)的值進(jìn)行求值,所以這里會(huì)一些新穎的處理I/O的方法。
hGetContents
新方法之一就是hGetContents函數(shù)。hGetContents 的類型是 Handle -> IO String。返回的 String 的值就是指定文件句柄中的全部?jī)?nèi)容。
在嚴(yán)格求值的語(yǔ)言里,使用這樣的函數(shù)往往是不太好的。因?yàn)樽x取一個(gè)2KB的文件還可以,要是讀取一個(gè)500GB文件的全部?jī)?nèi)容,將很可能會(huì)因?yàn)閮?nèi)存不夠而崩潰。在這些語(yǔ)言中,需要用傳統(tǒng)的機(jī)制如循環(huán)來(lái)處理文件的全部?jī)?nèi)容。
但是hGetContents是不一樣的。它返回的String是惰性求值的。也就是說(shuō)在調(diào)用hGetContents 時(shí)并不會(huì)進(jìn)行實(shí)際的讀取操作。只有當(dāng)列表的元素(字符)被處理時(shí)才會(huì)從文件句柄中讀取數(shù)據(jù)。當(dāng)String中的元素不被使用時(shí),Haskell的垃圾收集器會(huì)自動(dòng)釋放它的內(nèi)存。所有這些都是透明的。它看上去就像是——其實(shí)實(shí)際上也是——一個(gè)純的String,因此你可以把它傳遞給純函數(shù)式的代碼(沒(méi)有IO)。
我們來(lái)看一個(gè)例子。前一節(jié)“處理文件和句柄”中有一個(gè)命令式的程序,把文件的全部?jī)?nèi)容轉(zhuǎn)換成大寫。它的命令式算法與其他很多語(yǔ)言中見(jiàn)到的差不多。這里展示一個(gè)利用惰性求值的簡(jiǎn)單地多的算法:
-- file: ch07/toupper-lazy1.hs
import System.IO
import Data.Char(toUpper)
main :: IO ()
main = do
inh <- openFile "input.txt" ReadMode
outh <- openFile "output.txt" WriteMode
inpStr <- hGetContents inh
let result = processData inpStr
hPutStr outh result
hClose inh
hClose outh
processData :: String -> String
processData = map toUpper
注意hGetContents為我們處理了所有的讀取邏輯。還有 processData 這個(gè)函數(shù),它是一個(gè)純函數(shù),因?yàn)樗鼪](méi)有副作用,并且使用相同的參數(shù)調(diào)用總是返回相同的結(jié)果。它不需要——也沒(méi)辦法——知道它的輸入是從文件中惰性讀取的。不管是20個(gè)字符的字符串還是磁盤上存的500GB的數(shù)據(jù),它都可以完美地進(jìn)行處理。
可以使用ghci來(lái)確認(rèn)一下:
ghci> :load toupper-lazy1.hs
[1 of 1] Compiling Main ( toupper-lazy1.hs, interpreted )
Ok, modules loaded: Main.
ghci> processData "Hello, there! How are you?"
"HELLO, THERE! HOW ARE YOU?"
ghci> :type processData
processData :: String -> String
ghci> :type processData "Hello!"
processData "Hello!" :: String
[Warning] Warning
在上面的例子中,如果你想在處理完inpStr變量之后保留著繼續(xù)使用的話,程序的內(nèi)存利用率會(huì)有所降低。這是因?yàn)榫幾g器必須把inpStr 的值保留在內(nèi)存中,后面才可以繼續(xù)使用。上面這個(gè)程序里,編譯器知道 inpStr 永遠(yuǎn)不會(huì)再用了,因此會(huì)立刻把它釋放掉。只要記住:內(nèi)存只有在最后一次使用后才會(huì)被釋放。
為了清楚得展示其中純函數(shù)式的代碼的使用,使得這個(gè)程序?qū)懙糜悬c(diǎn)冗長(zhǎng),這里有個(gè)更簡(jiǎn)潔的版本,接下來(lái)將以這個(gè)程序作為基礎(chǔ)。
-- file: ch07/toupper-lazy2.hs
import System.IO
import Data.Char(toUpper)
main = do
inh <- openFile "input.txt" ReadMode
outh <- openFile "output.txt" WriteMode
inpStr <- hGetContents inh
hPutStr outh (map toUpper inpStr)
hClose inh
hClose outh
使用hGetContents時(shí)并不一定要處理輸入文件的全部?jī)?nèi)容。當(dāng)Haskell系統(tǒng)斷定hGetContents返回的整個(gè)字符串可以被垃圾回收時(shí)——意味著它再也不會(huì)被用到——它會(huì)自動(dòng)關(guān)閉相應(yīng)文件。從文件讀出來(lái)的數(shù)據(jù)也是一樣的處理。當(dāng)一段數(shù)據(jù)不再需要的時(shí)候,Haskell環(huán)境將會(huì)釋放占有的內(nèi)存。嚴(yán)格地說(shuō)在這個(gè)例子里根本不需要調(diào)用hClose。不過(guò)加上它仍然是一個(gè)好習(xí)慣,因?yàn)閷?lái)對(duì)程序的修改可能會(huì)使得調(diào)用hClose變得必要。
[警告]警告
當(dāng)使用hGetContents時(shí),要注意一點(diǎn):就算后面的程序中并不直接引用這個(gè)文件句柄,你也必須等到 hGetContents 的結(jié)果被處理完后才能關(guān)閉這個(gè)句柄。如果提前關(guān)閉可能會(huì)導(dǎo)致文件數(shù)據(jù)的丟失。因?yàn)镠askell是惰性的,你通常可以做出這樣的假設(shè),你輸出了多少計(jì)算結(jié)果,就有多少計(jì)算涉及的輸入被讀取。
readFile和writeFile
Haskell程序員經(jīng)常把hGetContents用作過(guò)濾器。他們從一個(gè)文件讀取數(shù)據(jù),對(duì)數(shù)據(jù)進(jìn)行一些處理,把結(jié)果寫出到其他地方。因?yàn)檫@個(gè)模式如此常見(jiàn),所以就產(chǎn)生了一些更快捷的方式。readFile 和 writeFile 就是這樣的快捷方式,他們可以把文件直接當(dāng)作字符串進(jìn)行處理。它們內(nèi)部會(huì)處理好打開文件,關(guān)閉文件,讀寫數(shù)據(jù)的所有細(xì)節(jié)問(wèn)題。readFile內(nèi)部就是使用hGetContents的。
你能猜出這些函數(shù)的類型么?我們用ghci來(lái)看看:
ghci> :type readFile
readFile :: FilePath -> IO String
ghci> :type writeFile
writeFile :: FilePath -> String -> IO ()
這里是一個(gè)使用readFile和writeFile的例子程序:
-- file: ch07/toupper-lazy3.hs
import Data.Char(toUpper)
main = do
inpStr <- readFile "input.txt"
writeFile "output.txt" (map toUpper inpStr)
看,程序的核心部分只有兩行!readFile 返回一個(gè)惰性String,我們把它存儲(chǔ)在 inpStr里。然后處理它,并傳給 writeFile 寫到輸出文件。
readFile和writeFile都沒(méi)有向你暴露文件句柄,因此也就不需要手動(dòng)調(diào)用 hClose。readFile內(nèi)部使用hGetContents,當(dāng)返回的String被垃圾回收,或者輸入文件被全部讀取完畢之后,隱藏文件句柄將會(huì)自動(dòng)被關(guān)閉。writeFile也會(huì)在輸入的String全部寫完后自動(dòng)關(guān)閉文件句柄。
關(guān)于惰性輸出
現(xiàn)在你應(yīng)該了解如何在Haskell中進(jìn)行惰性輸入了。但是惰性輸出又是怎么回事呢?
我們已經(jīng)知道,在Haskell中,只有到真正被使用的時(shí)候才會(huì)對(duì)表達(dá)式進(jìn)行求值。像writeFile 和putStr這樣的函數(shù)把傳入的整個(gè)字符串寫到輸出文件,因此整個(gè)字符串都必須被求值。因此可以保證putStr的參數(shù)將會(huì)被完全求值。
但是這對(duì)惰性輸入又意味著什么呢?在上面的例子里,對(duì)putStr或writeFile的調(diào)用是否會(huì)立刻將整個(gè)字符串強(qiáng)制載入到內(nèi)存中?
答案是否定的。 putStr(和所有類似的輸出函數(shù))負(fù)責(zé)在數(shù)據(jù)可用的時(shí)候把他們寫入到輸出文件,但它們沒(méi)有必要保留那些已經(jīng)被寫出的數(shù)據(jù),只要程序中沒(méi)有其他部分需要這些數(shù)據(jù),它們的內(nèi)存就會(huì)被立刻釋放。某種意義上來(lái)說(shuō),你可以把在readFile和writeFile 間傳遞的字符串想象成連接兩者的管道。數(shù)據(jù)從一端,經(jīng)過(guò)一些變形,流向另一端。
你可以給 toupper-lazy3.hs 生成一個(gè)大的 input.txt,來(lái)自己驗(yàn)證這一點(diǎn)。這會(huì)需要一點(diǎn)時(shí)間來(lái)處理,但是在它處理過(guò)程中你會(huì)看到一個(gè)很低且穩(wěn)定的內(nèi)存占用。
interact
我們已經(jīng)學(xué)到readFile 和 writeFile 是如何處理讀取文件,進(jìn)行轉(zhuǎn)換,再寫到另一個(gè)文件的這種常見(jiàn)情況。還有比這更常見(jiàn)的一種情況:從標(biāo)準(zhǔn)輸入讀入,進(jìn)行轉(zhuǎn)換,再將結(jié)果寫到標(biāo)準(zhǔn)輸出。要處理這種情況,有一個(gè)叫 interact 的函數(shù)。interact的類型是 (String -> String) -> IO ()。即取一個(gè)類型為 String -> String 的函數(shù)作為參數(shù)。它會(huì)通過(guò) getContents 惰性的讀入標(biāo)準(zhǔn)輸入,并將結(jié)果傳入這個(gè)函數(shù),再把這個(gè)函數(shù)的返回發(fā)送到標(biāo)準(zhǔn)輸出。
我們可以把我們上面的例程轉(zhuǎn)換成通過(guò)interact來(lái)操作標(biāo)準(zhǔn)輸入輸出,像下面這樣:
-- file: ch07/toupper-lazy4.hs
import Data.Char(toUpper)
main = interact (map toUpper)
看,我們只用一行代碼就完成了轉(zhuǎn)換!要得到和之前例子相同的效果,需要像下面這樣來(lái)執(zhí)行:
$ runghc toupper-lazy4.hs < input.txt > output.txt
或者,如果想在屏幕上看到輸出,可以這樣:
$ runghc toupper-lazy4.hs < input.txt
如果你希望程序交互性地處理輸入輸出的話,執(zhí)行 runghc toupper-lazy4.hs 即可,不要帶其他命令行參數(shù)。可以看到每輸入一個(gè)字符,它都用大寫形式回顯出來(lái)。不過(guò)這種情況下不同緩沖模式可能會(huì)對(duì)程序的具體行為有所影響,關(guān)于緩沖的更多信息參見(jiàn)本章后面的“緩沖”一節(jié)。如果你遇到輸入一行結(jié)束時(shí)才回顯,或者暫時(shí)沒(méi)有進(jìn)行回顯,那就是緩沖惹得禍了。
我們也可以用 interact 來(lái)編寫一些簡(jiǎn)單的交互程序。我們先上一個(gè)簡(jiǎn)單的例子:把輸入轉(zhuǎn)換成大寫并在前面添加一行文本。
-- file: ch07/toupper-lazy5.hs
import Data.Char(toUpper)
main = interact (map toUpper . (++) "Your data, in uppercase, is:\n\n")
[Tip] Tip
如果對(duì) . 操作符的使用感到迷惑,不妨參考下“通過(guò)函數(shù)組合重用代碼”一節(jié)。
這里我們?cè)谳敵龅拈_頭添加了一個(gè)字符串。但是你可以看出這里面有什么問(wèn)題么?
因?yàn)槲覀儗?duì) (++) 的結(jié)果調(diào)用 map,使得我們添加的這一串頭部字符串也變成大寫的了。修改一下:
-- file: ch07/toupper-lazy6.hs
import Data.Char(toUpper)
main = interact ((++) "Your data, in uppercase, is:\n\n" .
map toUpper)
它把頭部移到了map 之外。
用 interact 做過(guò)濾器
interact也常常用作過(guò)濾器。假設(shè)你要讀入一個(gè)文件,并將所有包含字母"a"的每一行打印出來(lái)。這里是一個(gè)使用 interact 的方法:
-- file: ch07/filter.hs
main = interact (unlines . filter (elem 'a') . lines)
這里引出了三個(gè)還不熟悉的函數(shù)。我們?cè)趃hci中看下它們的類型:
ghci> :type lines
lines :: String -> [String]
ghci> :type unlines
unlines :: [String] -> String
ghci> :type elem
elem :: (Eq a) => a -> [a] -> Bool
你可以通過(guò)它們的類型猜出這些函數(shù)是做什么的么?猜不到的話,也可以從“熱身:可移植的文本行切分”一節(jié)以及“特殊的字符串處理函數(shù)”一節(jié)中找到解釋。你會(huì)經(jīng)常看到在I/O動(dòng)作中使用 lines 和 unlines 函數(shù)。最后,elem 函數(shù)取一個(gè)元素和一個(gè)列表,如果這個(gè)元素出現(xiàn)在列表中的話就返回True。
用我們的標(biāo)準(zhǔn)輸入數(shù)據(jù)來(lái)運(yùn)行它:
$ runghc filter.hs < input.txt
I like Haskell
Haskell is great
果然輸出了包含 "a" 字符的兩行。惰性過(guò)濾器是Haskell中強(qiáng)大的處理方式。可以想想看,一個(gè)過(guò)濾器——如標(biāo)準(zhǔn)Unix程序 grep——聽(tīng)上去就像一個(gè)函數(shù)。它獲取一些輸入,進(jìn)行一些計(jì)算,產(chǎn)生一些可預(yù)測(cè)的輸出。
IO Monad
前面我們已經(jīng)看了很多使用Haskell語(yǔ)言進(jìn)行I/O處理的例子,下面讓我們退一步,來(lái)考慮下I/O是如何與更廣泛的Haskell語(yǔ)言進(jìn)行融合的。
因?yàn)镠askell是一種純函數(shù)的語(yǔ)言,每次用相同的參數(shù)調(diào)用一個(gè)函數(shù)都會(huì)返回相同的結(jié)果。而且,函數(shù)也不會(huì)改變程序的全局狀態(tài)。
那您可能會(huì)好奇I/O是如何融合進(jìn)這樣一種情況的呢,因?yàn)楹茱@然,如果從鍵盤上讀入一行輸入,讀取輸入的函數(shù)是不可能每次都返回相同的結(jié)果的。而且,I/O本來(lái)就是要改變狀態(tài)的,它可以點(diǎn)亮終端上的像素,也可以讓打印機(jī)進(jìn)行輸出,甚至可以讓一個(gè)包裹從倉(cāng)庫(kù)發(fā)往另一塊陸地。I/O不光是改變程序的狀態(tài),它甚至改變了世界的狀態(tài)。
動(dòng)作
大多數(shù)語(yǔ)言并不對(duì)函數(shù)進(jìn)行純粹還是不純粹之分,但是Haskell的函數(shù)是數(shù)學(xué)意義上的函數(shù):它們就是純粹的計(jì)算,不能改變?nèi)魏瓮獠康氖挛铩4送?#xff0c;計(jì)算可以在任何時(shí)間進(jìn)行執(zhí)行,如果其結(jié)果永遠(yuǎn)沒(méi)有地方用到,那就不用進(jìn)行計(jì)算。
所以,我們需要些其他的工具來(lái)操作I/O。這些工具在Haskell中被稱做動(dòng)作。動(dòng)作和函數(shù)類似,定義的時(shí)候不做任何事情,只有被調(diào)用時(shí)才會(huì)執(zhí)行一些任務(wù)。I/O動(dòng)作通過(guò) IO monad 進(jìn)行定義。Monad是一種把函數(shù)串接起來(lái)的強(qiáng)大機(jī)制,會(huì)在第14章Monad 中進(jìn)行介紹。不過(guò)理解monad并不是理解I/O的必要條件,只要把I/O想象成打上了"IO"標(biāo)簽的動(dòng)作就可以了。我們來(lái)看幾個(gè)例子:
ghci> :type putStrLn
putStrLn :: String -> IO ()
ghci> :type getLine
getLine :: IO String
putStrLn 的類型和一般的函數(shù)沒(méi)有什么兩樣,它接受一個(gè)參數(shù)并返回一個(gè) IO () 。 這個(gè) IO () 就是一個(gè)動(dòng)作。你還可以在純函數(shù)式代碼中存儲(chǔ)或傳遞動(dòng)作,不過(guò)這種情況并不常見(jiàn)。動(dòng)作在被調(diào)用之前什么都不做。再來(lái)看一個(gè)例子:
-- file: ch07/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
-- Take a list of actions, and execute each of them in turn.
runall :: [IO ()] -> IO ()
runall [] = return ()
runall (firstelem:remainingelems) =
do firstelem
runall remainingelems
main = do str2action "Start of the program"
printitall
str2action "Done!"
str2action 函數(shù)取一個(gè)參數(shù),并返回一個(gè) IO ()。在main 函數(shù)的末尾可以看出,可以直接在另一個(gè)動(dòng)作中對(duì)它進(jìn)行調(diào)用,它會(huì)立刻輸出一行。你也可以在純函數(shù)式代碼中存儲(chǔ)它但不能執(zhí)行。list2actions 函數(shù)就是這樣的例子,在 str2action 上使用 map ,返回一個(gè)動(dòng)作的列表,就和對(duì)待其他純的數(shù)據(jù)一樣。可以看到 printitall 函數(shù)完全是通過(guò)純函數(shù)實(shí)現(xiàn)的。
雖然定義了 printitall,但在它被求值并不會(huì)被執(zhí)行。注意在main中我們把 str2action當(dāng)作 I/O動(dòng)作來(lái)執(zhí)行,之前我們?cè)贗/O monad外面也使用過(guò)它來(lái)將結(jié)果組裝進(jìn)一個(gè)列表中。
你可以這樣來(lái)理解:do 程序塊中的每一個(gè)語(yǔ)句(除了 let)都必須產(chǎn)生一個(gè)I/O 動(dòng)作來(lái)執(zhí)行。
調(diào)用printitall將最終執(zhí)行所有的動(dòng)作。實(shí)際上,因?yàn)镠askell是惰性的,那些動(dòng)作也是到這個(gè)時(shí)候才被生成出來(lái)。
當(dāng)運(yùn)行這個(gè)程序時(shí),輸出如下:
Data: Start of the program
Data: 1
Data: 2
Data: 3
Data: 4
Data: 5
Data: 6
Data: 7
Data: 8
Data: 9
Data: 10
Data: Done!
這個(gè)例程還可以用更簡(jiǎn)潔的方式來(lái)寫。看下面這個(gè):
-- file: ch07/actions2.hs
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!"
注意 str2action 中函數(shù)組合操作符的使用。在 main 中有一個(gè)對(duì) mapM_ 的調(diào)用。這個(gè)函數(shù)類似 map。它取一個(gè)函數(shù)和一個(gè)列表作為輸入。提供給mapM_的函數(shù)是一個(gè)I/O動(dòng)作,它在列表中的每一個(gè)項(xiàng)目上進(jìn)行執(zhí)行。 mapM_ 拋棄動(dòng)作執(zhí)行的結(jié)果,當(dāng)然你也可以使用 mapM 來(lái)獲取IO動(dòng)作返回的結(jié)果。我們看一下它們的類型:
ghci> :type mapM
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
ghci> :type mapM_
mapM_ :: (Monad m) => (a -> m b) -> [a] -> m ()
[Tip] Tip
這些函數(shù)不止可以應(yīng)用在I/O上;它們也可以應(yīng)用在任何Monad上。現(xiàn)在,每當(dāng)看到"M",都要想到"IO"。而且,以下劃線結(jié)尾的函數(shù)一般都會(huì)將動(dòng)作的結(jié)果丟棄。
為什么有了map了還要有mapM呢?因?yàn)閙ap是純函數(shù),它返回一個(gè)列表,它不能直接執(zhí)行動(dòng)作。而mapM是IO monad中的工具,它可以直接執(zhí)行動(dòng)作。
回到main函數(shù),mapM_ 在numbers的每一個(gè)元素上調(diào)用 (str2action . show) 。show把一個(gè)數(shù)字轉(zhuǎn)換成字符串,str2action把一個(gè)字符串轉(zhuǎn)換成一個(gè)動(dòng)作。mapM_把這些單獨(dú)的動(dòng)作打包成一個(gè)系列的動(dòng)作,然后執(zhí)行他們,把數(shù)據(jù)打印出來(lái)。
順序
do語(yǔ)句塊是把動(dòng)作連接起來(lái)的縮寫方式。有兩個(gè)操作符可以用來(lái)代替 do 語(yǔ)句塊: >> 和 >>=。在ghci中看下它們的類型:
ghci> :type (>>)
(>>) :: (Monad m) => m a -> m b -> m b
ghci> :type (>>=)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
>> 操作符把兩個(gè)動(dòng)作順序連接在一起:先執(zhí)行第一個(gè)動(dòng)作,然后執(zhí)行第二個(gè)。最終返回第二個(gè)動(dòng)作執(zhí)行的結(jié)果。第一個(gè)動(dòng)作執(zhí)行的結(jié)果就被拋棄掉了。這類似于在do語(yǔ)句塊中包含簡(jiǎn)單的一行。可以用 putStrLn "line 1" >> putStrLn "line 2" 來(lái)試驗(yàn)下。它會(huì)打印出兩行,拋棄掉第一個(gè) putStrLn的結(jié)果,并給出第二個(gè)的結(jié)果。
>>= 操作符執(zhí)行一個(gè)動(dòng)作,然后把它的結(jié)果傳遞給一個(gè)函數(shù),這個(gè)函數(shù)會(huì)返回一個(gè)動(dòng)作,然后這個(gè)動(dòng)作也被執(zhí)行,整個(gè)表達(dá)式的結(jié)果是第二個(gè)動(dòng)作的結(jié)果。可以用 getLine >>= putStrLn 作為一個(gè)例子,它從鍵盤讀入一行,然后顯示出來(lái)。
我們來(lái)重寫一個(gè)例子,讓它不包含do語(yǔ)句塊。還記得本章開始的這個(gè)例子么?
-- file: ch07/basicio.hs
main = do
putStrLn "Greetings! What is your name?"
inpStr <- getLine
putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!"
我們不使用do語(yǔ)句塊來(lái)寫:
-- file: ch07/basicio-nodo.hs
main =
putStrLn "Greetings! What is your name?" >>
getLine >>=
(\inpStr -> putStrLn $ "Welcome to Haskell, " ++ inpStr ++ "!")
Haskell編譯器內(nèi)部會(huì)將do語(yǔ)句編譯成上面這種形式。
[Tip] Tip
忘記如何用 \ (lambda表達(dá)式)了?參考“匿名函數(shù)(lambda)”一節(jié)。
return的本質(zhì)
本章前面的時(shí)候有提到return在它的表象下面還有更深層的含義。很多語(yǔ)言中都有一個(gè)叫return的關(guān)鍵字,它們將會(huì)中斷當(dāng)前函數(shù)的執(zhí)行,并把結(jié)果返回給調(diào)用者。
Haskell的return函數(shù)跟他們很不一樣。在Haskell里,return是用來(lái)把數(shù)據(jù)包裝到monad中的。具體到I/O的話,return就是用來(lái)把純的數(shù)據(jù)包裝到IO monad 中的。
那我們?yōu)槭裁匆M(jìn)行這樣的操作呢?記住任何依賴于 I/O的函數(shù)必須存在于 IO monad中。因此如果編寫一個(gè)函數(shù),它要執(zhí)行一些 I/O動(dòng)作,然后又要進(jìn)行一些純的計(jì)算操作,那就需要通過(guò) return 把純計(jì)算操作的返回結(jié)果轉(zhuǎn)換成適當(dāng)?shù)姆祷仡愋土恕7駝t要產(chǎn)生類型錯(cuò)誤的。比如:
-- file: ch07/return1.hs
import Data.Char(toUpper)
isGreen :: IO Bool
isGreen =
do putStrLn "Is green your favorite color?"
inpStr <- getLine
return ((toUpper . head $ inpStr) == 'Y')
我們有一個(gè)純的計(jì)算,它產(chǎn)生一個(gè)布爾值。這個(gè)計(jì)算被傳遞給return,return把它放到 IO monad 中。因?yàn)樗?do語(yǔ)句塊的最后一個(gè)值,所以它成了 isGreen 的返回值,而不是因?yàn)槲覀兪褂昧藃eturn函數(shù)。
這是上面這個(gè)程序的另一個(gè)版本,它把純的計(jì)算提取到一個(gè)獨(dú)立的函數(shù)中。這有助于保持純函數(shù)式代碼的分離,并且使程序的意圖更加明確。
-- file: ch07/return2.hs
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)
最后,下面這個(gè)的例子展示了return并不一定要出現(xiàn)在do語(yǔ)句塊的末尾。在實(shí)踐中,它常常在末尾,但并非必需。
-- file: ch07/return3.hs
returnTest :: IO ()
returnTest =
do one <- return 1
let two = 2
putStrLn $ show (one + two)
注意我們把 <- 和 return進(jìn)行組合,而 let 和字面量進(jìn)行組合。這是因?yàn)橐M(jìn)行相加操作,需要使用純的值。所以需要用 <- 把純的值從 monad 中“拉”出來(lái),它是return的逆運(yùn)算。在ghci中執(zhí)行它,可以看到顯示出了3。
Haskell真的是命令式的么?
這些do語(yǔ)句塊看上去很像命令式語(yǔ)言。畢竟,大多數(shù)時(shí)候你都是給出一些順序執(zhí)行的命令。
但是Haskell的核心依然是一種惰性語(yǔ)言。雖然經(jīng)常需要順序的執(zhí)行I/O動(dòng)作,但它是通過(guò)Haskell中已有的工具來(lái)進(jìn)行的。而且Haskell通過(guò) IO monad 將I/O和語(yǔ)言的其他部分很好地分離開來(lái)。
惰性I/O 的副作用
本章前面講到了 hGetContents。我們解釋說(shuō)它返回的 String 可以在純函數(shù)式代碼中使用。
我們需要對(duì)究竟什么是副作用做出更明確的解釋。我們說(shuō)的Haskell沒(méi)有副作用,它究竟是什么意思呢?
在從某種意義上來(lái)說(shuō),“副作用”不可能完全避免的。一個(gè)有問(wèn)題的循環(huán),即使完全使用純函數(shù)式代碼進(jìn)行書寫,也有可能導(dǎo)致系統(tǒng)內(nèi)存耗盡并令機(jī)器崩潰,或者導(dǎo)致內(nèi)存數(shù)據(jù)被交換到磁盤。
當(dāng)我們說(shuō)沒(méi)有副作用時(shí),我們指的是Haskell中純的代碼不能執(zhí)行引發(fā)副作用的命令。純函數(shù)不能修改全局變量,不能進(jìn)行I/O請(qǐng)求,也不能執(zhí)行命令來(lái)破壞系統(tǒng)。
當(dāng)我們把hGetContents返回的String傳遞給純函數(shù),函數(shù)并不知道這個(gè)字符串是來(lái)自磁盤的文件。它還是表現(xiàn)得跟平常一樣,當(dāng)然處理那個(gè)String會(huì)導(dǎo)致I/O命令的執(zhí)行。但它們不是由純函數(shù)執(zhí)行的;它們是純函數(shù)進(jìn)行處理的過(guò)程所導(dǎo)致的一個(gè)結(jié)果,就和導(dǎo)致內(nèi)存和磁盤進(jìn)行交換的那個(gè)情況一樣。
在某些情況下,您可能需要對(duì)I/O何時(shí)發(fā)生進(jìn)行更精確的控制。也許你正在交互式地讀取用戶輸入的數(shù)據(jù),或者是通過(guò)管道從其他程序讀取輸入,這時(shí)需要直接與用戶通信。在這些情況下,hGetContents可能就不那么合適了。
緩沖
I/O子系統(tǒng)是現(xiàn)代計(jì)算機(jī)中最慢的部分。寫磁盤花費(fèi)的時(shí)間比寫內(nèi)存高出上千倍。通過(guò)網(wǎng)絡(luò)寫入還要再慢上成百上千倍。即使你的操作不直接導(dǎo)致磁盤操作——比如說(shuō)因?yàn)閿?shù)據(jù)被緩存了——但I(xiàn)/O依然會(huì)進(jìn)行一下系統(tǒng)調(diào)用,還是會(huì)把速度拖慢了。
為此,現(xiàn)代的操作系統(tǒng)和編程語(yǔ)言都提供一些工具,幫助程序更高效的處理I/O。操作系統(tǒng)通常會(huì)進(jìn)行緩存,把常用的數(shù)據(jù)塊存放在內(nèi)存以便進(jìn)行快速存取。
編程語(yǔ)言通常也會(huì)進(jìn)行緩沖。這意味著即使代碼一次只處理一個(gè)字節(jié),它們也可能會(huì)向操作系統(tǒng)請(qǐng)求一大塊數(shù)據(jù)。這樣可以獲得很大的性能提升,因?yàn)槊看螌?duì)操作系統(tǒng)進(jìn)行I/O請(qǐng)求都會(huì)帶來(lái)很多開銷。緩沖可以讓我們讀取相同數(shù)量數(shù)據(jù)時(shí)產(chǎn)生的I/O請(qǐng)求少的多。
Haskell也在它的I/O系統(tǒng)里提供了緩沖。很多情況下,緩沖是默認(rèn)的。只是在前面的章節(jié)中,我們一直沒(méi)有提到它的存在。Haskell一般情況都能夠很好地選取合適的默認(rèn)模式。但是這個(gè)默認(rèn)選擇很少是速度最快的。如果對(duì)你的代碼來(lái)說(shuō)I/O的速度很重要的話,手動(dòng)修改緩沖模式可以給程序的性能帶來(lái)很大提高。
緩沖模式
Haskell中有三種緩沖模式:NoBuffering , LineBuffering ,和BlockBuffering,他們通過(guò) BufferMode 類型進(jìn)行定義。
NoBuffering,正如它的名字所說(shuō):不進(jìn)行緩沖。使用 hGetLine 這樣的函數(shù)讀取數(shù)據(jù)的話,一次只會(huì)從操作系統(tǒng)讀取一個(gè)字符。寫數(shù)據(jù)的時(shí)候也會(huì)立刻數(shù)據(jù)把數(shù)據(jù)寫出去,并且經(jīng)常是一次只寫一個(gè)字符。因此NoBuffering通常性能很差,不適合一般用途使用。
LineBuffering 在出現(xiàn)換行符或者數(shù)據(jù)太多時(shí),才將緩沖數(shù)據(jù)寫出,在輸入時(shí),它嘗試讀取數(shù)據(jù)塊的所有數(shù)據(jù)直到遇到換行符。當(dāng)從終端讀取時(shí),每當(dāng)按下回車時(shí)它就會(huì)立刻返回?cái)?shù)據(jù)。它往往是合理的默認(rèn)設(shè)置。
BlockBuffering讓Haskell讀寫固定大小的數(shù)據(jù)塊。在處理大批量數(shù)據(jù)時(shí),它的性能是最好的,即使數(shù)據(jù)是面向行的。但是它不能用在交互程序上,因?yàn)樗趬K讀取滿之前是阻塞的。BlockBuffering接收一個(gè)Maybe類型參數(shù),如果是 Nothing,它使用Haskell實(shí)現(xiàn)預(yù)定義的緩沖區(qū)大小。或者,你可以用 Just 4096 這樣的設(shè)置來(lái)把緩沖區(qū)設(shè)置成4096個(gè)字節(jié)。
默認(rèn)的緩沖區(qū)模式取決于操作系統(tǒng)和Haskell的實(shí)現(xiàn)。可以調(diào)用 hGetBuffering 來(lái)詢問(wèn)系統(tǒng)當(dāng)前的緩沖模式。可以用hSetBuffering 設(shè)置當(dāng)前的緩沖模式,它接收一個(gè)句柄和一個(gè) BufferMode。例如,可以寫 hSetBuffering stdin (BlockBuffering Nothing)。
沖刷緩沖區(qū)
不管是哪種類型的緩沖,都會(huì)常常需要強(qiáng)制把緩沖區(qū)的內(nèi)容寫出去。有時(shí)這會(huì)自動(dòng)進(jìn)行:比如調(diào)用 hClose的時(shí)候。但還是會(huì)有時(shí)候需要你手動(dòng)來(lái)調(diào)用 hFlush,這會(huì)強(qiáng)制把在緩沖區(qū)中等待的數(shù)據(jù)立刻寫出。如果句柄是網(wǎng)絡(luò)socket的話,這個(gè)操作會(huì)很有用,因?yàn)槟愠3?huì)需要立刻把數(shù)據(jù)寫出去。或者希望把數(shù)據(jù)寫到磁盤上,使得其他并發(fā)讀取它的程序可以立刻使用。
讀取命令行參數(shù)
很多命令行程序需要處理傳給它的參數(shù)。System.Environment.getArgs 返回 IO [String] ,列出了每個(gè)參數(shù)。它類似于 C 語(yǔ)言argv在索引1之后的部分。程序名(C里的 argv[0])可以用 System.Environment.getProgName 獲得。
System.Console.GetOpt 模塊提供了一些解析命令行選項(xiàng)的工具。如果你的程序擁有復(fù)雜的選項(xiàng),它就很有用了。在“命令行解析”一節(jié)中有使用它的例子。
環(huán)境變量
如果需要讀取環(huán)境變量,可以用System.Environment里面的兩個(gè)函數(shù):getEnv和getEnvironment。getEnv查看特定的變量,如果不存在就拋出異常。getEnvironment 把所有環(huán)境變量返回成 [(String, String)] 的值,之后就可以用lookup這類的函數(shù)來(lái)查找到你需要的環(huán)境變量了。
在Haskell中能夠跨平臺(tái)地設(shè)置環(huán)境變量的方法。如果在POSIX平臺(tái)上如Linux,你可以用System.Posix.Env模塊中的 putEnv 或者 setEnv。Windows上設(shè)置環(huán)境變量的方法沒(méi)有進(jìn)行定義。
[15] 后面你會(huì)看到它還具有更廣泛的應(yīng)用,但現(xiàn)在考慮這幾條就夠了。
[16] 值() 的類型也是()。
[17] 命令式語(yǔ)言的程序員可能會(huì)擔(dān)心這樣的遞歸調(diào)用會(huì)消耗大量的桟空間。在Haskell里,遞歸是常見(jiàn)的用法,編譯器足夠聰明可以通過(guò)尾遞歸優(yōu)化來(lái)避免消耗太多桟空間。
[18] 例如在混合程序的C語(yǔ)言部分存在bug。
[19] 與其他程序通過(guò)管道進(jìn)行互操作的更詳細(xì)信息,請(qǐng)看“擴(kuò)展程序:管道”一節(jié)。
[20] POSIX 程序員會(huì)有興趣知道它與C中的unlink()相對(duì)應(yīng)。
[21] hGetContents 將在“惰性I/O”一節(jié)討論
[22] 也有一個(gè)操作標(biāo)準(zhǔn)輸入的快捷函數(shù) getContents
[23] 更精確的說(shuō),它是從文件當(dāng)前位置到文件末尾的全部?jī)?nèi)容
[24] I/O錯(cuò)誤如磁盤空間滿了
[25] 技術(shù)上講,mapM把一組單獨(dú)的I/O動(dòng)作組合成一個(gè)大的動(dòng)作。大的動(dòng)作執(zhí)行時(shí)單獨(dú)的動(dòng)作被分別執(zhí)行。

轉(zhuǎn)載于:https://www.cnblogs.com/IBBC/archive/2011/07/25/2116321.html

總結(jié)

以上是生活随笔為你收集整理的Real World Haskell 第七章 I/O的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

国产精品无码久久av | 76少妇精品导航 | 乱中年女人伦av三区 | 亚洲人成人无码网www国产 | 精品国产乱码久久久久乱码 | 精品国产成人一区二区三区 | 国产精品理论片在线观看 | 亚洲性无码av中文字幕 | 青青草原综合久久大伊人精品 | 波多野结衣 黑人 | 一个人看的www免费视频在线观看 | 日韩少妇内射免费播放 | 爽爽影院免费观看 | 国产人妻精品午夜福利免费 | 亚洲欧洲中文日韩av乱码 | 色一情一乱一伦一视频免费看 | 久久午夜无码鲁丝片午夜精品 | 亚洲爆乳大丰满无码专区 | 国产真实夫妇视频 | aⅴ亚洲 日韩 色 图网站 播放 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产精品久久久久9999小说 | 99久久亚洲精品无码毛片 | yw尤物av无码国产在线观看 | 色综合久久久无码网中文 | 国产口爆吞精在线视频 | 国产av久久久久精东av | 特级做a爰片毛片免费69 | 国产真实伦对白全集 | 日本www一道久久久免费榴莲 | 99久久无码一区人妻 | 2020久久香蕉国产线看观看 | 亚洲国产精品无码久久久久高潮 | 老熟妇乱子伦牲交视频 | 成人无码精品1区2区3区免费看 | 波多野结衣高清一区二区三区 | 国产精品美女久久久久av爽李琼 | 55夜色66夜色国产精品视频 | 帮老师解开蕾丝奶罩吸乳网站 | 久久久久久久女国产乱让韩 | 2020久久香蕉国产线看观看 | 在线亚洲高清揄拍自拍一品区 | 亚洲午夜福利在线观看 | 久久亚洲中文字幕无码 | 国产麻豆精品一区二区三区v视界 | 亚洲小说图区综合在线 | 国内少妇偷人精品视频免费 | 巨爆乳无码视频在线观看 | 亚洲 另类 在线 欧美 制服 | 无码av中文字幕免费放 | 人妻天天爽夜夜爽一区二区 | 久久精品99久久香蕉国产色戒 | 77777熟女视频在线观看 а天堂中文在线官网 | 免费观看黄网站 | 亚洲一区二区三区国产精华液 | 日日干夜夜干 | 麻豆人妻少妇精品无码专区 | 日本精品人妻无码77777 天堂一区人妻无码 | 在教室伦流澡到高潮hnp视频 | 国产av一区二区三区最新精品 | 天堂一区人妻无码 | 亚洲乱码日产精品bd | 亚洲区小说区激情区图片区 | 久久久久久久久888 | 中文字幕久久久久人妻 | 亚洲精品综合五月久久小说 | 中文亚洲成a人片在线观看 | 中文字幕无码乱人伦 | 亲嘴扒胸摸屁股激烈网站 | 性欧美疯狂xxxxbbbb | 一个人看的www免费视频在线观看 | 欧美精品在线观看 | 98国产精品综合一区二区三区 | 好男人www社区 | 5858s亚洲色大成网站www | 日日天干夜夜狠狠爱 | 久久久久se色偷偷亚洲精品av | 国产精品无码mv在线观看 | 真人与拘做受免费视频 | 激情五月综合色婷婷一区二区 | 牲欲强的熟妇农村老妇女视频 | 成年美女黄网站色大免费全看 | 亚洲乱码中文字幕在线 | 中文字幕无码日韩欧毛 | 天堂а√在线地址中文在线 | 男人的天堂av网站 | 成人免费视频视频在线观看 免费 | 秋霞特色aa大片 | 久久99久久99精品中文字幕 | 精品久久久无码人妻字幂 | 国产激情艳情在线看视频 | 国产av久久久久精东av | 国产精品人人妻人人爽 | 妺妺窝人体色www婷婷 | 老子影院午夜伦不卡 | 欧美三级不卡在线观看 | 亚洲一区二区三区香蕉 | 人人妻人人澡人人爽人人精品 | 全黄性性激高免费视频 | 午夜福利一区二区三区在线观看 | 丝袜 中出 制服 人妻 美腿 | 人人妻人人澡人人爽精品欧美 | 国产在线无码精品电影网 | 亚洲自偷自偷在线制服 | 漂亮人妻洗澡被公强 日日躁 | 国产精品无码永久免费888 | 99麻豆久久久国产精品免费 | 18无码粉嫩小泬无套在线观看 | 性欧美大战久久久久久久 | 国产suv精品一区二区五 | 国模大胆一区二区三区 | 日产精品99久久久久久 | 久久国语露脸国产精品电影 | 4hu四虎永久在线观看 | 国产亚洲精品精品国产亚洲综合 | 丰满护士巨好爽好大乳 | 无码一区二区三区在线观看 | 亚洲 高清 成人 动漫 | 亚洲中文字幕无码中文字在线 | 国产在线无码精品电影网 | 熟妇人妻无码xxx视频 | 国产一区二区三区精品视频 | 中文字幕+乱码+中文字幕一区 | 中文字幕乱码人妻二区三区 | 女人被男人躁得好爽免费视频 | 国产精品毛片一区二区 | 国産精品久久久久久久 | 久久午夜无码鲁丝片 | 精品无码一区二区三区爱欲 | ass日本丰满熟妇pics | 黄网在线观看免费网站 | 日本一区二区更新不卡 | 亚洲aⅴ无码成人网站国产app | 麻豆精产国品 | 亚洲精品综合一区二区三区在线 | 啦啦啦www在线观看免费视频 | 国产精品人人妻人人爽 | 激情爆乳一区二区三区 | 国产凸凹视频一区二区 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | av无码久久久久不卡免费网站 | 装睡被陌生人摸出水好爽 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 一本加勒比波多野结衣 | 装睡被陌生人摸出水好爽 | 精品水蜜桃久久久久久久 | 人妻中文无码久热丝袜 | 亚洲国产欧美国产综合一区 | 在线a亚洲视频播放在线观看 | av无码不卡在线观看免费 | 午夜无码人妻av大片色欲 | 国产亚洲日韩欧美另类第八页 | 精品国产一区二区三区av 性色 | 曰韩少妇内射免费播放 | 亚拍精品一区二区三区探花 | 久久99精品国产.久久久久 | 国产片av国语在线观看 | 领导边摸边吃奶边做爽在线观看 | 亚洲精品中文字幕乱码 | 亚洲色成人中文字幕网站 | 久久精品中文闷骚内射 | 成人欧美一区二区三区黑人免费 | 成熟妇人a片免费看网站 | 蜜臀aⅴ国产精品久久久国产老师 | 蜜桃臀无码内射一区二区三区 | 无码人中文字幕 | 蜜臀aⅴ国产精品久久久国产老师 | 成人亚洲精品久久久久软件 | 国产成人精品久久亚洲高清不卡 | 7777奇米四色成人眼影 | 久久综合狠狠综合久久综合88 | 国产xxx69麻豆国语对白 | 草草网站影院白丝内射 | 欧洲熟妇色 欧美 | 无码人妻少妇伦在线电影 | 成人无码影片精品久久久 | 亚洲区小说区激情区图片区 | 欧洲熟妇精品视频 | 中文字幕久久久久人妻 | 天堂久久天堂av色综合 | 色综合久久中文娱乐网 | 99久久精品国产一区二区蜜芽 | 亚洲va欧美va天堂v国产综合 | 亚洲精品www久久久 | 亚洲国产精品无码一区二区三区 | 正在播放老肥熟妇露脸 | 久久久久免费看成人影片 | 亚洲精品综合一区二区三区在线 | 欧美人妻一区二区三区 | 欧美三级不卡在线观看 | 精品国产一区av天美传媒 | 任你躁在线精品免费 | 中文字幕无线码 | 国产99久久精品一区二区 | 无码人妻精品一区二区三区不卡 | 成年美女黄网站色大免费视频 | 性做久久久久久久久 | 亚洲成a人片在线观看无码3d | 波多野结衣高清一区二区三区 | 人人澡人人透人人爽 | 国产色在线 | 国产 | 中文字幕乱码中文乱码51精品 | 无码毛片视频一区二区本码 | 中文字幕无码视频专区 | 人人爽人人澡人人高潮 | 国产黄在线观看免费观看不卡 | 久久精品国产99久久6动漫 | 香蕉久久久久久av成人 | 国产xxx69麻豆国语对白 | 国产极品视觉盛宴 | 性欧美牲交xxxxx视频 | 日本精品人妻无码77777 天堂一区人妻无码 | 丰腴饱满的极品熟妇 | 欧美人与动性行为视频 | 骚片av蜜桃精品一区 | 男人的天堂av网站 | 熟妇人妻无乱码中文字幕 | 国产欧美精品一区二区三区 | 奇米影视888欧美在线观看 | 丰满少妇高潮惨叫视频 | 在线精品亚洲一区二区 | 亚洲熟妇自偷自拍另类 | 亚洲国产午夜精品理论片 | 精品久久久久久人妻无码中文字幕 | 亚洲自偷自拍另类第1页 | 国产亚av手机在线观看 | 欧美丰满熟妇xxxx性ppx人交 | 久久午夜无码鲁丝片午夜精品 | 国产亚洲精品精品国产亚洲综合 | 无码人中文字幕 | 免费无码av一区二区 | 无码精品人妻一区二区三区av | 色综合久久久无码中文字幕 | 日本护士xxxxhd少妇 | 国产97在线 | 亚洲 | 熟妇女人妻丰满少妇中文字幕 | 成人精品视频一区二区三区尤物 | 国产精品鲁鲁鲁 | 超碰97人人做人人爱少妇 | 无码福利日韩神码福利片 | 青春草在线视频免费观看 | 无遮挡啪啪摇乳动态图 | 久久久精品人妻久久影视 | 精品国产一区av天美传媒 | 亚洲欧美日韩成人高清在线一区 | 亚洲春色在线视频 | 国产高潮视频在线观看 | 日韩精品无码免费一区二区三区 | 人妻aⅴ无码一区二区三区 | 激情爆乳一区二区三区 | 日韩 欧美 动漫 国产 制服 | 久久久久久久人妻无码中文字幕爆 | 免费观看激色视频网站 | 日韩 欧美 动漫 国产 制服 | 亚洲精品中文字幕 | 成人精品视频一区二区三区尤物 | 国产精品无码成人午夜电影 | 自拍偷自拍亚洲精品10p | 国产一区二区三区精品视频 | 奇米影视888欧美在线观看 | 黑人大群体交免费视频 | 欧美肥老太牲交大战 | 一本久道久久综合婷婷五月 | 亚洲精品鲁一鲁一区二区三区 | 精品国产福利一区二区 | 精品熟女少妇av免费观看 | 免费看少妇作爱视频 | 欧美日韩精品 | 人妻少妇被猛烈进入中文字幕 | 99er热精品视频 | 成人精品视频一区二区三区尤物 | 亚洲熟妇色xxxxx亚洲 | 天干天干啦夜天干天2017 | 夜夜高潮次次欢爽av女 | 亚洲一区二区三区香蕉 | 又粗又大又硬毛片免费看 | 色婷婷香蕉在线一区二区 | 日本一本二本三区免费 | 成人性做爰aaa片免费看不忠 | 青草青草久热国产精品 | 无码人妻精品一区二区三区不卡 | 国精产品一区二区三区 | 国产成人无码午夜视频在线观看 | 色五月五月丁香亚洲综合网 | 天堂а√在线中文在线 | 99久久久无码国产aaa精品 | а√天堂www在线天堂小说 | 久久久国产精品无码免费专区 | 日本免费一区二区三区最新 | 51国偷自产一区二区三区 | 久久亚洲a片com人成 | 一本久久伊人热热精品中文字幕 | 国产真人无遮挡作爱免费视频 | 精品国产aⅴ无码一区二区 | 国产成人精品视频ⅴa片软件竹菊 | 国精品人妻无码一区二区三区蜜柚 | √天堂资源地址中文在线 | 亚洲色成人中文字幕网站 | 久久久久亚洲精品男人的天堂 | 国产内射老熟女aaaa | 图片小说视频一区二区 | 国产乱人伦偷精品视频 | 久久国产精品二国产精品 | 十八禁视频网站在线观看 | 高清无码午夜福利视频 | 午夜无码区在线观看 | 丰满人妻精品国产99aⅴ | 亚洲精品中文字幕 | 精品国偷自产在线视频 | 国产精品美女久久久久av爽李琼 | 久久视频在线观看精品 | 99riav国产精品视频 | 中文字幕无线码免费人妻 | 精品久久久中文字幕人妻 | 青春草在线视频免费观看 | 亚洲 日韩 欧美 成人 在线观看 | 亚洲一区二区三区偷拍女厕 | 特级做a爰片毛片免费69 | 在线成人www免费观看视频 | 日本乱偷人妻中文字幕 | 国产在线一区二区三区四区五区 | 丰满少妇高潮惨叫视频 | 乱码av麻豆丝袜熟女系列 | 高潮毛片无遮挡高清免费 | 久久熟妇人妻午夜寂寞影院 | 国产精品二区一区二区aⅴ污介绍 | 久久综合九色综合欧美狠狠 | 性色欲网站人妻丰满中文久久不卡 | 好屌草这里只有精品 | 精品人妻中文字幕有码在线 | 国产亚洲精品久久久久久久久动漫 | 久久国产自偷自偷免费一区调 | 小鲜肉自慰网站xnxx | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 少妇人妻大乳在线视频 | 久久综合激激的五月天 | 丁香花在线影院观看在线播放 | 国产肉丝袜在线观看 | 免费看少妇作爱视频 | 亚洲精品久久久久中文第一幕 | 中文字幕av日韩精品一区二区 | 久久久久亚洲精品男人的天堂 | 野外少妇愉情中文字幕 | 99久久精品日本一区二区免费 | 亚洲成色www久久网站 | 亚洲理论电影在线观看 | 偷窥日本少妇撒尿chinese | 奇米影视888欧美在线观看 | 亚洲一区二区三区四区 | 国产亚洲欧美在线专区 | 亚洲理论电影在线观看 | 精品乱子伦一区二区三区 | 欧美喷潮久久久xxxxx | 国产av一区二区三区最新精品 | 国产精品资源一区二区 | 国产成人综合色在线观看网站 | av香港经典三级级 在线 | 国产人妻精品一区二区三区不卡 | 真人与拘做受免费视频 | 久久久久亚洲精品男人的天堂 | 国产麻豆精品一区二区三区v视界 | 日韩av无码一区二区三区 | 中文字幕无码日韩专区 | 少妇厨房愉情理9仑片视频 | 精品午夜福利在线观看 | 色综合久久88色综合天天 | 成人aaa片一区国产精品 | 狠狠噜狠狠狠狠丁香五月 | 亚洲精品午夜国产va久久成人 | 国产香蕉尹人综合在线观看 | 少妇人妻大乳在线视频 | 精品国产乱码久久久久乱码 | 无码免费一区二区三区 | 免费人成网站视频在线观看 | 激情国产av做激情国产爱 | 亚洲国精产品一二二线 | 97无码免费人妻超级碰碰夜夜 | 中文字幕日产无线码一区 | 国产精品高潮呻吟av久久4虎 | 荫蒂被男人添的好舒服爽免费视频 | 东京无码熟妇人妻av在线网址 | 日产国产精品亚洲系列 | 国产av无码专区亚洲awww | 极品尤物被啪到呻吟喷水 | 亚洲熟妇色xxxxx欧美老妇 | 婷婷六月久久综合丁香 | 少妇被黑人到高潮喷出白浆 | 亚洲天堂2017无码 | 精品亚洲成av人在线观看 | 国产免费观看黄av片 | 天天拍夜夜添久久精品 | 国产乱码精品一品二品 | 女人色极品影院 | 国内精品九九久久久精品 | 亚洲一区二区三区在线观看网站 | 亚洲精品午夜国产va久久成人 | 精品国产一区二区三区四区在线看 | 内射爽无广熟女亚洲 | 青草青草久热国产精品 | 欧美丰满老熟妇xxxxx性 | 粉嫩少妇内射浓精videos | 日本一区二区三区免费播放 | 99精品国产综合久久久久五月天 | 日本一卡2卡3卡四卡精品网站 | 99久久久国产精品无码免费 | 色综合久久88色综合天天 | 久久精品国产亚洲精品 | 久久久久国色av免费观看性色 | 色一情一乱一伦一区二区三欧美 | av无码久久久久不卡免费网站 | 两性色午夜免费视频 | 东京热无码av男人的天堂 | 人人超人人超碰超国产 | 国产偷国产偷精品高清尤物 | 中文字幕色婷婷在线视频 | 成 人 网 站国产免费观看 | 久久伊人色av天堂九九小黄鸭 | 18精品久久久无码午夜福利 | 欧美日本日韩 | 色欲人妻aaaaaaa无码 | 欧美三级a做爰在线观看 | 在线观看国产午夜福利片 | 国产熟女一区二区三区四区五区 | 日韩精品一区二区av在线 | 日本熟妇浓毛 | 2019nv天堂香蕉在线观看 | 狠狠cao日日穞夜夜穞av | 帮老师解开蕾丝奶罩吸乳网站 | 色婷婷久久一区二区三区麻豆 | 午夜理论片yy44880影院 | 少妇久久久久久人妻无码 | 少妇高潮一区二区三区99 | 国产午夜精品一区二区三区嫩草 | 成人一在线视频日韩国产 | 99久久久无码国产精品免费 | 国产人妻精品午夜福利免费 | 国产精品a成v人在线播放 | 亚洲人成影院在线无码按摩店 | 伊在人天堂亚洲香蕉精品区 | 玩弄少妇高潮ⅹxxxyw | 日日天日日夜日日摸 | 大乳丰满人妻中文字幕日本 | 少女韩国电视剧在线观看完整 | 国产三级精品三级男人的天堂 | 欧美xxxxx精品 | 久久成人a毛片免费观看网站 | a在线观看免费网站大全 | 精品乱子伦一区二区三区 | 免费无码的av片在线观看 | 国产亚洲精品久久久久久 | 国产精品.xx视频.xxtv | 欧美激情一区二区三区成人 | 香港三级日本三级妇三级 | 男女超爽视频免费播放 | 国产av久久久久精东av | 欧美阿v高清资源不卡在线播放 | 欧美性猛交内射兽交老熟妇 | 色综合视频一区二区三区 | 国产精品自产拍在线观看 | 天海翼激烈高潮到腰振不止 | 98国产精品综合一区二区三区 | 国产精品久久久久无码av色戒 | 一本久久a久久精品亚洲 | 55夜色66夜色国产精品视频 | 又粗又大又硬又长又爽 | 天天燥日日燥 | 国产人妻精品午夜福利免费 | 中文字幕人成乱码熟女app | 无码人妻丰满熟妇区五十路百度 | 我要看www免费看插插视频 | 十八禁真人啪啪免费网站 | 亚洲日韩中文字幕在线播放 | 中文字幕无码免费久久9一区9 | 人妻尝试又大又粗久久 | 亚洲 日韩 欧美 成人 在线观看 | √天堂中文官网8在线 | 少妇一晚三次一区二区三区 | 日本大乳高潮视频在线观看 | 欧美精品一区二区精品久久 | 97精品人妻一区二区三区香蕉 | 久久精品中文字幕一区 | 欧美日韩在线亚洲综合国产人 | 欧美熟妇另类久久久久久多毛 | 色一情一乱一伦一视频免费看 | 内射欧美老妇wbb | 少妇性l交大片 | 欧美 丝袜 自拍 制服 另类 | 露脸叫床粗话东北少妇 | 日韩亚洲欧美精品综合 | 狠狠色噜噜狠狠狠狠7777米奇 | 精品久久久久久人妻无码中文字幕 | 18无码粉嫩小泬无套在线观看 | 国产激情精品一区二区三区 | 在线成人www免费观看视频 | 国产亚洲tv在线观看 | 国内精品久久毛片一区二区 | 日本一区二区三区免费高清 | 四虎永久在线精品免费网址 | 男人扒开女人内裤强吻桶进去 | 久久精品女人的天堂av | 国内综合精品午夜久久资源 | 青青久在线视频免费观看 | 国产一精品一av一免费 | 国产亚av手机在线观看 | 人妻人人添人妻人人爱 | 在线精品亚洲一区二区 | 嫩b人妻精品一区二区三区 | 亚洲阿v天堂在线 | 亚洲精品一区国产 | 国产亚洲精品久久久久久久久动漫 | 国产av一区二区三区最新精品 | 久久人人爽人人爽人人片av高清 | 亚洲国产精品久久久天堂 | 亚洲国产精华液网站w | 精品人妻av区 | 久久婷婷五月综合色国产香蕉 | 日日碰狠狠躁久久躁蜜桃 | 国产精品久久久久无码av色戒 | 国产高潮视频在线观看 | 午夜无码人妻av大片色欲 | 亚洲第一无码av无码专区 | 一本久道高清无码视频 | а√资源新版在线天堂 | 国产亚洲精品久久久久久大师 | 大屁股大乳丰满人妻 | 国产成人精品无码播放 | 日本精品少妇一区二区三区 | 国产97色在线 | 免 | 久久亚洲中文字幕精品一区 | 中文字幕无码乱人伦 | 99久久精品无码一区二区毛片 | 黑人大群体交免费视频 | 无码播放一区二区三区 | 亚洲色大成网站www国产 | aⅴ亚洲 日韩 色 图网站 播放 | 波多野结衣av在线观看 | 免费无码午夜福利片69 | av在线亚洲欧洲日产一区二区 | 最新国产乱人伦偷精品免费网站 | 亚洲国产一区二区三区在线观看 | 国产舌乚八伦偷品w中 | 欧美猛少妇色xxxxx | 西西人体www44rt大胆高清 | 好爽又高潮了毛片免费下载 | 牲欲强的熟妇农村老妇女视频 | 国产色xx群视频射精 | 波多野结衣乳巨码无在线观看 | 乱中年女人伦av三区 | 性色欲网站人妻丰满中文久久不卡 | 无码国产乱人伦偷精品视频 | 成人免费视频一区二区 | 四虎国产精品一区二区 | 亚洲精品国偷拍自产在线麻豆 | 一本加勒比波多野结衣 | 纯爱无遮挡h肉动漫在线播放 | 国产猛烈高潮尖叫视频免费 | 欧美老熟妇乱xxxxx | 亚洲欧美色中文字幕在线 | 中文字幕乱码亚洲无线三区 | 亚洲国产成人av在线观看 | 久久久精品欧美一区二区免费 | 久久精品女人天堂av免费观看 | 婷婷色婷婷开心五月四房播播 | 国产特级毛片aaaaaa高潮流水 | 国产精品久久久久久亚洲毛片 | 婷婷六月久久综合丁香 | 亚洲の无码国产の无码影院 | 亚洲成在人网站无码天堂 | 亚洲爆乳精品无码一区二区三区 | 人妻无码久久精品人妻 | 国产成人无码一二三区视频 | 欧美国产日韩久久mv | 国产色精品久久人妻 | 性色欲网站人妻丰满中文久久不卡 | 国产精品久久国产精品99 | 午夜不卡av免费 一本久久a久久精品vr综合 | 国产精品久久久久影院嫩草 | 国产成人一区二区三区在线观看 | 国产99久久精品一区二区 | 欧美国产亚洲日韩在线二区 | 日韩欧美群交p片內射中文 | 国产亚洲日韩欧美另类第八页 | 久久国产精品_国产精品 | 欧美国产亚洲日韩在线二区 | 蜜臀aⅴ国产精品久久久国产老师 | 国产九九九九九九九a片 | 网友自拍区视频精品 | 无码国内精品人妻少妇 | 日本www一道久久久免费榴莲 | 国产激情一区二区三区 | 波多野42部无码喷潮在线 | 欧美国产日韩亚洲中文 | 国产成人亚洲综合无码 | 在线欧美精品一区二区三区 | 乱人伦中文视频在线观看 | 永久免费观看国产裸体美女 | 久久99精品久久久久久 | yw尤物av无码国产在线观看 | 日韩精品无码免费一区二区三区 | 国产三级久久久精品麻豆三级 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 久久人人爽人人人人片 | 在线观看欧美一区二区三区 | 国产精品亚洲综合色区韩国 | 亚洲精品中文字幕乱码 | 亚洲欧洲无卡二区视頻 | 国产熟女一区二区三区四区五区 | 亚洲精品欧美二区三区中文字幕 | 国产农村妇女高潮大叫 | 色婷婷久久一区二区三区麻豆 | 国产热a欧美热a在线视频 | 老熟女重囗味hdxx69 | 国产在热线精品视频 | 四虎国产精品免费久久 | 国产精品99爱免费视频 | 久久亚洲日韩精品一区二区三区 | 亚洲爆乳精品无码一区二区三区 | 纯爱无遮挡h肉动漫在线播放 | 久久99久久99精品中文字幕 | 日本爽爽爽爽爽爽在线观看免 | 国产成人无码av片在线观看不卡 | 99riav国产精品视频 | 欧美人妻一区二区三区 | 久久久久99精品国产片 | 亚洲 激情 小说 另类 欧美 | 女人被爽到呻吟gif动态图视看 | 性做久久久久久久免费看 | 亚洲成在人网站无码天堂 | 免费无码av一区二区 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 久久综合香蕉国产蜜臀av | 高清国产亚洲精品自在久久 | 日本护士xxxxhd少妇 | 高中生自慰www网站 | 精品无码成人片一区二区98 | 人人超人人超碰超国产 | 国产真实夫妇视频 | 亚洲精品国产精品乱码视色 | 日本在线高清不卡免费播放 | 成人毛片一区二区 | аⅴ资源天堂资源库在线 | 中文无码精品a∨在线观看不卡 | 久久久久国色av免费观看性色 | 麻豆蜜桃av蜜臀av色欲av | 啦啦啦www在线观看免费视频 | 国精产品一区二区三区 | 2020久久香蕉国产线看观看 | 国产人妻精品一区二区三区不卡 | 欧美成人午夜精品久久久 | 偷窥日本少妇撒尿chinese | 久久精品女人的天堂av | 狠狠躁日日躁夜夜躁2020 | a在线观看免费网站大全 | 亚洲精品国产第一综合99久久 | 给我免费的视频在线观看 | 国产无套内射久久久国产 | 国产九九九九九九九a片 | 亚洲va中文字幕无码久久不卡 | 久久99国产综合精品 | 成人精品视频一区二区三区尤物 | 国产va免费精品观看 | 国产人妻精品午夜福利免费 | 免费观看的无遮挡av | 天堂无码人妻精品一区二区三区 | 无遮挡国产高潮视频免费观看 | 成人性做爰aaa片免费看 | 国产高潮视频在线观看 | 欧美丰满熟妇xxxx | 亚洲精品国产品国语在线观看 | 欧美黑人巨大xxxxx | 女人和拘做爰正片视频 | 成人免费视频一区二区 | 丰满人妻一区二区三区免费视频 | 麻豆人妻少妇精品无码专区 | 久久99精品国产麻豆蜜芽 | 初尝人妻少妇中文字幕 | 国产午夜福利100集发布 | 中国大陆精品视频xxxx | 国产香蕉97碰碰久久人人 | 亚洲精品一区二区三区在线观看 | 88国产精品欧美一区二区三区 | 99久久99久久免费精品蜜桃 | 激情五月综合色婷婷一区二区 | 久久伊人色av天堂九九小黄鸭 | 久久综合狠狠综合久久综合88 | www国产亚洲精品久久网站 | 精品久久久久久人妻无码中文字幕 | 亚欧洲精品在线视频免费观看 | 国产精品免费大片 | 国产无遮挡又黄又爽免费视频 | 色情久久久av熟女人妻网站 | 久久久久成人精品免费播放动漫 | 国产真实伦对白全集 | 久久国产自偷自偷免费一区调 | 精品国产一区二区三区四区 | 亚洲日韩乱码中文无码蜜桃臀网站 | 国产精品无套呻吟在线 | 性欧美牲交xxxxx视频 | 欧美黑人巨大xxxxx | 精品国偷自产在线 | 任你躁在线精品免费 | 中文字幕无码免费久久99 | 男女爱爱好爽视频免费看 | 97久久超碰中文字幕 | 久久久精品成人免费观看 | 国产精品亚洲综合色区韩国 | 精品久久久久香蕉网 | 激情五月综合色婷婷一区二区 | 色妞www精品免费视频 | 国产亚洲精品久久久久久久久动漫 | 人人妻人人澡人人爽精品欧美 | 日韩人妻系列无码专区 | 午夜性刺激在线视频免费 | 国产精品人妻一区二区三区四 | 对白脏话肉麻粗话av | 久久国语露脸国产精品电影 | 88国产精品欧美一区二区三区 | 中文字幕久久久久人妻 | 中文毛片无遮挡高清免费 | 久久久久久亚洲精品a片成人 | 永久免费观看美女裸体的网站 | 国产av无码专区亚洲a∨毛片 | 无码一区二区三区在线 | 亚洲精品一区三区三区在线观看 | 2020最新国产自产精品 | 日韩精品无码一区二区中文字幕 | 国产亚洲欧美日韩亚洲中文色 | 老子影院午夜精品无码 | 国产成人综合美国十次 | 97夜夜澡人人爽人人喊中国片 | 国产亚洲人成在线播放 | 九九综合va免费看 | 亚洲性无码av中文字幕 | 午夜福利电影 | 夫妻免费无码v看片 | 领导边摸边吃奶边做爽在线观看 | 国产av一区二区精品久久凹凸 | 人妻无码久久精品人妻 | 国产人妖乱国产精品人妖 | 成人片黄网站色大片免费观看 | 国内揄拍国内精品少妇国语 | 天天综合网天天综合色 | 久久久久久久女国产乱让韩 | 久久亚洲精品成人无码 | 国产成人一区二区三区别 | 久久无码中文字幕免费影院蜜桃 | 少妇的肉体aa片免费 | 无码免费一区二区三区 | 亚洲午夜久久久影院 | 亚洲人成人无码网www国产 | 熟妇人妻中文av无码 | 中文字幕乱码亚洲无线三区 | 亚洲国产午夜精品理论片 | 黑人巨大精品欧美黑寡妇 | 最近免费中文字幕中文高清百度 | 日日噜噜噜噜夜夜爽亚洲精品 | 国产av一区二区三区最新精品 | 国产9 9在线 | 中文 | 欧美日韩视频无码一区二区三 | 精品人人妻人人澡人人爽人人 | 高清无码午夜福利视频 | 一区二区三区高清视频一 | 国产猛烈高潮尖叫视频免费 | 久精品国产欧美亚洲色aⅴ大片 | 久久久久成人精品免费播放动漫 | 一个人看的视频www在线 | 国产精品无码一区二区桃花视频 | 中文字幕久久久久人妻 | 无码精品国产va在线观看dvd | 未满小14洗澡无码视频网站 | aⅴ在线视频男人的天堂 | 日韩欧美群交p片內射中文 | 东北女人啪啪对白 | 亚洲欧美日韩国产精品一区二区 | 99精品久久毛片a片 | 装睡被陌生人摸出水好爽 | 亚洲色成人中文字幕网站 | 一区二区三区乱码在线 | 欧洲 | 亚洲乱码中文字幕在线 | 亚洲欧美精品伊人久久 | 国产亚洲精品久久久久久久 | 久久精品中文闷骚内射 | 久久综合久久自在自线精品自 | 国内少妇偷人精品视频免费 | 亚洲一区二区三区香蕉 | 东京无码熟妇人妻av在线网址 | 18禁黄网站男男禁片免费观看 | 久久99精品国产麻豆蜜芽 | 麻豆国产97在线 | 欧洲 | 高潮毛片无遮挡高清免费 | 久久亚洲精品中文字幕无男同 | 日本一区二区更新不卡 | 日韩视频 中文字幕 视频一区 | 欧美性猛交内射兽交老熟妇 | 久久综合九色综合欧美狠狠 | 日韩精品一区二区av在线 | 成人性做爰aaa片免费看 | 欧美自拍另类欧美综合图片区 | 熟女俱乐部五十路六十路av | 中文字幕亚洲情99在线 | 成人无码视频在线观看网站 | 强奷人妻日本中文字幕 | 国产精品资源一区二区 | 人人妻人人澡人人爽欧美一区 | 久久久久免费看成人影片 | 国产成人精品视频ⅴa片软件竹菊 | 久久久久免费精品国产 | 欧美日本日韩 | 无码播放一区二区三区 | 中文字幕日产无线码一区 | 无码人妻精品一区二区三区下载 | 国产精品人人妻人人爽 | 熟妇人妻中文av无码 | 国产无遮挡又黄又爽免费视频 | 久久精品人人做人人综合试看 | 国产精品二区一区二区aⅴ污介绍 | 亚洲码国产精品高潮在线 | 国产手机在线αⅴ片无码观看 | 国产性生交xxxxx无码 | 久久亚洲国产成人精品性色 | 日日麻批免费40分钟无码 | 小泽玛莉亚一区二区视频在线 | 伊人久久大香线蕉亚洲 | 日韩无套无码精品 | 强奷人妻日本中文字幕 | 熟女俱乐部五十路六十路av | 人妻少妇精品无码专区二区 | 最新国产麻豆aⅴ精品无码 | 波多野结衣av一区二区全免费观看 | www国产精品内射老师 | 欧洲极品少妇 | 精品国产青草久久久久福利 | 2020久久香蕉国产线看观看 | 亚洲熟妇色xxxxx欧美老妇y | 久在线观看福利视频 | 色婷婷久久一区二区三区麻豆 | 精品国产一区二区三区四区在线看 | 久久精品成人欧美大片 | 波多野结衣高清一区二区三区 | 未满小14洗澡无码视频网站 | aⅴ亚洲 日韩 色 图网站 播放 | 亚洲精品欧美二区三区中文字幕 | 爆乳一区二区三区无码 | 四十如虎的丰满熟妇啪啪 | 成人一在线视频日韩国产 | 俺去俺来也www色官网 | 丰满人妻一区二区三区免费视频 | 久久亚洲日韩精品一区二区三区 | 成人亚洲精品久久久久软件 | 欧美自拍另类欧美综合图片区 | 女人被男人躁得好爽免费视频 | 日本大香伊一区二区三区 | 久久天天躁夜夜躁狠狠 | 天堂а√在线地址中文在线 | 免费人成在线观看网站 | 任你躁国产自任一区二区三区 | 娇妻被黑人粗大高潮白浆 | 我要看www免费看插插视频 | 日本高清一区免费中文视频 | 成年女人永久免费看片 | 亚洲精品一区三区三区在线观看 | 久久亚洲a片com人成 | 国产69精品久久久久app下载 | 漂亮人妻洗澡被公强 日日躁 | 精品国产成人一区二区三区 | 风流少妇按摩来高潮 | 久久国内精品自在自线 | 婷婷色婷婷开心五月四房播播 | 熟妇人妻无乱码中文字幕 | 亚洲男人av天堂午夜在 | 97精品人妻一区二区三区香蕉 | 亚洲爆乳精品无码一区二区三区 | 未满小14洗澡无码视频网站 | 日韩亚洲欧美精品综合 | 伊人久久大香线焦av综合影院 | 少妇的肉体aa片免费 | 国产激情综合五月久久 | 色综合视频一区二区三区 | 国产成人综合美国十次 | 中文字幕乱码人妻无码久久 | 超碰97人人做人人爱少妇 | 久久久久亚洲精品中文字幕 | 性欧美熟妇videofreesex | 未满小14洗澡无码视频网站 | 亚洲中文字幕在线观看 | 天天拍夜夜添久久精品大 | 东京热无码av男人的天堂 | 欧洲极品少妇 | 超碰97人人做人人爱少妇 | 久久精品99久久香蕉国产色戒 | 国产绳艺sm调教室论坛 | 无码人妻丰满熟妇区毛片18 | 日本精品久久久久中文字幕 | 国产人妻精品一区二区三区 | 日本xxxx色视频在线观看免费 | 精品亚洲成av人在线观看 | 人妻无码αv中文字幕久久琪琪布 | 国产在线无码精品电影网 | 国产黑色丝袜在线播放 | 亚洲性无码av中文字幕 | 日韩精品无码一区二区中文字幕 | 婷婷六月久久综合丁香 | 国产成人人人97超碰超爽8 | 久久国产精品萌白酱免费 | 人人澡人人透人人爽 | 一本久久a久久精品亚洲 | 中文毛片无遮挡高清免费 | 国产97在线 | 亚洲 | 人人妻人人澡人人爽欧美一区 | 国产精品久久久久影院嫩草 | 性啪啪chinese东北女人 | 老头边吃奶边弄进去呻吟 | 亚洲国产精品一区二区美利坚 | 樱花草在线播放免费中文 | 亚洲区欧美区综合区自拍区 | 男人和女人高潮免费网站 | 免费人成网站视频在线观看 | 一本久道久久综合婷婷五月 | 少妇无码一区二区二三区 | 日本精品少妇一区二区三区 | av人摸人人人澡人人超碰下载 | 婷婷综合久久中文字幕蜜桃三电影 | 久久精品人人做人人综合 | 高清国产亚洲精品自在久久 | 国产一精品一av一免费 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 亚洲毛片av日韩av无码 | 日韩av无码中文无码电影 | 77777熟女视频在线观看 а天堂中文在线官网 | 人人爽人人澡人人高潮 | 7777奇米四色成人眼影 | 激情人妻另类人妻伦 | 性色欲网站人妻丰满中文久久不卡 | 亚洲人成影院在线无码按摩店 | 亚洲精品一区三区三区在线观看 | 麻豆蜜桃av蜜臀av色欲av | 中国女人内谢69xxxx | 日本肉体xxxx裸交 | 国产色视频一区二区三区 | 中文字幕无码免费久久9一区9 | а√天堂www在线天堂小说 | 亚洲一区二区三区无码久久 | 少妇的肉体aa片免费 | 国产亚洲精品久久久久久久久动漫 | 爽爽影院免费观看 | 乱人伦人妻中文字幕无码 | 久久人妻内射无码一区三区 | 亚洲成色在线综合网站 | 理论片87福利理论电影 | 国产亚洲精品久久久久久国模美 | 天天摸天天透天天添 | 亚洲日本va中文字幕 | 国产两女互慰高潮视频在线观看 | 久久精品人人做人人综合试看 | 亚洲色成人中文字幕网站 | 成人精品一区二区三区中文字幕 | 日本丰满护士爆乳xxxx | 一本色道婷婷久久欧美 | 天天摸天天碰天天添 | 久久久www成人免费毛片 | 欧美熟妇另类久久久久久多毛 | 伊人久久大香线蕉av一区二区 | 狠狠噜狠狠狠狠丁香五月 | 亚洲国产精品毛片av不卡在线 | 青春草在线视频免费观看 | 国产精品国产三级国产专播 | 亚洲自偷自拍另类第1页 | 精品成在人线av无码免费看 | 高潮毛片无遮挡高清免费 | av香港经典三级级 在线 | 九一九色国产 | 国产肉丝袜在线观看 | 婷婷丁香五月天综合东京热 | av无码电影一区二区三区 | 国产精品-区区久久久狼 | 国产精品沙发午睡系列 | 老子影院午夜伦不卡 | 无码人妻精品一区二区三区下载 | 最近免费中文字幕中文高清百度 | 国产精品99久久精品爆乳 | 无码人妻精品一区二区三区下载 | 精品国产一区二区三区四区 | 国产成人无码区免费内射一片色欲 | 亚洲男人av天堂午夜在 | 国产午夜福利亚洲第一 | 国产网红无码精品视频 | 99精品视频在线观看免费 | 欧美日本免费一区二区三区 | 东京热一精品无码av | 国产人妻人伦精品 | 一本精品99久久精品77 | 老司机亚洲精品影院无码 | 丰满妇女强制高潮18xxxx | 日韩视频 中文字幕 视频一区 | 国产又粗又硬又大爽黄老大爷视 | 成 人 免费观看网站 | 美女扒开屁股让男人桶 | 牲交欧美兽交欧美 | 成人欧美一区二区三区黑人 | 久久久久久九九精品久 | 欧美野外疯狂做受xxxx高潮 | 一本一道久久综合久久 | 东京无码熟妇人妻av在线网址 | 成人免费视频在线观看 | 亚洲а∨天堂久久精品2021 | 欧美日韩亚洲国产精品 | 无码av最新清无码专区吞精 | 色婷婷久久一区二区三区麻豆 | 欧美亚洲国产一区二区三区 | 无码吃奶揉捏奶头高潮视频 | 亚洲午夜久久久影院 | 亚洲国产高清在线观看视频 | 无码国产激情在线观看 | 中文字幕无码日韩专区 | 中文字幕无码热在线视频 | 2020久久超碰国产精品最新 | 亚洲国产精品无码久久久久高潮 | 国内精品久久久久久中文字幕 | 欧美丰满老熟妇xxxxx性 | 日韩欧美成人免费观看 | 国产精品无码mv在线观看 | 中文字幕乱码中文乱码51精品 | 婷婷综合久久中文字幕蜜桃三电影 | 国产麻豆精品精东影业av网站 | 中文久久乱码一区二区 | 久久熟妇人妻午夜寂寞影院 | 久久综合给久久狠狠97色 | 亚洲va中文字幕无码久久不卡 | 国产一区二区三区四区五区加勒比 | 日本一区二区三区免费高清 | 亚洲の无码国产の无码影院 | 久久午夜无码鲁丝片秋霞 | 麻豆国产丝袜白领秘书在线观看 | 亚洲无人区午夜福利码高清完整版 | 亚洲欧美国产精品专区久久 | 亚洲中文字幕在线观看 | 国产成人精品一区二区在线小狼 | 未满成年国产在线观看 | 国精产品一品二品国精品69xx | 国产手机在线αⅴ片无码观看 | 精品国偷自产在线视频 | 精品国产精品久久一区免费式 | 国产偷抇久久精品a片69 | 亚洲啪av永久无码精品放毛片 | 捆绑白丝粉色jk震动捧喷白浆 | 男女猛烈xx00免费视频试看 | 性生交片免费无码看人 | 2019nv天堂香蕉在线观看 | 在线欧美精品一区二区三区 | 国产午夜亚洲精品不卡下载 | 国产网红无码精品视频 | 少妇久久久久久人妻无码 | 两性色午夜免费视频 | 国产成人午夜福利在线播放 | а天堂中文在线官网 | 亚洲精品一区二区三区大桥未久 | 亚洲一区二区三区含羞草 | 国产suv精品一区二区五 | 午夜精品久久久久久久久 | 久久久久人妻一区精品色欧美 | 女人高潮内射99精品 | 天下第一社区视频www日本 | 亚洲娇小与黑人巨大交 | 久久精品国产日本波多野结衣 | 无码免费一区二区三区 | 一二三四社区在线中文视频 | 99在线 | 亚洲 | 国产xxx69麻豆国语对白 | 色婷婷香蕉在线一区二区 | 国产午夜视频在线观看 | 免费中文字幕日韩欧美 | 亚洲成av人片天堂网无码】 | 国产成人无码区免费内射一片色欲 | 妺妺窝人体色www在线小说 | a国产一区二区免费入口 | 乱码午夜-极国产极内射 | 国产偷自视频区视频 | 亚洲精品久久久久中文第一幕 | 中文久久乱码一区二区 | 玩弄中年熟妇正在播放 | 日韩欧美成人免费观看 | 精品无码国产自产拍在线观看蜜 | 国产精品自产拍在线观看 | 天天摸天天透天天添 | 日日碰狠狠丁香久燥 | 日韩 欧美 动漫 国产 制服 | 九九在线中文字幕无码 | 国产免费无码一区二区视频 | 青青青爽视频在线观看 | 小泽玛莉亚一区二区视频在线 | 亚洲日本va中文字幕 | 久久人妻内射无码一区三区 | 亚洲国产成人av在线观看 | 成人精品天堂一区二区三区 | 成在人线av无码免费 | 国产香蕉尹人综合在线观看 | 欧美熟妇另类久久久久久不卡 | 亚洲区欧美区综合区自拍区 | 国产特级毛片aaaaaa高潮流水 | 国产精品久免费的黄网站 | 美女张开腿让人桶 | 精品久久8x国产免费观看 | 国产片av国语在线观看 | 人妻互换免费中文字幕 | 久久久久av无码免费网 | 无码国内精品人妻少妇 | 免费观看激色视频网站 | 久久久久av无码免费网 | 精品人妻中文字幕有码在线 | 日本va欧美va欧美va精品 | 黑人粗大猛烈进出高潮视频 | 欧美熟妇另类久久久久久多毛 | 国内少妇偷人精品视频免费 | 欧美 亚洲 国产 另类 | 久久99精品国产麻豆 | 国产精品怡红院永久免费 | 久久亚洲中文字幕精品一区 | 国产成人综合色在线观看网站 | 天天做天天爱天天爽综合网 | 无码一区二区三区在线观看 | 午夜精品久久久久久久 | 精品成在人线av无码免费看 | 狠狠cao日日穞夜夜穞av | 久久zyz资源站无码中文动漫 | √8天堂资源地址中文在线 | 国产亚洲tv在线观看 | 国产精品亚洲专区无码不卡 | 人人爽人人澡人人人妻 | 无码精品国产va在线观看dvd | 国产热a欧美热a在线视频 | 无码人妻少妇伦在线电影 | 蜜桃av抽搐高潮一区二区 | 国产成人无码午夜视频在线观看 | 天天燥日日燥 | 丰满少妇熟乱xxxxx视频 | 中文字幕亚洲情99在线 | 人人爽人人澡人人高潮 | 亚洲精品一区二区三区在线观看 | 无码av最新清无码专区吞精 | 久久久久久久久888 | 丰满少妇女裸体bbw | 国产欧美精品一区二区三区 | 国产精品久久福利网站 | 久久99精品国产麻豆蜜芽 | 国产精品无码永久免费888 | 婷婷五月综合缴情在线视频 | 人人妻人人澡人人爽欧美一区 | 美女张开腿让人桶 | 无套内谢的新婚少妇国语播放 | 天海翼激烈高潮到腰振不止 | 曰韩无码二三区中文字幕 | 一个人免费观看的www视频 | 国产亚洲精品久久久久久大师 | 免费无码av一区二区 | 99久久婷婷国产综合精品青草免费 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲色www成人永久网址 | 精品国产成人一区二区三区 | 性史性农村dvd毛片 | 人妻夜夜爽天天爽三区 | 久青草影院在线观看国产 | 精品国产一区二区三区四区 | 久久精品成人欧美大片 | 国产av一区二区精品久久凹凸 | 色综合久久88色综合天天 | 中文毛片无遮挡高清免费 | 麻豆精产国品 | 国产乱人伦av在线无码 | 久久久久久亚洲精品a片成人 | 欧美日韩亚洲国产精品 | 精品无码av一区二区三区 | 国产精品亚洲а∨无码播放麻豆 | 亚洲日韩一区二区三区 | 精品国产一区二区三区四区在线看 | 亚洲日韩精品欧美一区二区 | 欧美喷潮久久久xxxxx | 日韩人妻无码中文字幕视频 | 亚洲精品成人av在线 | 国产偷抇久久精品a片69 | 国产一精品一av一免费 | 性色欲网站人妻丰满中文久久不卡 | 国产三级久久久精品麻豆三级 | 国产农村乱对白刺激视频 | 内射巨臀欧美在线视频 | 熟女俱乐部五十路六十路av | 成人免费视频视频在线观看 免费 | 国产精品成人av在线观看 | 午夜福利试看120秒体验区 | 中国女人内谢69xxxx | 日本大香伊一区二区三区 | 国产真人无遮挡作爱免费视频 | 影音先锋中文字幕无码 | 中文字幕无码视频专区 | 露脸叫床粗话东北少妇 | 无码吃奶揉捏奶头高潮视频 | 无人区乱码一区二区三区 | 色婷婷久久一区二区三区麻豆 | 欧美放荡的少妇 | 免费网站看v片在线18禁无码 | 国产精品久久久久9999小说 | 国精品人妻无码一区二区三区蜜柚 | 亚洲人亚洲人成电影网站色 | 久久熟妇人妻午夜寂寞影院 | 色婷婷香蕉在线一区二区 | 久久久无码中文字幕久... | 中文无码伦av中文字幕 | 久9re热视频这里只有精品 | аⅴ资源天堂资源库在线 | 午夜成人1000部免费视频 | 国产香蕉97碰碰久久人人 | 小泽玛莉亚一区二区视频在线 | 奇米影视7777久久精品人人爽 | 婷婷五月综合激情中文字幕 | 无码国模国产在线观看 | 国产熟妇另类久久久久 | 97久久精品无码一区二区 | 少妇人妻大乳在线视频 | 中文精品无码中文字幕无码专区 | 波多野结衣 黑人 | 久久zyz资源站无码中文动漫 | 欧美自拍另类欧美综合图片区 | 奇米影视7777久久精品 | 丁香花在线影院观看在线播放 | 亚洲精品一区三区三区在线观看 | 97人妻精品一区二区三区 | 亚洲の无码国产の无码步美 | 欧美激情一区二区三区成人 | 99久久人妻精品免费一区 | 国产乱人偷精品人妻a片 | 色 综合 欧美 亚洲 国产 | 一个人看的www免费视频在线观看 | 中文字幕无码免费久久9一区9 | 国产亚洲日韩欧美另类第八页 | 亚洲理论电影在线观看 | 欧美日韩视频无码一区二区三 | 狠狠cao日日穞夜夜穞av | 日韩精品无码一区二区中文字幕 | 又大又紧又粉嫩18p少妇 | 妺妺窝人体色www婷婷 | 亚洲国产精品久久久久久 | 无码人妻丰满熟妇区五十路百度 | 1000部啪啪未满十八勿入下载 | 国产成人无码a区在线观看视频app | 在线播放免费人成毛片乱码 | 性做久久久久久久免费看 | 国产精品久久久久久亚洲毛片 | 荫蒂添的好舒服视频囗交 | 欧美性生交xxxxx久久久 | 午夜性刺激在线视频免费 | 色五月五月丁香亚洲综合网 | 巨爆乳无码视频在线观看 | 漂亮人妻洗澡被公强 日日躁 | 18禁止看的免费污网站 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 国产午夜手机精彩视频 | 激情爆乳一区二区三区 | 亚洲中文字幕无码一久久区 | 国产亚洲精品久久久久久大师 | 久久久久99精品成人片 | 成人av无码一区二区三区 | 亚洲一区二区三区含羞草 | 欧美怡红院免费全部视频 | 欧美一区二区三区视频在线观看 | 日韩欧美中文字幕公布 | 中文字幕久久久久人妻 | 人妻体内射精一区二区三四 | 亚洲精品无码人妻无码 | 国产av一区二区三区最新精品 | 成人毛片一区二区 | 麻豆av传媒蜜桃天美传媒 | ass日本丰满熟妇pics | 最近免费中文字幕中文高清百度 | 麻豆精产国品 | 人人妻人人澡人人爽人人精品浪潮 | 精品国产乱码久久久久乱码 | 欧美老人巨大xxxx做受 | 国産精品久久久久久久 | 欧美国产亚洲日韩在线二区 | 精品无码成人片一区二区98 | 久久久成人毛片无码 | 久久精品女人的天堂av | 日本一区二区更新不卡 | 日韩精品久久久肉伦网站 | 欧美丰满熟妇xxxx | 国产色精品久久人妻 | 色欲人妻aaaaaaa无码 | 丰满人妻精品国产99aⅴ | 青青青爽视频在线观看 | 中文字幕色婷婷在线视频 | 亚洲日本va午夜在线电影 | 玩弄中年熟妇正在播放 | 亚洲中文字幕乱码av波多ji | 国产综合久久久久鬼色 | 在线精品亚洲一区二区 | 国产成人无码专区 | 久久久久久久女国产乱让韩 | 亚洲色无码一区二区三区 | 免费无码午夜福利片69 | 午夜福利电影 | 粉嫩少妇内射浓精videos | 国产av一区二区三区最新精品 | 99riav国产精品视频 | 少妇被粗大的猛进出69影院 | 熟女少妇在线视频播放 | 老司机亚洲精品影院无码 | 国产极品美女高潮无套在线观看 | 少妇高潮一区二区三区99 | 天堂亚洲2017在线观看 | 女人被爽到呻吟gif动态图视看 | 人妻夜夜爽天天爽三区 | 丰满肥臀大屁股熟妇激情视频 | 超碰97人人做人人爱少妇 | 中文无码精品a∨在线观看不卡 | 乱中年女人伦av三区 | 国产成人无码av在线影院 | 亚洲狠狠色丁香婷婷综合 | 麻豆国产丝袜白领秘书在线观看 | 中文字幕人妻无码一区二区三区 | 国产热a欧美热a在线视频 | 99精品无人区乱码1区2区3区 | 黑人巨大精品欧美黑寡妇 | 亚洲色欲久久久综合网东京热 | 日韩欧美中文字幕在线三区 | 少妇厨房愉情理9仑片视频 | 色婷婷久久一区二区三区麻豆 | 亚洲乱码国产乱码精品精 | 欧美丰满熟妇xxxx | 99久久久无码国产精品免费 | 99久久精品无码一区二区毛片 | 激情爆乳一区二区三区 | 日本大乳高潮视频在线观看 | 国产成人午夜福利在线播放 | 中文字幕中文有码在线 | 欧美老人巨大xxxx做受 | 露脸叫床粗话东北少妇 | 亚洲日本一区二区三区在线 | 荫蒂添的好舒服视频囗交 | 国精产品一区二区三区 | 亚洲日韩av一区二区三区四区 | 久激情内射婷内射蜜桃人妖 | 风流少妇按摩来高潮 | 午夜精品一区二区三区的区别 | 精品人妻av区 | 婷婷六月久久综合丁香 | 亚洲人成影院在线观看 | 我要看www免费看插插视频 | 熟女少妇在线视频播放 | 亚洲欧洲中文日韩av乱码 | 久久亚洲精品成人无码 | 麻豆人妻少妇精品无码专区 | 麻豆国产丝袜白领秘书在线观看 | 无人区乱码一区二区三区 | 久久久久亚洲精品男人的天堂 | 国产精品无码mv在线观看 | 日本护士毛茸茸高潮 | 东京热无码av男人的天堂 | 一本一道久久综合久久 | 国产av久久久久精东av | 国精品人妻无码一区二区三区蜜柚 | 久久久久久久人妻无码中文字幕爆 | 人妻少妇被猛烈进入中文字幕 | 天堂久久天堂av色综合 | 久久久久久久人妻无码中文字幕爆 | 少妇被黑人到高潮喷出白浆 | 最近免费中文字幕中文高清百度 | 在线亚洲高清揄拍自拍一品区 | 亚洲中文字幕在线观看 | 扒开双腿疯狂进出爽爽爽视频 | 亚洲国产成人av在线观看 | 岛国片人妻三上悠亚 | 国产综合在线观看 | 亚洲日韩一区二区 | 天堂无码人妻精品一区二区三区 | 欧美激情一区二区三区成人 | 亚洲va中文字幕无码久久不卡 | 99久久久无码国产精品免费 | 99久久亚洲精品无码毛片 | 国产午夜精品一区二区三区嫩草 | 99精品国产综合久久久久五月天 | 国产亚洲精品精品国产亚洲综合 | 乱人伦中文视频在线观看 | 久久天天躁夜夜躁狠狠 | 人妻天天爽夜夜爽一区二区 | 国产av剧情md精品麻豆 | 国产午夜福利亚洲第一 | 性生交大片免费看女人按摩摩 | 亚洲自偷自拍另类第1页 | 一本大道伊人av久久综合 | 国产精品久久国产精品99 | 成在人线av无码免观看麻豆 | 无码福利日韩神码福利片 | 亚洲va欧美va天堂v国产综合 | 精品无码成人片一区二区98 | 亚洲中文字幕乱码av波多ji | 水蜜桃av无码 | 少妇高潮喷潮久久久影院 | 内射后入在线观看一区 | 国产成人av免费观看 | 亚洲国产欧美日韩精品一区二区三区 | 国产精品久久久av久久久 | 国产无遮挡又黄又爽免费视频 | 成人一区二区免费视频 | 国产美女精品一区二区三区 | 99久久婷婷国产综合精品青草免费 | 欧美日韩久久久精品a片 | 亚洲の无码国产の无码影院 | 久久 国产 尿 小便 嘘嘘 | 九一九色国产 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 一个人看的www免费视频在线观看 | 天堂а√在线地址中文在线 | 荫蒂添的好舒服视频囗交 | 日日麻批免费40分钟无码 | 亚洲日韩精品欧美一区二区 | 国产精品无码一区二区三区不卡 | 性色欲情网站iwww九文堂 | 国产农村乱对白刺激视频 | 久久亚洲精品中文字幕无男同 | 无码人妻久久一区二区三区不卡 | 波多野结衣av在线观看 | a在线亚洲男人的天堂 | 国产sm调教视频在线观看 | 久久天天躁夜夜躁狠狠 | 性色欲网站人妻丰满中文久久不卡 | 无人区乱码一区二区三区 | 久久久久人妻一区精品色欧美 | 无码人妻丰满熟妇区毛片18 | 国产做国产爱免费视频 | 1000部啪啪未满十八勿入下载 | 中文字幕无码热在线视频 | 精品夜夜澡人妻无码av蜜桃 | 国产免费无码一区二区视频 | 国产 精品 自在自线 | 亚洲欧美色中文字幕在线 | 国产美女精品一区二区三区 | 色五月丁香五月综合五月 | 欧美野外疯狂做受xxxx高潮 | 久久99精品久久久久久 | 久久伊人色av天堂九九小黄鸭 | 日本一本二本三区免费 | 18精品久久久无码午夜福利 | 欧洲熟妇色 欧美 | 免费无码午夜福利片69 | 日本一卡二卡不卡视频查询 | 高清国产亚洲精品自在久久 | 日本精品人妻无码免费大全 | 人妻插b视频一区二区三区 | 99久久久国产精品无码免费 | 亚洲一区二区三区 | 一本色道久久综合狠狠躁 | 377p欧洲日本亚洲大胆 | 女人被爽到呻吟gif动态图视看 | 人人妻人人澡人人爽欧美一区 | 国产成人无码av一区二区 | 成人性做爰aaa片免费看不忠 | 国产精品国产自线拍免费软件 | 思思久久99热只有频精品66 | 久久精品国产精品国产精品污 | 性欧美疯狂xxxxbbbb | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 日本精品久久久久中文字幕 | 一本大道久久东京热无码av | 牲欲强的熟妇农村老妇女视频 | 奇米影视7777久久精品 | 国产精品毛片一区二区 | 成人亚洲精品久久久久 | 人人澡人人妻人人爽人人蜜桃 | 国产精品沙发午睡系列 | 日本精品人妻无码免费大全 | 波多野结衣高清一区二区三区 | 成人影院yy111111在线观看 | av无码电影一区二区三区 | 一区二区三区高清视频一 | 亚洲の无码国产の无码影院 | 亚洲aⅴ无码成人网站国产app | 欧美亚洲国产一区二区三区 | 亚洲精品综合一区二区三区在线 | 又大又硬又爽免费视频 | 亚洲综合另类小说色区 | 日本成熟视频免费视频 | 亚洲日韩乱码中文无码蜜桃臀网站 | 国精产品一区二区三区 | 精品一区二区三区无码免费视频 | 国产乡下妇女做爰 | 国産精品久久久久久久 | 宝宝好涨水快流出来免费视频 | 国产在线精品一区二区高清不卡 | 狂野欧美性猛交免费视频 | 欧美日韩一区二区免费视频 | 99久久99久久免费精品蜜桃 | 3d动漫精品啪啪一区二区中 | 人妻尝试又大又粗久久 | 国产熟妇高潮叫床视频播放 | 午夜免费福利小电影 | 377p欧洲日本亚洲大胆 | 中文字幕无码日韩专区 | 欧美大屁股xxxxhd黑色 | 77777熟女视频在线观看 а天堂中文在线官网 | 亚洲天堂2017无码中文 | 亚洲 a v无 码免 费 成 人 a v | 伊人久久大香线焦av综合影院 | 久久午夜无码鲁丝片秋霞 | 色欲av亚洲一区无码少妇 | 狠狠躁日日躁夜夜躁2020 | 在线视频网站www色 | 国产av一区二区精品久久凹凸 | www国产亚洲精品久久网站 | 久久久中文字幕日本无吗 | 奇米影视7777久久精品 | 国产精品嫩草久久久久 | 无码福利日韩神码福利片 | 性色欲网站人妻丰满中文久久不卡 | 国产一区二区三区日韩精品 | 18禁黄网站男男禁片免费观看 | 国产精品自产拍在线观看 | 久久视频在线观看精品 | 又粗又大又硬又长又爽 | 波多野结衣 黑人 | 丰满岳乱妇在线观看中字无码 | 永久免费观看美女裸体的网站 | a国产一区二区免费入口 | 爽爽影院免费观看 | 日本熟妇乱子伦xxxx | ass日本丰满熟妇pics | 国产成人综合色在线观看网站 | 99久久精品午夜一区二区 | 国产精品久久福利网站 | 久久 国产 尿 小便 嘘嘘 | 无码帝国www无码专区色综合 | 欧美老妇交乱视频在线观看 | 激情爆乳一区二区三区 | 无码一区二区三区在线 | 久久成人a毛片免费观看网站 | 国产网红无码精品视频 | 强伦人妻一区二区三区视频18 | 成人亚洲精品久久久久软件 | 欧美人与动性行为视频 | 少女韩国电视剧在线观看完整 | 人人妻人人澡人人爽人人精品浪潮 | 日韩精品成人一区二区三区 | 亚洲欧美日韩综合久久久 | 十八禁真人啪啪免费网站 | 国产免费久久精品国产传媒 | 丰满人妻精品国产99aⅴ | 高潮毛片无遮挡高清免费 | 狠狠色丁香久久婷婷综合五月 | 日韩精品一区二区av在线 | 男人的天堂av网站 | 在线观看国产午夜福利片 | 久久精品女人的天堂av | 亚洲色大成网站www | 国产免费久久精品国产传媒 | 欧美真人作爱免费视频 | 国产sm调教视频在线观看 | 7777奇米四色成人眼影 | 日本丰满护士爆乳xxxx | 国产精品理论片在线观看 | 欧洲精品码一区二区三区免费看 | 亚洲精品国产精品乱码视色 | 精品国偷自产在线视频 | 亚洲精品国偷拍自产在线麻豆 | 老头边吃奶边弄进去呻吟 | 午夜丰满少妇性开放视频 | 无遮挡啪啪摇乳动态图 | 中文字幕无码免费久久99 | 亚洲大尺度无码无码专区 | 丰满人妻一区二区三区免费视频 | 少妇被粗大的猛进出69影院 | 中文字幕人妻无码一夲道 | 久久久久久九九精品久 | 婷婷六月久久综合丁香 | 无套内射视频囯产 | 亚洲 激情 小说 另类 欧美 | 久久精品国产精品国产精品污 | 日本饥渴人妻欲求不满 | 免费中文字幕日韩欧美 | 久久久久成人片免费观看蜜芽 | 亚洲精品一区三区三区在线观看 | 成人精品一区二区三区中文字幕 | 欧美国产日韩久久mv | 亚洲人成网站在线播放942 | 久久天天躁狠狠躁夜夜免费观看 | 在线欧美精品一区二区三区 | 天天做天天爱天天爽综合网 | 国产亚洲精品久久久久久 | 东京一本一道一二三区 | 野外少妇愉情中文字幕 | av人摸人人人澡人人超碰下载 | 国产亲子乱弄免费视频 | 欧美丰满熟妇xxxx | 久久 国产 尿 小便 嘘嘘 | 国产精品理论片在线观看 | 国产在线一区二区三区四区五区 | 国内精品九九久久久精品 | 99国产精品白浆在线观看免费 | 国产精品二区一区二区aⅴ污介绍 | 白嫩日本少妇做爰 | 欧美第一黄网免费网站 | 亚洲日本一区二区三区在线 | 性欧美牲交在线视频 | 成熟女人特级毛片www免费 | 天海翼激烈高潮到腰振不止 | 啦啦啦www在线观看免费视频 | 日韩人妻系列无码专区 | av无码不卡在线观看免费 | 狂野欧美性猛交免费视频 | 国产精品毛多多水多 | 精品国产福利一区二区 | 欧美 丝袜 自拍 制服 另类 | www国产亚洲精品久久网站 | 99视频精品全部免费免费观看 | 无码午夜成人1000部免费视频 | 在线精品亚洲一区二区 | 女人被爽到呻吟gif动态图视看 | 未满小14洗澡无码视频网站 | 国产九九九九九九九a片 | 欧美日韩视频无码一区二区三 | 日韩av无码中文无码电影 | 久久精品国产精品国产精品污 | 日本一区二区更新不卡 | 国产农村妇女高潮大叫 | 精品人人妻人人澡人人爽人人 | 老熟女乱子伦 | 东京热无码av男人的天堂 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲乱码日产精品bd | 天堂一区人妻无码 | 女人被男人躁得好爽免费视频 | 日日躁夜夜躁狠狠躁 | 中文字幕乱码人妻二区三区 | 国产国产精品人在线视 | 天堂а√在线中文在线 | 日韩精品a片一区二区三区妖精 | 老子影院午夜伦不卡 | 亚洲色欲久久久综合网东京热 | 国产精品a成v人在线播放 | 97精品人妻一区二区三区香蕉 | 成人亚洲精品久久久久 | 性色欲网站人妻丰满中文久久不卡 | 色综合久久久久综合一本到桃花网 | 无码精品国产va在线观看dvd | 亚洲 欧美 激情 小说 另类 | 中文字幕av日韩精品一区二区 | 国产精品人人妻人人爽 | 色 综合 欧美 亚洲 国产 | 亚洲成a人一区二区三区 | 人人澡人人妻人人爽人人蜜桃 | 丰满人妻被黑人猛烈进入 | 欧美精品国产综合久久 | 亚洲一区二区三区四区 | 国产麻豆精品一区二区三区v视界 | 东京热无码av男人的天堂 | 国产亚洲精品精品国产亚洲综合 | 偷窥日本少妇撒尿chinese | 99久久精品国产一区二区蜜芽 | 天天拍夜夜添久久精品 | 天天拍夜夜添久久精品 | 国产人妻久久精品二区三区老狼 | 天天摸天天碰天天添 | 精品夜夜澡人妻无码av蜜桃 | 日本熟妇大屁股人妻 | 波多野结衣 黑人 | 日本大乳高潮视频在线观看 | 成人动漫在线观看 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 天天燥日日燥 | 亚洲自偷自偷在线制服 | 初尝人妻少妇中文字幕 | 久久亚洲日韩精品一区二区三区 | 精品无码成人片一区二区98 | 色综合久久久无码中文字幕 | 黑人玩弄人妻中文在线 | 无码人妻久久一区二区三区不卡 | 欧美黑人性暴力猛交喷水 | 亚洲午夜福利在线观看 | 麻豆蜜桃av蜜臀av色欲av |