iOS-Swift-map filter reduce及刻、函數(shù)式編程

一. Array常用的內(nèi)置函數(shù)

1. map

遍歷數(shù)組中的元素镀裤,傳入到后面的閉包里面,閉包的返回值組成新的數(shù)組缴饭,最后返回這個(gè)新數(shù)組暑劝。

var arr = [1, 2, 3, 4]
// [2, 4, 6, 8]
var arr2 = arr.map { $0 * 2 } //重新映射

也可以傳入一個(gè)函數(shù),如下:

func double(_ i: Int) -> Int { i * 2 }
var arr = [1, 2, 3, 4]
// [2, 4, 6, 8]
print(arr.map(double))

① map和flatMap(緊貼颗搂、平坦担猛、展開)的區(qū)別

map:無論你給我返回的是什么我都把返回的東西放到新數(shù)組里面。
flatMap:如果你給我返回的是數(shù)組,我會(huì)把數(shù)組攤開傅联,將數(shù)組里面的元素放到新數(shù)組里面智嚷。

var arr = [1, 2, 3]
// [[1], [2, 2], [3, 3, 3]]
var arr2 = arr.map { Array.init(repeating: $0, count: $0) }
// [1, 2, 2, 3, 3, 3]
var arr3 = arr.flatMap { Array.init(repeating: $0, count: $0) }

② compactMap(壓縮、結(jié)實(shí))

compactMap會(huì)將新數(shù)組中的元素解包纺且,如果是nil就清除這個(gè)元素盏道,最后返回新數(shù)組。

var arr = ["123", "test", "jack", "-30"]
// [Optional(123), nil, nil, Optional(-30)]
var arr2 = arr.map { Int($0) }
// [123, -30]
var arr3 = arr.compactMap { Int($0) } //壓縮

③ map的lazy的優(yōu)化

let arr = [1, 2, 3]
let result = arr.lazy.map { //有l(wèi)azy
    (i: Int) -> Int in
    print("mapping \(i)")
    return i * 2
}
print("begin-----")
print("mapped", result[0])
print("mapped", result[1])
print("mapped", result[2])
print("end----")

如果沒有l(wèi)azy载碌,打印如下猜嘱,可以發(fā)現(xiàn),在begin之前嫁艇,arr中的元素已經(jīng)映射完了朗伶,但是有時(shí)候這樣比較浪費(fèi)和耗時(shí)。

mapping 1
mapping 2
mapping 3
begin-----
mapped 2
mapped 4
mapped 6
end----

如果有l(wèi)azy步咪,打印如下论皆,用到哪個(gè)元素才映射哪個(gè)元素。

begin-----
mapping 1
mapped 2
mapping 2
mapped 4
mapping 3
mapped 6
end----

2. filter

遍歷數(shù)組中的元素猾漫,傳入到后面的閉包里面点晴,閉包的返回值如果是true,就把這個(gè)元素放到新數(shù)組里面悯周,閉包的返回值如果是false粒督,繼續(xù)遍歷數(shù)組中元素,最后返回這個(gè)新數(shù)組禽翼。

var arr = [1, 2, 3, 4]
// [2, 4]
var arr2 = arr.filter { $0 % 2 == 0 } //過濾

3. reduce

這個(gè)函數(shù)比較復(fù)雜屠橄,查看reduce函數(shù)定義如下:

@inlinable public func reduce<Result>(_ initialResult: Result,
                                      _ nextPartialResult: (Result, Self.Element) throws -> Result) rethrows -> Result

可以發(fā)現(xiàn),reduce函數(shù)有兩個(gè)參數(shù)闰挡,第一個(gè)參數(shù)是Result锐墙,第二個(gè)參數(shù)是個(gè)閉包,閉包第一個(gè)參數(shù)是Result长酗,第二個(gè)參數(shù)是數(shù)組中的元素溪北。

如下,如果arr.reduce(0)花枫,最后算出的就是整個(gè)數(shù)組元素的和:

var arr = [1, 2, 3, 4]
var arr2 = arr.reduce(0) {
    (result, element) -> Int in
    return result + element;
} //尾隨閉包
print(arr2) //0 + 1 + 2 + 3 + 4 = 10

上面代碼也可以簡(jiǎn)寫如下:

var arr = [1, 2, 3, 4]
// 10
var arr2 = arr.reduce(0) { $0 + $1 } //尾隨閉包
// 10
var arr2 = arr.reduce(0, +)

擴(kuò)展:使用reduce實(shí)現(xiàn)map刻盐、filter的功能

var arr = [1, 2, 3, 4]
//[2, 4, 6, 8]
print(arr.map { $0 * 2 })
print(arr.reduce([]) { $0 + [$1 * 2] }) //和上面map一樣
//[2, 4]
print(arr.filter { $0 % 2 == 0 })
print(arr.reduce([]) { $1 % 2 == 0 ? $0 + [$1] : $0 }) //和上面filter一樣

二. 可選類型的map和flatMap

可選類型也有map和flatMap,可選類型的map和flatMap就是將.map前?的參數(shù)解包(就是$0)劳翰,解包成功后的值傳給后?的函數(shù)敦锌,解包失敗直接返回nil。

var num1: Int? = 10
// Optional(20)
var num2 = num1.map { $0 * 2 }

var num3: Int? = nil
// nil
var num4 = num3.map { $0 * 2 }

1. 區(qū)別

可選類型的map和flatMap佳簸,如果解包成功乙墙,map會(huì)將解包后的值包裝成可選項(xiàng)颖变,flatMap不會(huì)。

var num1: Int? = 10
// Optional(Optional(20))
var num2 = num1.map { Optional.some($0 * 2) }
// Optional(20)
var num3 = num1.flatMap { Optional.some($0 * 2) }

下面num2听想、num3是等價(jià)的

var num1: Int? = 10
var num2 = (num1 != nil) ? (num1! + 10) : nil
var num3 = num1.map { $0 + 10 }
// num2腥刹、num3是等價(jià)的

2. 實(shí)際使用

可選類型使用flatMap可以減少一些判斷是否為nil再執(zhí)行三目運(yùn)算的操作。

var fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
var str: String? = "2011-09-10"
// old
var date1 = str != nil ? fmt.date(from: str!) : nil
// new  用flatMap汉买,如果返回的是可選類型就不會(huì)再包裝一層
var date2 = str.flatMap{fmt.date(from: $0)}
var date3 = str.flatMap{fmt.date}  //更簡(jiǎn)潔
var score: Int? = 98
// old
var str1 = score != nil ? "socre is \(score!)" : "No score"
// new  如果不為nil就將$0包裝成可選字符串衔峰,如果為nil就返回"No score"
var str2 = score.map { "score is \($0)" } ?? "No score"
struct Person {
    var name: String
    var age: Int
}
var items = [
     Person(name: "jack", age: 20),
     Person(name: "rose", age: 21),
     Person(name: "kate", age: 22)
]
// old
func getPerson1(_ name: String) -> Person? {
    let index = items.firstIndex { $0.name == name }
    return index != nil ? items[index!] : nil
}
// new  索引如果有值就會(huì)調(diào)?.map,索引沒值就直接返回nil
func getPerson2(_ name: String) -> Person? {
    //第一個(gè)$0代表items中的元素蛙粘,第二個(gè)$0代表index
    return items.firstIndex { $0.name == name}.map { items[$0] }
}
struct Person {
    var name: String
    var age: Int
    init?(_ json: [String : Any]) {
        guard let name = json["name"] as? String,let age = json["age"] as? Int
        else {
            return nil
        }
        self.name = name
        self.age = age
    }
}
var json: Dictionary? = ["name" : "Jack", "age" : 10]
// old
var p1 = json != nil ? Person(json!) : nil
// new
var p2 = json.flatMap(Person.init)

三. 函數(shù)式編程(Funtional Programming)

函數(shù)式編程(Funtional Programming垫卤,簡(jiǎn)稱FP)是一種編程范式,也就是如何編寫程序的方法論

主要思想:把計(jì)算過程盡量分解成一系列可復(fù)用函數(shù)的調(diào)用
主要特征:函數(shù)是“第一等公民”出牧,函數(shù)與其他數(shù)據(jù)類型一樣的地位穴肘,可以賦值給其他變量,也可以作為函數(shù)參數(shù)舔痕、函數(shù)返回值

函數(shù)式編程最早出現(xiàn)在LISP語言评抚,絕大部分的現(xiàn)代編程語言也對(duì)函數(shù)式編程做了不同程度的支持,比如:
Haskell伯复、JavaScript慨代、Python、Swift边翼、Kotlin鱼响、Scala等

函數(shù)式編程中幾個(gè)常用的概念:
Higher-Order Function、Function Currying
Functor组底、Applicative Functor、Monad

參考資料:
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
http://www.mokacoding.com/blog/functor-applicative-monads-in-pictures

1. FP實(shí)踐 – 傳統(tǒng)寫法

假設(shè)要實(shí)現(xiàn)以下功能:[(num + 3) * 5 - 1] % 10 / 2

var num = 1

func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
func sub(_ v1: Int, _ v2: Int) -> Int { v1 - v2 }
func multiple(_ v1: Int, _ v2: Int) -> Int { v1 * v2 }
func divide(_ v1: Int, _ v2: Int) -> Int { v1 / v2 }
func mod(_ v1: Int, _ v2: Int) -> Int { v1 % v2 }

divide(mod(sub(multiple(add(num, 3), 5), 1), 10), 2) //4

2. FP實(shí)踐 – 函數(shù)式寫法

傳統(tǒng)寫法嵌套比較麻煩筐骇,下面將上面的函數(shù)柯里化(將一個(gè)接受多參數(shù)的函數(shù)變換為一系列只接受單個(gè)參數(shù)的函數(shù))

下面是柯里化后的函數(shù)债鸡,只接收一個(gè)參數(shù),返回一個(gè)函數(shù)

func add(_ v: Int) -> (Int) -> Int { { $0 + v } } //加
func sub(_ v: Int) -> (Int) -> Int { { $0 - v } } //減
func multiple(_ v: Int) -> (Int) -> Int { { $0 * v } } //乘
func divide(_ v: Int) -> (Int) -> Int { { $0 / v } } //除
func mod(_ v: Int) -> (Int) -> Int { { $0 % v } } //取余

let fn1 = add(3) //加3
let fn2 = multiple(6) //乘6
let fn3 = sub(1) //減1
let fn4 = mod(10) //對(duì)10取余
let fn5 = divide(2) //除以2

//num -> fn1 -> fn2 -> fn3 -> fn4 -> fn5
print(fn5(fn4(fn3(fn2(fn1(num)))))) //4

這樣寫也是麻煩铛纬,所以?定義運(yùn)算符厌均,把A->B和B->C的函數(shù)合成?個(gè)函數(shù),??只有?個(gè)A->C

//函數(shù)合成
infix operator >>> : AdditionPrecedence //優(yōu)先級(jí)和加法一樣
func >>><A, B, C>(_ f1: @escaping (A) -> B,
                  _ f2: @escaping (B) -> C) -> (A) -> C {
    { f2(f1($0)) } //$0就是num
}

var fn = add(3) >>> multiple(5) >>> sub(1) >>> mod(10) >>> divide(2)
fn(num)

這樣就通過函數(shù)式思想實(shí)現(xiàn)了:[(num + 3) * 5 - 1] % 10 / 2告唆,可以發(fā)現(xiàn)對(duì)比傳統(tǒng)寫法簡(jiǎn)潔了很多棺弊。

3. 高階函數(shù)(Higher-Order Function)

高階函數(shù)是至少滿足下列一個(gè)條件的函數(shù):

  1. 接受一個(gè)或多個(gè)函數(shù)作為輸入(map、filter擒悬、reduce等)
  2. 返回一個(gè)函數(shù)

FP中到處都是高階函數(shù)模她,如下:

func add(_ v: Int) -> (Int) -> Int { { $0 + v } }

4. 柯里化(Currying)

什么是柯里化?
將一個(gè)接受多參數(shù)的函數(shù)變換為一系列只接受單個(gè)參數(shù)的函數(shù)

兩個(gè)數(shù)相加函數(shù):

func add1(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
add(20, 10)

柯里化之后懂牧,如下:

func add1(_ v: Int) -> (Int) -> Int { { $0 + v } }
add(20)(10) //10 + 20 = 30

Tip:Array侈净、Optional的map方法接收的參數(shù)就是一個(gè)柯里化函數(shù)(只接收?個(gè)參數(shù),返回?個(gè)東?)

三個(gè)數(shù)相加函數(shù)的柯?化如下:

//傳統(tǒng)的
func add2(_ v1: Int, _ v2: Int, _ v3: Int) -> Int { v1 + v2 + v3 }

//柯?化
//v3 == 30
func add2(_ v3: Int) -> (Int) -> (Int) -> (Int) {
    //v2 == 20
    return { v2 in
        //v1 == 10
        return { v1 in
            return v3 + v2 + v1
        }
    }
} //最?層在做事情,外層在不斷減少參數(shù)

add2(30)(20)(10) //10 + 20 + 30 = 60
  • ?動(dòng)將函數(shù)柯?化

上面的add1畜侦、add2柯?化版本都是我們手動(dòng)敲出來的元扔,下面看一下如何?動(dòng)將函數(shù)柯?化。

func add1(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
func add2(_ v1: Int, _ v2: Int, _ v3: Int) -> Int { v1 + v2 + v3 }

func currying<A, B, C>(_ fn: @escaping (A, B) -> C) -> (B) -> (A) -> C {
    { b in { a in fn(a, b) } }
}
func currying<A, B, C, D>(_ fn: @escaping (A, B, C) -> D) -> (C) -> (B) -> (A) -> D {
    { c in { b in { a in fn(a, b, c) } } }
}

let curriedAdd1 = currying(add1)
print(curriedAdd1(20)(10)) //10 + 20 = 30  20傳給b旋膳,10傳給a
let curriedAdd2 = currying(add2)
print(curriedAdd2(30)(20)(10)) //10 + 20 + 30 = 60  30傳給c澎语,20傳給b,10傳給a

我們也可以自定義運(yùn)算符验懊,將上面我們舉的例子的函數(shù)自動(dòng)柯?化:

func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
func sub(_ v1: Int, _ v2: Int) -> Int { v1 - v2 }
func multiple(_ v1: Int, _ v2: Int) -> Int { v1 * v2 }
func divide(_ v1: Int, _ v2: Int) -> Int { v1 / v2 }
func mod(_ v1: Int, _ v2: Int) -> Int { v1 % v2 }

//重載~運(yùn)算符咏连,將函數(shù)柯?化
prefix func ~<A, B, C>(_ fn: @escaping (A, B) -> C) -> (B) -> (A) -> C {
    { b in { a in fn(a, b) } }
}

//?定義>>>運(yùn)算符
infix operator >>> : AdditionPrecedence
func >>><A, B, C>(_ f1: @escaping (A) -> B,
                  _ f2: @escaping (B) -> C) -> (A) -> C {
    { f2(f1($0)) }
}

var num = 1
var fn = (~add)(3) >>> (~multiple)(5) >>> (~sub)(1) >>> (~mod)(10) >>> (~divide)(2)
fn(num) //[(num + 3) * 5 - 1] % 10 / 2

5. 函子(Functor)

① 什么是函子

怎么樣的Type才能稱之為函子(Functor)?如下:

func map<T>(_ fn: (Inner) -> T) -> Type<T>

支持如上map運(yùn)算的類型才能稱之為函子鲁森,可以發(fā)現(xiàn)有3個(gè)條件:

  1. 這個(gè)map運(yùn)算要支持泛型
  2. 要求接收一個(gè)函數(shù)祟滴,這個(gè)函數(shù)把Type內(nèi)部存放的數(shù)據(jù)當(dāng)作參數(shù)傳進(jìn)去,返回一個(gè)T
  3. 返回的也是同一種Type<T>類型

② Array歌溉、Optional為什么是函子

Array垄懂、Optional也是支持如上map運(yùn)算的類型,所以我們稱之為函子

Array的map定義:

// Array<Element>
public func map<T>(_ transform: (Element) -> T) -> Array<T>

Optional的map定義:

// Optional<Wrapped>
public func map<U>(_ transform: (Wrapped) -> U) -> Optional<U>

③ 圖解理解函子

如下是個(gè)函子痛垛,函子里面包裝的是2草慧,如果想對(duì)這個(gè)函子做+3的操作,怎么做呢匙头?

函子.png

首先漫谷,將函子解包取出里面的2,再將2做+3的操作蹂析,得到5舔示,最后再將5又放到盒子里面,形成一個(gè)新的函子电抚,如下圖:

map操作.png

Optional的map就是這樣的惕稻,比如:想要對(duì)Optional(2)做+3的操作,首先將Optional(2)里面的2取出來做+3的操作蝙叛,得到5俺祠,最后又將5包裝成可選類型Optional(5),進(jìn)一步說明了Optional類型是函子借帘。

如果這個(gè)可選類型是空的蜘渣,那么map就不會(huì)調(diào)用,那么+3操作就不會(huì)執(zhí)行肺然,如下圖:

盒子為空.png

如果是數(shù)組蔫缸,里面存放的是2 4 6,先將2 4 6取出來分別做相應(yīng)的操作狰挡,最后操作的結(jié)果再包裝成數(shù)組捂龄,如下圖释涛。

數(shù)組.png

6. 適用函子(Applicative Functor)

對(duì)任意一個(gè)函子F,如果能支持以下運(yùn)算倦沧,該函子就是一個(gè)適用函子

func pure<A>(_ value: A) -> F<A> //可以理解為唇撬,隨便給?個(gè)值就能返回??類型的泛型
func <*><A, B>(fn: F<(A) -> B>, value: F<A>) -> F<B> //可以理解為,給?個(gè)泛型F<A>和?個(gè)泛型函數(shù)fn展融,最后返回?個(gè)泛型B

① Optional是適用函子

Optional可以滿足上面兩個(gè)條件窖认,可以成為適用函子,如下:

func pure<A>(_ value: A) -> A? { value } //滿足了上面第一個(gè)條件
infix operator <*> : AdditionPrecedence
func <*><A, B>(fn: ((A) -> B)?, value: A?) -> B? { //滿足了上面第二個(gè)條件
    guard let f = fn, let v = value else { return nil }
    return f(v)
}

使用一下上面的運(yùn)算告希,如下:

var value: Int? = 10
var fn: ((Int) -> Int)? = { $0 * 2}
print(fn <*> value as Any) //Optional(20)

② 圖解適用函子

有操作如下:

操作.png

函子只是將需要計(jì)算的值包裝到里面扑浸,適用函子會(huì)將需要計(jì)算的操作也包裝到里面,如下圖:

適用函子.png

③ Array也是適用函子

Array也可以滿足上面兩個(gè)條件燕偶,也可以成為適用函子喝噪,如下:

func pure<A>(_ value: A) -> [A] { [value] }
func <*><A, B>(fn: [(A) -> B], value: [A]) -> [B] {
    var arr: [B] = []
    if fn.count == value.count {
        for i in fn.startIndex..<fn.endIndex {
            arr.append(fn[i](value[i]))
        }
    }
    return arr
}

使用一下上面的運(yùn)算,如下:

print(pure(10)) // [10]

var arr = [{ $0 * 2}, { $0 + 10 }, { $0 - 5 }] <*> [1, 2, 3]
print(arr) // [2, 12, -2]

傳入?個(gè)數(shù)組A和?個(gè)函數(shù)數(shù)組指么,把數(shù)組A中每?個(gè)元素傳給函數(shù)數(shù)組中對(duì)應(yīng)的函數(shù)酝惧,這個(gè)函數(shù)拿到對(duì)應(yīng)的元素,計(jì)算后放到數(shù)組B里面伯诬,最后返回?cái)?shù)組B晚唇。

7. 單子(Monad)

對(duì)任意一個(gè)類型F,如果能支持以下運(yùn)算盗似,那么就可以稱為是一個(gè)單子(Monad)

func pure<A>(_ value: A) -> F<A>
func flatMap<A, B>(_ value: F<A>, _ fn: (A) -> F<B>) -> F<B>

很顯然哩陕,Array、Optional都是單子

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赫舒,一起剝皮案震驚了整個(gè)濱河市悍及,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌号阿,老刑警劉巖并鸵,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異扔涧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)届谈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門枯夜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人艰山,你說我怎么就攤上這事湖雹。” “怎么了曙搬?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵摔吏,是天一觀的道長(zhǎng)鸽嫂。 經(jīng)常有香客問我,道長(zhǎng)征讲,這世上最難降的妖魔是什么据某? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮诗箍,結(jié)果婚禮上癣籽,老公的妹妹穿的比我還像新娘。我一直安慰自己滤祖,他們只是感情好筷狼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著匠童,像睡著了一般埂材。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汤求,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天俏险,我揣著相機(jī)與錄音,去河邊找鬼首昔。 笑死寡喝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的勒奇。 我是一名探鬼主播预鬓,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼赊颠!你這毒婦竟也來了格二?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤竣蹦,失蹤者是張志新(化名)和其女友劉穎顶猜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痘括,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡长窄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纲菌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挠日。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖翰舌,靈堂內(nèi)的尸體忽然破棺而出嚣潜,到底是詐尸還是另有隱情,我是刑警寧澤椅贱,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布懂算,位于F島的核電站只冻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏计技。R本人自食惡果不足惜喜德,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酸役。 院中可真熱鬧住诸,春花似錦、人聲如沸涣澡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽入桂。三九已至奄薇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抗愁,已是汗流浹背馁蒂。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜘腌,地道東北人沫屡。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像撮珠,于是被迫代替她去往敵國和親沮脖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

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