func 第一節(jié)() -> Functions

Functions

第一節(jié)討論的都是函數(shù)爹橱!我們將會(huì)進(jìn)行一波“是什么讓函數(shù)特別”须妻、“函數(shù)式編程與我們?nèi)粘5木幋a方式的差異”以及一些關(guān)于操作符及其結(jié)合方式的探索式討論。

Introduction

歡迎加入Point-Free! Point-Free將會(huì)囊括許多函數(shù)式編程的概念澜薄,所以讓我們從定義一個(gè)計(jì)算輸入輸出的函數(shù)開始吧侣滩。
定義對(duì)Int類型數(shù)值進(jìn)行加1操作的一個(gè)函數(shù):

func incr(_ x: Int) -> Int {
    return x + 1
}

調(diào)用這個(gè)函數(shù)初烘,入?yún)?

incr(2) // 3

定義一個(gè)求平方的函數(shù):

func square(_ x: Int) -> Int {
    return x * x
}

同樣調(diào)用這個(gè)函數(shù):

square(2) // 4

現(xiàn)在我們將這兩個(gè)函數(shù)進(jìn)行組合調(diào)用:

square(incr(2)) // 9

很簡(jiǎn)單涡真,但是在Swift中并不常見分俯。在設(shè)計(jì)最外層、可使用的純函數(shù)時(shí)通常會(huì)避免“方法“的調(diào)用(注: 翻譯不準(zhǔn)確哆料,請(qǐng)移步原始內(nèi)容)缸剪。

我們可以通過(guò)給Int進(jìn)行擴(kuò)展來(lái)實(shí)現(xiàn)這兩個(gè)方法:

extension Int {
    func incr() -> Int {
        return self + 1
    }
    
    func square() -> Int {
        return self * self
    }
}

想使用incr()方法時(shí),我們可以直接調(diào)用

2.incr() // 3

后面還能讓這個(gè)函數(shù)的結(jié)果再進(jìn)行平方:

2.incr().square()

這樣使用純函數(shù)后從左至右讀起來(lái)更加的漂亮和流暢东亦,比起之前的調(diào)用方法杏节,在調(diào)用square()函數(shù)之前去看看incr()函數(shù)做了什么更加節(jié)省思考時(shí)間。這也許是純函數(shù)在Swift中并不常見的原因典阵。一個(gè)簡(jiǎn)答的表達(dá)式在使用傳統(tǒng)的方法調(diào)用時(shí)閱讀起來(lái)會(huì)更加困難奋渔。可以想象壮啊,如果使用嵌套函數(shù)結(jié)合起來(lái)嫉鲸,那讀起來(lái)基本就GG了。單純的方法調(diào)用則不會(huì)有這個(gè)問(wèn)題歹啼。

Introduction |> 操作符

其他的某些編程語(yǔ)言通過(guò)一些前綴操作符實(shí)現(xiàn)了純函數(shù)并且保持了代碼的可讀性玄渗。Swift允許我們自定義操作符,讓我們來(lái)試試:

infix operator |>

這里我們定義了一個(gè)“管道傳遞”操作符狸眼,F(xiàn)#藤树,Elixir以及Elm中都用來(lái)進(jìn)行函數(shù)的組合操作。

func |> <A, B>(a: A, f: (A) -> B) -> B {
    return f(a)
}

這個(gè)函數(shù)中有兩個(gè)泛型:A和B拓萌。左側(cè)是我們的輸入值岁钓,類型為A孤紧,右側(cè)是我們的操作函數(shù)f: (A) -> B凌受。我們最終通過(guò)函數(shù)f操作輸入A來(lái)得到返回值B距芬。
現(xiàn)在我們能將一個(gè)輸入值加入到純函數(shù)的管道中:

2 |> incr // 3

我們應(yīng)該也能將管道上端的輸入加入管道并調(diào)用下一個(gè)函數(shù):

2 |> incr |> square

但是我們會(huì)得到一個(gè)編譯錯(cuò)誤:

adjacent operators are in non-associative precedence group 'DefaultPrecedence'
2 |> incr |> square

當(dāng)我們?cè)谝恍写a中多次使用|>操作符時(shí)汇恤,Swift并不知道這個(gè)操作符應(yīng)該先計(jì)算哪一邊囚玫。在左邊:

2 |> incr

將2加入到管道中當(dāng)然是可以的趣避,因?yàn)閕ncr函數(shù)的入?yún)⒕托枰粋€(gè)Int瞧省。在右邊:

incr |> square

將incr函數(shù)傳遞給square函數(shù)當(dāng)然是不行的巢音,因?yàn)閟quare函數(shù)入?yún)⑿枰氖且粋€(gè)Int類型激才,而不是一個(gè) (Int) -> Int 類型的函數(shù)拓型。
我們需要給Swfit加上一些提示,讓編譯器知道|>操作符的結(jié)合順序:

(2 |> incr) |> square // 9

工作正常瘸恼,但是看起來(lái)有點(diǎn)凌亂劣挫。這只是一個(gè)簡(jiǎn)單的操作結(jié)合,試想如果有非常多的操作時(shí)东帅,將會(huì)有非常非常的括號(hào)压固,難以閱讀。所以讓我們給Swift加上一個(gè)更加有效的提示靠闭。
Swift允許我們通過(guò)使用優(yōu)先級(jí)組來(lái)定義操作符的結(jié)合順序:

precedencegroup ForwardApplication {
    associativity: left
}

我們讓操作符具有左結(jié)合性帐我,使其優(yōu)先求得操作符左側(cè)的值坎炼。
現(xiàn)在讓我們定義的|>操作符遵守這個(gè)ForwardApplication優(yōu)先級(jí)組

infix operator |>: ForwardApplication

然后就可以將括號(hào)給移除了:

2 |> incr |> square

看起來(lái)非常像之前的鏈?zhǔn)秸{(diào)用:

2.incr().square()

操作符插曲

我們已經(jīng)解決了函數(shù)嵌套的可讀性問(wèn)題,但是又碰到了一個(gè)新的問(wèn)題:自定義操作符拦键。自定義操作符并不常用谣光。事實(shí)上大多數(shù)人因?yàn)槠潆y以預(yù)計(jì)的風(fēng)險(xiǎn)而避免使用自定義操作符。一個(gè)典型的例子就是重載操作符芬为。
比如在C++中萄金,我們無(wú)法定義新的操作符,但是可以重載已有的操作符媚朦。如果我們打算寫一個(gè)矢量庫(kù)氧敢,我們可以重載+操作符用于求得兩個(gè)矢量相加的結(jié)果,重載操作符求得兩個(gè)矢量點(diǎn)乘的結(jié)果莲镣。然而福稳,也可以表示兩個(gè)矢量叉乘的操作,這樣就使得*操作符的使用變得有些混淆和迷惑瑞侮。
我們絕不建議在函數(shù)的實(shí)際應(yīng)用中重載乘法操作符:

2 * incr // 這是什么鬼的圆?

如果我們定義了這樣一個(gè)操作符,將很難知道它到底是什么意思半火。
但是很幸運(yùn)越妈,我們這里沒有這樣的問(wèn)題。我們是重新定義的操作符|>钮糖,Swift并沒有預(yù)置它梅掠。也許有人會(huì)反駁說(shuō)既然Swift沒有這個(gè)操作符,那么Swift開發(fā)人員也沒有必要知道和使用它店归,然而在其他語(yǔ)言比如F#阎抒,Elixir和Elm,都在使用它消痛。熟悉這些編程語(yǔ)言的Swift開發(fā)者應(yīng)該也很熟悉這個(gè)操作符且叁。而且它看起來(lái)非常簡(jiǎn)潔美觀。管道標(biāo)識(shí)(|)的靈感來(lái)自Unix秩伞,意思是一個(gè)程序的輸出作為另一個(gè)程序輸入逞带。箭頭(>)也指向右側(cè),給我們直觀地從左至右的閱讀體驗(yàn)纱新,

即使我們現(xiàn)在對(duì)這個(gè)操作符不太熟悉展氓,也讓我們繼續(xù)下去,看看接下來(lái)的內(nèi)容脸爱。
由于在Point-Free中我們會(huì)用到非常多的操作符遇汞,所以有必要給出這些操作符的規(guī)范和使用。下列是我們覺得在介紹新操作符之前必須了解的規(guī)則:

  1. 不能重載已有確定意義的操作符
  2. 操作符應(yīng)該擁有簡(jiǎn)潔、直觀的符號(hào)來(lái)表示其意義
  3. 不能為特定操作定義操作符勺疼,而只是應(yīng)該抽象出非常通用教寂,可復(fù)用的操作符

自動(dòng)補(bǔ)全呢?

雖然操作符賦予了代碼良好的可讀性执庐,但我們?nèi)匀缓鲆暳舜a編寫時(shí)一個(gè)非常重要的特性:自動(dòng)補(bǔ)全。
在Xcode中导梆,我們可以輸入一個(gè)值轨淌,隨后就可以使用"."號(hào)來(lái)喚出其所有可調(diào)用的方法。甚至可以再輸入一些字符讓方法補(bǔ)全的范圍縮小到想要使用的范圍看尼。
這是代碼可查找性非常好的地方递鹉,但自動(dòng)補(bǔ)全對(duì)方法的實(shí)現(xiàn)本身來(lái)說(shuō)并不重要。純函數(shù)也會(huì)有代碼補(bǔ)全藏斩。
但這是頂層設(shè)計(jì)時(shí)應(yīng)該考慮的問(wèn)題躏结,所以我們現(xiàn)在缺失了自定義操作時(shí)的代碼補(bǔ)全功能,畢竟這些新的操作符并不會(huì)阻礙我們的開發(fā)環(huán)境理解它們狰域,輸入一個(gè)值媳拴,然后調(diào)用|>方法,應(yīng)該有一個(gè)自動(dòng)補(bǔ)全|>讓這個(gè)值作為入?yún)⒄桌馈OM@個(gè)問(wèn)題可以再新版本的Xcode中得到解決屈溉。

>>>操作符

與此同時(shí),純函數(shù)的某些特性在普通方法的范圍內(nèi)也有無(wú)法解決的地方:函數(shù)的結(jié)合抬探。兩個(gè)函數(shù)中子巾,如果一個(gè)函數(shù)的輸出和另一個(gè)函數(shù)的輸入匹配,就可以將兩個(gè)函數(shù)粘連在一起形成一個(gè)新的函數(shù)小压,這里我們要介紹一個(gè)新的操作符 >>>:

infix operator >>>

很明顯它看起來(lái)就是左結(jié)合或者右輸出的操作符线梗。我們來(lái)定義它的實(shí)現(xiàn):

func >>> <A, B, C>(f: @escaping (A) -> B, g: @escaping (B) -> C) -> ((A) -> C) {
    return { a in
        return g(f(a))
    }
}

這個(gè)函數(shù)有3個(gè)泛型約束,A, B, C怠益。入?yún)⑹莾蓚€(gè)函數(shù)f:(A) -> B仪搔,g:(B) -> C。f的入?yún)⑹茿類型溉痢,輸出是B類型僻造;g的入?yún)⑹荁類型,輸出是C類型孩饼。將這兩個(gè)函數(shù)結(jié)合在一起髓削,形成一個(gè)新的入?yún)锳類型的函數(shù),并且將輸出的B類型參數(shù)傳遞給函數(shù)g镀娶,返回值為C類型的新的函數(shù)立膛。

現(xiàn)在我們將incr函數(shù)和square函數(shù)結(jié)合起來(lái):

incr >>> square

也可以
現(xiàn)在有了一個(gè)新的Int -> Int類型的函數(shù),先進(jìn)行加1再平方。
也可以反轉(zhuǎn)結(jié)合順序:

square >>> incr

我們可以用常用的方法調(diào)用這個(gè)新的函數(shù):

(square >>> incr)(3) // 10

上面的代碼讀起來(lái)可能不太流暢宝泵,可以使用之前的|>操作符增加可讀性:

2 |> square >>> incr

不幸的是這時(shí)我們會(huì)得到一個(gè)錯(cuò)誤:

Adjacent operators are in unordered precedence groups 'ForwardApplication' and 'DefaultPrecedence'

由于我們混用了兩個(gè)操作符好啰,Swift并不知道哪個(gè)操作符應(yīng)該先使用。我們的想法是先將square和incr操作結(jié)合起來(lái)得到一個(gè)新的函數(shù)儿奶,再由2來(lái)調(diào)用框往。如果按照當(dāng)前的順序,2先調(diào)用square方法闯捎,得到Int型的返回值椰弊,再調(diào)用操作符>>>,顯然類型不匹配瓤鼻,無(wú)法調(diào)用成功秉版。
此時(shí)我們?cè)偈褂胮recedencegroup來(lái)解決這個(gè)問(wèn)題:

precedencegroup ForwardComposition {
    associativity: left
    higherThan: ForwardApplication
}

infix operator >>>: ForwardComposition

上面的代碼可以看出,F(xiàn)orwardComposition比ForwardApplication擁有更高的優(yōu)先級(jí)茬祷,在它們同時(shí)出現(xiàn)的時(shí)候會(huì)優(yōu)先調(diào)用∏寤溃現(xiàn)在我們的運(yùn)算看起來(lái)會(huì)非常直觀了:

2 |> incr >>> square // 9

但先別高興得太早,我們還需要驗(yàn)證這個(gè)新的操作符是否符合之前我們提出的3個(gè)規(guī)則:

  1. 不能重載已有確定意義的操作符
  2. 操作符應(yīng)該擁有簡(jiǎn)潔祭犯、直觀的符號(hào)來(lái)表示其意義
  3. 不能為特定操作定義操作符秸妥,而只是應(yīng)該抽象出非常通用,可復(fù)用的操作符

看起來(lái)全部滿足盹憎!

方法的結(jié)合

在方法調(diào)用的世界中函數(shù)的結(jié)合是什么樣呢筛峭?如果我們想要用函數(shù)式結(jié)合它們,除了為Int添加一個(gè)結(jié)合了兩個(gè)方法的新的犯法外別無(wú)選擇:

extension Int {
    func incrAndSquare() -> Int {
        return self.incr().square()
    }
}

調(diào)用這個(gè)新的方法:

2.incrAndSquare()

工作正常陪每,但是我們另外寫了好幾行代碼影晓,規(guī)定了調(diào)用的類型,在看看incr()和square()方法檩禾,只是整個(gè)場(chǎng)景中非常小的功能挂签。如果這樣的結(jié)合需要這么多的工作,我們是不是應(yīng)該問(wèn)問(wèn)這是否值得呢盼产?
與此同時(shí)饵婆,看看我們沒有任何其他影響,即開即用的新操作符

incr >>> square

你可以進(jìn)一步看看最小的有效組件的重用情況戏售。即使我們刪去部分的函數(shù)調(diào)用侨核,整個(gè)程序依然工作正常。

2 |> incr
2 |> square
2 |> incr >>> square

如果只使用方法的形式灌灾,在沒有輸入的情況下卻無(wú)法得到可編譯的程序:


// 有效
2.incr().square()

// 無(wú)效

.incr().square()

.incrAndSquare()

正因如此搓译,它們的復(fù)用性就沒有那么好了!

也許在Swift的日常使用中我們感覺一直在使用方法而不是函數(shù)锋喜,但其實(shí)函數(shù)無(wú)處不在只是我們沒有意識(shí)到而已些己。

一個(gè)日常使用中我們經(jīng)常使用的便是初始化豌鸡!它是一個(gè)返回當(dāng)前類型值的全局函數(shù)。而且所有的初始化方法在我們的函數(shù)結(jié)合操作中無(wú)縫使用段标。我們可以將之前的操作和String的初始化方法結(jié)合使用來(lái)一窺管豹:

incr >>> square >>> String.init

調(diào)用:

2 |> incr >>> square >>> String.init // "9"

與此同時(shí)涯冠,在方法的世界中,無(wú)法將每個(gè)方法的調(diào)用結(jié)果鏈?zhǔn)絺鬟f到下一個(gè)方法逼庞,而必須調(diào)整方法的調(diào)用順序蛇更。

String(2.incr().square())

除了初始化方法是純函數(shù)的,標(biāo)準(zhǔn)庫(kù)中還有大量操作函數(shù)將純函數(shù)作為輸入赛糟。比如Array械荷,我們有一個(gè)map方法:

[1, 2, 3].map // (transform: (Int) throws -> T) rethrows -> T

map方法使用一個(gè)純函數(shù)作為入?yún)ⅲrray中的每個(gè)元素都在這個(gè)純函數(shù)的映射作用下輸出T類型的新元素虑灰,這些新元素組成的新數(shù)組作為map方法整個(gè)的返回值。
一個(gè)典型的操作痹兜,將數(shù)組中的每個(gè)元素加1后再平方:

[1, 2, 3].map { ($0 + 1) * ($0 + 1) } // [4, 9, 16]

當(dāng)我們這樣操作時(shí)穆咐,已經(jīng)失去了復(fù)用性。但我們使用的是函數(shù)字旭,所以可以這樣寫:

[1, 2, 3]
  .map(incr)
  .map(square) // [4, 9, 16]

我們沒有新增任何新的函數(shù)或者參數(shù)对湃,這樣的調(diào)用方式稱為"point-free"風(fēng)格。當(dāng)我們定義函數(shù)遗淳,規(guī)定參數(shù)時(shí)拍柒,即使是用$0表示,這些參數(shù)也被視作"points"屈暗。在"point-free"編程風(fēng)格中強(qiáng)調(diào)函數(shù)以及函數(shù)間的結(jié)合拆讯,所以我們幾乎都不用關(guān)心所操作的數(shù)據(jù)。這也是我們這個(gè)討論系列叫做"point-free"的原因!

在使用incr函數(shù)映射我們的數(shù)組后养叛,再用square函數(shù)繼續(xù)映射得到的結(jié)果和我們之前談到的函數(shù)的結(jié)合是完全等價(jià)的种呐。我們可以使用一次操作完成這個(gè)任務(wù):

[1, 2, 3].map(incr >>> square) // [4, 9, 16]

這看起來(lái)酷極了!我們看到使用方法進(jìn)行map時(shí)難以直觀了解的操作如何在函數(shù)結(jié)合中得到清晰的展現(xiàn)弃甥。在這里我們可以看到map操作將兩步操作合并成一個(gè)結(jié)合起來(lái)的函數(shù)爽室,分發(fā)到各個(gè)元素。之后我們將會(huì)探索更多類似的操作淆攻!

目的何在阔墩?

讓我們放慢腳步,問(wèn)問(wèn)自己:這樣做的目的何在瓶珊?我們?yōu)槭裁匆鲞@些啸箫?這一節(jié)我們自定義了兩個(gè)操作符,使用純函數(shù)污染了全局命名空間艰毒,為什么不繼續(xù)使用我們熟悉并且喜愛的方法式調(diào)用呢筐高?

希望我們今天編寫的代碼為將函數(shù)引入日常工作提供了一個(gè)強(qiáng)力的佐證:函數(shù)可以在方法式調(diào)用無(wú)法完成的方式下結(jié)合起來(lái)。使用方法式調(diào)用將這些函數(shù)結(jié)合起來(lái)會(huì)費(fèi)時(shí)費(fèi)力,并且結(jié)合之后給閱讀代碼造成困難柑土。僅僅構(gòu)造出幾個(gè)操作符蜀肘,我們解鎖了之前不曾看到的新的由小及大的結(jié)合型的世界,并且得到了我們所期望的代碼可讀性稽屏!

Swift也沒有我們所擔(dān)心的真正意義上的全局命名空間扮宠。我們可以為我們定義的函數(shù)定制多種使用范圍:

  1. 可以為一個(gè)文件定義私有的函數(shù)
  2. 可以將函數(shù)定義為結(jié)構(gòu)體或者枚舉的靜態(tài)成員
  3. 可以為模塊定制函數(shù)。甚至可以在不同模塊間定義同名的函數(shù)狐榔,并在不同的模塊中定制這些函數(shù)坛增。

一句話:“不必害怕函數(shù)”

我們?cè)?Point-Free"中將大量使用函數(shù)。幾乎很難找到我們某一節(jié)內(nèi)容中不使用純函數(shù)薄腻。我們將會(huì)在僅僅使用函數(shù)及其結(jié)合的情況下構(gòu)造出復(fù)雜應(yīng)用收捣。在實(shí)踐它們?nèi)绾问褂靡约敖Y(jié)合時(shí),將會(huì)非常美妙庵楷、有趣罢艾。函數(shù)的結(jié)合將會(huì)幫助我們看到以前未知的世界。

這就是這節(jié)的內(nèi)容尽纽,請(qǐng)繼續(xù)關(guān)注我們咐蚯!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市弄贿,隨后出現(xiàn)的幾起案子春锋,更是在濱河造成了極大的恐慌,老刑警劉巖差凹,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件期奔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡直奋,警方通過(guò)查閱死者的電腦和手機(jī)能庆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)脚线,“玉大人搁胆,你說(shuō)我怎么就攤上這事∮事蹋” “怎么了渠旁?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)船逮。 經(jīng)常有香客問(wèn)我顾腊,道長(zhǎng),這世上最難降的妖魔是什么挖胃? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任杂靶,我火速辦了婚禮梆惯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吗垮。我一直安慰自己垛吗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布烁登。 她就那樣靜靜地躺著怯屉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饵沧。 梳的紋絲不亂的頭發(fā)上锨络,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音狼牺,去河邊找鬼羡儿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛是钥,可吹牛的內(nèi)容都是我干的失受。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼咏瑟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了痪署?” 一聲冷哼從身側(cè)響起码泞,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狼犯,沒想到半個(gè)月后余寥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悯森,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年宋舷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓢姻。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祝蝠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出幻碱,到底是詐尸還是另有隱情绎狭,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布褥傍,位于F島的核電站儡嘶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏恍风。R本人自食惡果不足惜蹦狂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一誓篱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凯楔,春花似錦窜骄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至鸥拧,卻和暖如春党远,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背富弦。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工沟娱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腕柜。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓济似,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親盏缤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砰蠢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容