青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 17  文章 - 48  trackbacks - 0
<2013年9月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

常用鏈接

留言簿(3)

隨筆檔案

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

最近幾個(gè)月利用上下班的時(shí)間在學(xué)習(xí)Haskell,Haskell有不少讓人開闊思路的東西,也有不少看起來很美好,用起來不錯(cuò),但是讀起來費(fèi)勁的東西。Haskell的語法學(xué)的差不多了之后,用Haskell寫了一個(gè)簡單的C++代碼行統(tǒng)計(jì)工具,寫過幾個(gè)版本,留下了兩個(gè),一個(gè)是直接用模式匹配寫的,一個(gè)是山寨了一個(gè)極簡的parse combinator,然后用這個(gè)山寨的parse combinator寫了一個(gè)版本,代碼估計(jì)寫的都比較爛,以后進(jìn)階學(xué)習(xí)之后有時(shí)間再改。這個(gè)統(tǒng)計(jì)工具并不是完整的處理C++的語法,也沒對(duì)在字符串和宏定義里面的"http://" "/*" "*/"做處理,因此對(duì)某些C++程序統(tǒng)計(jì)代碼行,可能不完全正確,但是基本可以用。


data, type, newtype


Haskell里面用data來定義數(shù)據(jù)類型,它可以是這樣:
 

data Mode = ReadMode | WriteMode
data Some = Some Int String
data Thing = { a :: Int, b :: String }
data Func a b = { func :: a -> b }

 

第一行定義了一個(gè)Mode,包含ReadMode和WriteMode;

第二行定義了一個(gè)普通數(shù)據(jù)類型Some,包含一個(gè)Int數(shù)據(jù)和一個(gè)String數(shù)據(jù);

第三行定義了一個(gè)普通數(shù)據(jù)類型Thing,包含類型為Int的a和類型為String的b;

第四行定義了一個(gè)符合數(shù)據(jù)類型Func,里面有個(gè)函數(shù)類型為(a -> b)的數(shù)據(jù)func。


第一種相當(dāng)于C++中的enum class,第二種第三種相當(dāng)于普通的struct數(shù)據(jù),第二種和第三種的區(qū)別是第二種不能直接取到Int和String的數(shù)據(jù),第三種可以通過a,b取到數(shù)據(jù),第四種相當(dāng)于C++的template class(struct),第四種寫成這樣來定義具體的數(shù)據(jù)類型:

type IntStringFunc = data Func Int String

 

type在這里定義了一個(gè)別名IntStringFunc類型,包含了一個(gè)函數(shù)類型是Int -> String的func的數(shù)據(jù),這里的type相當(dāng)于C++ 11的using別名,因?yàn)樗€可以這樣寫:


type IntBFunc b = data Func Int b


在C++ 11中,using包含了typedef的功能,也支持了template class的類型typedef,如下:


template <typename T, typename P>
class SomeType;

template <typename T>
using SomeTypeInt = SomeType<T, int>;


newtype定義的數(shù)據(jù)類型跟type類型,不過type定義的純粹是別名,別名類型跟原始類型是一致的,而newtype則定義的是一個(gè)wrapper,是一種新的數(shù)據(jù)類型,所以是newtype。newtype定義的類型是編譯時(shí)期的wrapper,Haskell保證沒有運(yùn)行時(shí)期的開銷,newtype定義的跟data類似:

newtype NewType a b = NewType { func :: a -> b }


模式匹配


上面說道data定義的第二種數(shù)據(jù)類型,包含Int和String的數(shù)據(jù),但是不能直接取到這兩個(gè)數(shù)據(jù),所以我們需要定義兩個(gè)函數(shù)來取其中的數(shù)據(jù):


some = Some 0 "test"  -- 定義一個(gè)數(shù)據(jù),類型為Some

-- 定義兩個(gè)函數(shù)用于獲取Some的數(shù)據(jù)
getSomeInt Some i _ = i
getSomeString Some _ s = s

getSomeInt some  -- 0
getSomeString some  -- "test"


這里的getSomeInt和getSomeString函數(shù)都是采用模式匹配來實(shí)現(xiàn),模式匹配就是把數(shù)據(jù)的結(jié)構(gòu)直接寫在代碼中來匹配,然后取出想要使用的數(shù)據(jù)即可。


Haskell里常用的Maybe數(shù)據(jù)類型是這樣定義的:


data Maybe a = Nothing
                     | Just a


如果要取用Maybe里面的值,我們通常使用模式匹配來獲取數(shù)據(jù),如下:


useMaybe maybe =
     case maybe of
          Nothing -> …  -- Maybe的值是空
          Just a -> …  -- 直接使用a即可

useMaybe (Just 1)


下面調(diào)用useMaybe的函數(shù)體內(nèi)取到的a的值就是1。


Haskell里的內(nèi)置數(shù)據(jù)類型list,比如[1, 2, 3, 4],使用:可以把新的元素添加到list頭部,即:


0 : [1, 2, 3, 4]  -- [0, 1, 2, 3, 4]


這樣的特性同樣可以簡單的套用在模式匹配上面,如下:


useList [] = 
useList (x:xs) = … -- x是list里面的第一個(gè)元素,xs是list的尾部


模式匹配可以很直觀的匹配數(shù)據(jù)的原始表示方式,并可以取出其中有用的值做其他操作,它是一個(gè)簡單直觀有效的操作數(shù)據(jù)的方式,甚至可以在嵌套很深的tuple數(shù)據(jù)里面直接取出想要的數(shù)據(jù),而不用像C++那樣調(diào)用tuple::get之類的函數(shù)來取出其中的值,比如:


getTupleValue (_, _, _, (_, _, (x:xs))) = … -- 取得x和xs數(shù)據(jù)


Visitor模式和C++ template


王垠說設(shè)計(jì)模式中值得一提的模式不多,其中之一的visitor模式是在模擬模式匹配。visitor模式通常是訪問獲取某個(gè)繼承類層次構(gòu)成的一個(gè)樹形結(jié)構(gòu)中的某個(gè)節(jié)點(diǎn)的具體類型,并對(duì)這種具體類型做某些操作,而模式匹配是可以從復(fù)雜的數(shù)據(jù)結(jié)構(gòu)中直接取出想要的數(shù)據(jù)。


C++的template meta-programming也可以看成是一種模式匹配,C++里面著名的Factorial求值是這樣的:


template <int N>
struct Factorial
{
     enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0>
{
     enum { value = 1 };
};

int v = Factorial<10>::value;


而這段代碼如果用Haskell寫是這樣的:


factorial 0 = 1
factorial n = n * factorial (n - 1)

v = factorial 10


C++中的模板參數(shù)就是用來做模式匹配的,每特化一種類型就可以匹配某種類型,然后對(duì)那種匹配的類型做相應(yīng)的操作。C++的template meta-programming是編譯時(shí)期(編譯器運(yùn)行期)的行為,所以它只能操作類型以及編譯時(shí)期能夠確定的值,而模式匹配是程序本身的運(yùn)行期的行為。


Currying


Haskell的Currying是一個(gè)很有用的特性,但是我覺得這個(gè)特性濫用的話,也會(huì)讓程序代碼的可讀性降低不少。所謂Currying就是可以向一個(gè)多參數(shù)的函數(shù)傳遞比它所需的參數(shù)個(gè)數(shù)更少的參數(shù)后返回生成的一個(gè)新函數(shù)接受剩余的參數(shù)的函數(shù)。Haskell里的函數(shù)默認(rèn)都是curried的,所以Haskell里面的函數(shù)可以隨意currying,比如:


add :: Int -> (Int -> Int)  -- 一般寫成 Int -> Int -> Int
add a b = a + b

addOne :: Int -> Int
addOne = add 1

addOne 2 -- result: 3


Currying的實(shí)現(xiàn)是使用的單參數(shù)的lambda構(gòu)成的閉包(closure),add可以看成是接受一個(gè)Int參數(shù)返回一個(gè)函數(shù),這個(gè)函數(shù)的類型是Int -> Int。


Partial application


Currying是一個(gè)從左到右部分傳參數(shù)的一個(gè)過程,也就是不會(huì)出現(xiàn)參數(shù)a還沒給,就給了具體的參數(shù)b的情況。如果確定要先給參數(shù)b,那么它是Partial application,如下:


addTwo a = add a 2

addTwo 1 -- result: 3

(+ 2) 1 -- result: 3


(+ 2)這種類似的用法可能會(huì)作為參數(shù)傳遞給另外一個(gè)函數(shù)。Partial application是一種更寬泛的概念,上面的Currying是一種Partial application。


正如王垠所說的,如果一個(gè)函數(shù)接受了多個(gè)參數(shù),但是這個(gè)函數(shù)在實(shí)際調(diào)用中被Currying了很多次,那最后生成的那個(gè)函數(shù)它到底接受幾個(gè)參數(shù)是不能很直觀的看明白的,比如:


func a b c d e f = …

do1 = func 1
do2 = do1 2
do3 = do2 3
do4 = do3 4
do5 = do4 5


那當(dāng)我們看到do5函數(shù)的時(shí)候,我們是很難判斷do5到底接受幾個(gè)參數(shù),尤其是do5跟前面幾個(gè)doN函數(shù)不在同一個(gè)地方定義,很有可能do5只是傳遞給某個(gè)函數(shù)的參數(shù),當(dāng)然如果給每個(gè)函數(shù)都加上函數(shù)類型聲明會(huì)清晰許多。當(dāng)Currying碰到了flip之后,那代碼的可讀性會(huì)降低更多,所以我覺得Currying是一個(gè)很有用的特性,但是如果被濫用的話,那代碼的可讀性會(huì)是一個(gè)問題。


C++: function + bind


C++中的function + bind其實(shí)是一種Partial application實(shí)現(xiàn),比如:


int Func(int a, int b, int c);

std::function<int (intint)> f1 = std::bind(Func, 1, std::placeholders::_1, std::placeholders::_2);
std::function<int (int)> f2 = std::bind(f1, std::placeholders::_1, 3);
f2(2); // Func(1, 2, 3);


我覺得C++的function + bind會(huì)比Currying的可讀性要好一些,畢竟我們可以完整看到f1和f2的函數(shù)類型,知道參數(shù)類型及個(gè)數(shù)和返回值,是有利于代碼的可讀性的,當(dāng)然這里完全可以不寫出f1和f2的類型,采用auto,我們同樣可以從調(diào)用函數(shù)bind的placeholder的個(gè)數(shù)得知bind之后的function的參數(shù)個(gè)數(shù),這樣我們可以不用看到函數(shù)Func的聲明,就知道需要傳幾個(gè)參數(shù)。function + bind跟Currying一樣會(huì)影響代碼的可讀性,如果嵌套的層次越多,可讀性就越差,所以使用這些特性的時(shí)候不要過度。


typeclass


Haskell用typeclass來表示一個(gè)concept,它是一組抽象函數(shù)的集合,一個(gè)滿足某個(gè)typeclass的數(shù)據(jù)類型,它就可以跟其他使用這個(gè)typeclass的函數(shù)或者數(shù)據(jù)類型組合使用。typeclass一般這么定義:


class Monad m where
     (>>=) :: m a -> (a -> m b) -> m b
     (>>) :: m a -> m b -> m b
     return :: a -> m a
     fail :: String -> m a


它定義了一個(gè)叫Monad的typeclass,這個(gè)typeclass的concept里有四個(gè)函數(shù),分別是(>>=), (>>), return和fail,m是一個(gè)帶類型參數(shù)的數(shù)據(jù)類型。我們上面知道了Maybe是一個(gè)帶類型參數(shù)的data類型,它定義如下:


data Maybe a = Nothing
                     | Just a


既然Maybe是一個(gè)帶類型參數(shù)的data,那它就滿足Monad typeclass中m的需求,因此可以把Maybe定義成Monad,如下:


instance Monad Maybe where
     (>>=) maybeA f =
          case maybeA of
               Nothing -> Nothing
               Just a -> f a

     (>>) maybeA maybeB = maybeA >>= (\_ -> maybeB)

     return = Just

     fail = error


這里(\_ -> maybeB)定義了一個(gè)lambda,參數(shù) _ 緊接 \,-> 后面則是函數(shù)體。函數(shù)(>>)和fail是可以作為默認(rèn)實(shí)現(xiàn)放到class Monad的定義里面,而instance Monad的時(shí)候只需要實(shí)現(xiàn)(>>=)和return即可。


class Monad m where
     (>>=) :: m a -> (a -> m b) -> m b
     (>>) :: m a -> m b -> m b
     (>>) ma mb = ma >>= (\_ -> mb)
     return :: a -> m a
     fail :: String -> m a
     fail = error


對(duì)于內(nèi)置list類型[a],也是帶有一個(gè)類型參數(shù)a,因此,我們同樣可以把[] instance成為class Monad,如下:


instance Monad [] where
     (>>=) (x:xs) f = (f x) ++ (xs >>= f)
     (>>=) [] _ = []
     return a = [a]


函數(shù)(>>)和fail我們保留默認(rèn)的實(shí)現(xiàn)即可。


Monad


上面實(shí)現(xiàn)的定義的typeclass就是Haskell著名的Monad,它是組合其他操作的一個(gè)基礎(chǔ)typeclass,是與no pure交互的一個(gè)重要媒介。一般情況下Monad有兩種,一種是數(shù)據(jù)wrapper,一種是action的wrapper。上面定義的Maybe Monad和list Monad都是數(shù)據(jù)類型的wrapper,它們實(shí)現(xiàn)了Monad定義的接口函數(shù),我們還可以將其它data instance成Monad,只需要遵循了Monad的接口即可。


我們知道Haskell的函數(shù)都是pure的,沒有任何狀態(tài)的函數(shù),但是與現(xiàn)實(shí)世界交互必然需要影響或修改某種狀態(tài),并且會(huì)需要順序執(zhí)行某些操作以完成交互。我們把a(bǔ)ction操作封裝在一個(gè)data里面,并讓它instance Monad,為了讓前一個(gè)action的結(jié)果值作為某種狀態(tài)往下傳遞,Monad的(>>=)就是為了這個(gè)目的而存在的,(>>=) 函數(shù)的類型是 m a -> (a -> m b) -> m b,它的意思就是執(zhí)行封裝在m a這個(gè)數(shù)據(jù)里面的action,然后把這個(gè)action的結(jié)果值做為參數(shù)傳遞給(>>=)的第二個(gè)參數(shù)(a -> m b),第二個(gè)參數(shù)是一個(gè)函數(shù),這函數(shù)可以取用第一個(gè)參數(shù)的結(jié)果,再返回一個(gè)m b的數(shù)據(jù),m b的數(shù)據(jù)也是一個(gè)action的封裝,這樣當(dāng)一連串的(>>=)放到一起的時(shí)候,就可以把一個(gè)狀態(tài)值作為action的參數(shù)和結(jié)果值往下傳遞。


從Monad的函數(shù)(>>)的實(shí)現(xiàn)我們可以看到,它把m a的action的結(jié)果值丟棄直接返回了m b,當(dāng)一連串的(>>)放到一起的時(shí)候,其實(shí)就是讓一組action順序執(zhí)行。通過(>>=)和(>>),可以把一組Monad action data組合起來。


IO Monad


IO Monad是一個(gè)把IO action封裝的data,我們可以使用IO Monad與外界進(jìn)行輸入輸出交互,下面是一個(gè)"hello world":


helloWorld = do
     putStr "hello "
     putStrLn "world"


這里do語法糖其實(shí)就是用的Monad來實(shí)現(xiàn),展開之后是這樣:


helloWorld =
     (putStr "hello ") >>
     (putStrLn "world")


由(>>)函數(shù)確定(putStr "hello ")和(putStrLn "world")需要是同一個(gè)Monad類型,我們可以查詢到putStr和putStrLn的類型是String -> IO (),那么(putStr "hello ")和(putStrLn "world")的類型都是IO (),helloWorld函數(shù)把兩個(gè)IO ()的action數(shù)據(jù)順序組合起來生成一個(gè)新的IO (),當(dāng)這個(gè)helloWorld IO action被執(zhí)行的時(shí)候,它會(huì)依次執(zhí)行封裝在它里面的IO action。我們可以把helloWorld IO action放到Main函數(shù)里面然后編譯執(zhí)行,也可以直接在ghci里面執(zhí)行。


我們可以自己定義某種data再instance Monad,這樣可以構(gòu)成一組data combination,可以實(shí)現(xiàn)任意的action combine。我山寨的極簡的parse combinator的數(shù)據(jù)類型定義如下:


newtype Parser a = Parser {
     runP :: State (ByteString, Context) a
} deriving (Monad, MonadState (ByteString, Context))


這里Parser帶一個(gè)類型參數(shù)a,deriving (Monad, MonadState (ByteString, Context))表示編譯器自動(dòng)instance Monad和instance MonadState (ByteString, Context)。有了這個(gè)Parser之后,可以寫出簡單的幾個(gè)combinator,然后使用這幾個(gè)combinator組合成更加復(fù)雜的,組合的過程就是利用了Monad的組合能力。當(dāng)所需的combinator都實(shí)現(xiàn)了好了之后,可以最終實(shí)現(xiàn)一個(gè)Parser a來分析整個(gè)C++文件:


file = repeatP $ spaceLine <||> normalLine


file就把分析整個(gè)C++文件所需的操作都combine到了一起,有了這個(gè)分析整個(gè)文件的Parser a之后,需要把它跑起來,那就需要定義下面這個(gè)函數(shù):


runParse :: Parser a -> ByteString -> (a, (ByteString, Context))
runParse p b = runState (runP p) $ (b, emptyContext)


這個(gè)函數(shù)接受一個(gè)Parser a和一個(gè)文件內(nèi)容ByteString作為參數(shù),把整個(gè)Parser a封裝的action用于分析文件內(nèi)容,再產(chǎn)生一個(gè)分析結(jié)果。


這里的file,它是一個(gè)一個(gè)小的combinator構(gòu)成的,每個(gè)combinator是一個(gè)action加上它所需數(shù)據(jù)構(gòu)成一個(gè)“閉包”再存放到Parser a的data里面,其實(shí)可以認(rèn)為實(shí)現(xiàn)了Monad的數(shù)據(jù)類型是一個(gè)“閉包”的載體。在其它語言里,我們可以使用閉包來實(shí)現(xiàn)combinator,我記得兩年半前,我使用lua的閉包實(shí)現(xiàn)了一組游戲副本內(nèi)容玩法操作的combinator,這些閉包自由組合在一起之后就能完成一個(gè)副本中所需的玩法操作。


Monad transformer


一種Monad類型只能封裝和組合一種action操作,而與外界交互的時(shí)候,很多時(shí)候一種Monad類型是不夠的,為了讓多種Monad類型組合在一起,就需要定義Monad transformer,它跟Monad一樣也是一個(gè)數(shù)據(jù)類型,不同的是它接受至少兩種類型參數(shù),其中一種就是Monad的類型,這樣就可以把某個(gè)Monad類型嵌套在它里面。


newtype StateT s m a = StateT {
     runStateT :: s -> m (a, s)
}


這里StateT就是一個(gè)Monad transformer,它允許嵌套一個(gè)Monad m類型,它是typeclass MonadState的一個(gè)instance,MonadState如下:


class Monad m => MonadState s m | m -> s where
     get :: m s
     put :: s -> m ()


為了讓Monad transformer可以嵌套進(jìn)StateT,其它類型的Monad transformer就需要instance MonadState,而StateT Monad transformer為了可以嵌套在其它Monad transformer中,就需要對(duì)其它Monad transformer抽象出來的typeclass instance,符合這種規(guī)則的Monad transformer就可以相互之間嵌套了,嵌套的層次可以任意深,這樣構(gòu)造出來的Monad里面有個(gè)Monad transformer stack,而這個(gè)新構(gòu)造出來的Monad就可以使用多種Monad的action操作組合在一起了。


Monad transformer會(huì)帶來一個(gè)問題,如果想定義一個(gè)新的Monad transformer,需要先抽象出這個(gè)Monad transformer的typeclass,就像MonadState typeclass一樣,然后把其它Monad transformer都instance這個(gè)新抽象出來的typeclass,這樣才能讓這個(gè)新的Monad transformer嵌套在其它的Monad transformer之中,接著,為了讓其它Monad transformer能夠嵌套在新的Monad transformer之中,需要把新的Monad transformer instance其它Monad transformer抽象的typeclass。


我覺得其實(shí)Haskell為什么會(huì)有Monad和Monad transformer的存在,是因?yàn)镠askell是一個(gè)純函數(shù)式語言,它本身沒有順序執(zhí)行語句的能力,為了能讓Haskell擁有修改外部狀態(tài)并能夠順序執(zhí)行語句的能力,引入了Monad,又為了讓多種action的Monad能夠組合到一起,由于Monad是一個(gè)data type,它不能簡單的組合到一起,因?yàn)轭愋筒灰恢拢瑸榱俗屗鼈兘M合到一起,又引入了更一般化的Monad transformer,讓這些Monad transformer嵌套在一起構(gòu)成一個(gè)stack,才能將這些不同類型的Monad組合。


Lazy evaluation


Haskell里面使用的是惰性求值方式,王垠說Haskell的惰性求值是一個(gè)很嚴(yán)重的問題。我目前也覺得惰性求值是一種負(fù)擔(dān),因?yàn)槎栊郧笾担瑫?huì)使得程序很容易就出現(xiàn)space leak,我寫的那兩個(gè)版本的統(tǒng)計(jì)C++代碼行工具都有這個(gè)問題,因?yàn)樗嵌栊郧笾担运鼤?huì)把整個(gè)目錄的數(shù)據(jù)全部取出來構(gòu)造存放到內(nèi)存中,最后再進(jìn)行求值,這就自然導(dǎo)致統(tǒng)計(jì)大量C++代碼文件的目錄時(shí),占用內(nèi)存會(huì)很高(幾百M(fèi)上G),也許當(dāng)我進(jìn)一步學(xué)習(xí)之后,我能夠避免這種space leak,但這對(duì)于一個(gè)初學(xué)Haskell的人是一個(gè)不小的負(fù)擔(dān),因?yàn)殡S便寫一個(gè)小程序都有可能耗用幾百M(fèi)的內(nèi)存,而用其他語言實(shí)現(xiàn)的話,內(nèi)存很容易很自然的控制在幾M之內(nèi)。(看完優(yōu)化章節(jié),只對(duì)程序修改了幾行代碼就讓內(nèi)存使用降到可以接受的程度,看來Lazy evaluation的問題沒之前想像的那么嚴(yán)重。)

posted on 2013-04-30 20:41 airtrack 閱讀(5533) 評(píng)論(0)  編輯 收藏 引用

只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            性欧美大战久久久久久久久| 影音先锋久久| 国产亚洲一二三区| 在线观看欧美成人| 久久久精品五月天| 欧美fxxxxxx另类| 一本色道久久综合亚洲精品不| 欧美国产日韩一区二区| 欧美国产第一页| 一区二区三区视频在线观看| 老司机午夜精品视频在线观看| 欧美日韩视频在线一区二区观看视频| 香蕉尹人综合在线观看| 欧美成人综合在线| 在线一区欧美| 亚洲激情一区| 久久露脸国产精品| 国产精品久久久久毛片软件| 日韩亚洲欧美精品| 欧美激情一区二区三区蜜桃视频| 亚洲欧美日韩国产综合| 国产一区观看| 亚洲电影在线播放| 欧美激情精品久久久久久蜜臀| 国产日韩视频| 中文国产成人精品| 欧美一区二区日韩| 亚洲成色www8888| 亚洲日本免费电影| 国产一区二区精品| 午夜影院日韩| 永久免费毛片在线播放不卡| 亚洲伦理精品| 亚洲激情不卡| 亚洲电影成人| 国产精品青草综合久久久久99 | 一区二区三区|亚洲午夜| 欧美日韩情趣电影| 久久久噜噜噜久久狠狠50岁| 亚洲一区二区三区在线观看视频| 国内精品视频一区| 亚洲一区视频在线| 亚洲电影免费| 狂野欧美一区| 老司机免费视频一区二区三区| 午夜精品成人在线| 亚洲久久一区二区| 激情综合电影网| 影音先锋成人资源站| 一区二区三区在线不卡| 在线欧美不卡| 亚洲免费观看在线视频| 一区二区欧美日韩视频| 亚洲欧美国产日韩天堂区| 久久xxxx| 欧美国产日韩精品| 99精品欧美一区| 午夜精品短视频| 欧美福利网址| 国产精品videossex久久发布| 国产欧美日韩一区| 在线视频观看日韩| 亚洲一区视频| 久久久久久一区| 亚洲美女视频在线免费观看| 亚洲婷婷国产精品电影人久久 | 国产性天天综合网| 激情婷婷久久| 午夜欧美精品久久久久久久| 欧美成人激情在线| 亚洲天堂偷拍| 欧美日韩成人免费| 在线电影院国产精品| 欧美在线观看一区二区| 亚洲黄一区二区三区| 久久久一区二区| 国产色产综合色产在线视频| 亚洲特色特黄| 亚洲美女少妇无套啪啪呻吟| 欧美91福利在线观看| 亚洲成色777777在线观看影院| 亚洲欧美日韩一区| 亚洲影院色无极综合| 国产精品嫩草影院av蜜臀| 亚洲一区免费在线观看| 一区二区三区国产盗摄| 欧美人体xx| 亚洲欧美日韩另类| 久久中文精品| 久久午夜色播影院免费高清| 国产色爱av资源综合区| 久久久久久久久久看片| 久久久在线视频| 亚洲精品免费在线播放| 亚洲伦伦在线| 国产欧美在线观看| 久久久亚洲影院你懂的| 欧美大片在线观看一区二区| 一区二区三区福利| 欧美在线视频a| 亚洲乱码国产乱码精品精| 亚洲视频自拍偷拍| 亚洲高清网站| 亚洲午夜国产一区99re久久| 国语自产精品视频在线看8查询8| 亚洲第一中文字幕| 国产欧美一区二区精品性色| 亚洲黄一区二区三区| 国产一区自拍视频| 宅男66日本亚洲欧美视频| 亚洲激情综合| 久久国产乱子精品免费女| 亚洲小说区图片区| 免费久久精品视频| 久色成人在线| 激情一区二区| 欧美一区影院| 久久福利毛片| 国产乱码精品| 亚洲欧美日韩精品久久久| 亚洲欧美日韩第一区| 欧美日韩日本网| 亚洲精品久久7777| av不卡免费看| 国产精品高潮粉嫩av| 一区二区免费在线播放| 一级成人国产| 国产精品国产三级国产| 亚洲欧美日韩视频一区| 久久精品人人做人人爽| 影音先锋一区| 欧美精品一区二区精品网| 亚洲人妖在线| 欧美一级专区免费大片| 国产性色一区二区| 欧美日韩国产综合久久| 久久久久www| 亚洲精品久久久久中文字幕欢迎你| 免费久久精品视频| 亚洲国产欧美日韩| 午夜日韩在线观看| 亚洲国产精品123| 国产精品久线观看视频| 久久久久久9| 亚洲欧美www| 欧美高清在线一区| 久久精品夜色噜噜亚洲a∨| 最新亚洲视频| 激情一区二区| 国产精品男女猛烈高潮激情| 蜜乳av另类精品一区二区| 亚洲视频日本| 一本色道久久88亚洲综合88| 免费欧美在线| 久久亚洲私人国产精品va媚药| 夜夜精品视频一区二区| 亚洲精品国产品国语在线app | 久久激情视频久久| 亚洲视频1区2区| 一区二区欧美精品| 亚洲视频在线播放| 一区二区三区欧美在线观看| 亚洲国产精品综合| 亚洲娇小video精品| 亚洲黄色尤物视频| 亚洲欧洲在线看| 亚洲国产合集| 夜夜爽夜夜爽精品视频| 99热这里只有精品8| 亚洲一级一区| 午夜在线a亚洲v天堂网2018| 久久av老司机精品网站导航| 久久精品国产精品| 欧美承认网站| 亚洲精品一二三| 亚洲一区在线观看视频 | 欧美一级淫片aaaaaaa视频| 久久精品二区三区| 蜜桃伊人久久| 亚洲无毛电影| 免费日韩精品中文字幕视频在线| 欧美日韩色综合| 国产日韩一区二区| av成人免费在线| 久久综合伊人77777麻豆| 亚洲啪啪91| 欧美一区二区三区四区视频| 免费成人黄色av| 国产精品亚洲精品| 99视频+国产日韩欧美| 久久久夜夜夜| 午夜久久久久久| 国产精品第2页| 99国产精品国产精品久久 | 国产精品成人免费视频| 亚洲高清不卡在线观看| 久久人人九九| 欧美在线国产| 黑人巨大精品欧美一区二区小视频| 亚洲一级在线|