Kotlin學(xué)習(xí)(十二): 函數(shù)梢褐、高級函數(shù)和Lambda表達(dá)式

Kotlin

高階函數(shù),又稱算子(運(yùn)算符)或泛函赵讯,包含多于一個(gè)箭頭的函數(shù)盈咳,高階函數(shù)是至少滿足下列一個(gè)條件的函數(shù):1.接受一個(gè)或多個(gè)函數(shù)作為輸入,2.輸出一個(gè)函數(shù)边翼。
在無類型Lambda 演算鱼响,所有函數(shù)都是高階的;在有類型Lambda 演算(大多數(shù)函數(shù)式編程語言都從中演化而來)中组底,高階函數(shù)一般是那些函數(shù)型別包含多于一個(gè)箭頭的函數(shù)丈积。在函數(shù)式編程中,返回另一個(gè)函數(shù)的高階函數(shù)被稱為Curry化的函數(shù)债鸡。
在很多函數(shù)式編程語言中能找到的 map 函數(shù)是高階函數(shù)的一個(gè)例子江滨。它接受一個(gè)函數(shù) f 作為參數(shù),并返回接受一個(gè)列表并應(yīng)用 f 到它的每個(gè)元素的一個(gè)函數(shù)厌均。

函數(shù)(Functions)

Kotlin使用函數(shù)用fun表示

fun double(x: Int): Int {

}

使用:

// 一般調(diào)用
val result = double(2)
// 使用.調(diào)用
Sample().foo() // 創(chuàng)建Sample類的實(shí)例,調(diào)用foo方法

參數(shù)(Parameters)和默認(rèn)參數(shù)(Default Arguments)

參數(shù)的定義與變量的定義一樣唬滑,使用name: type,該類型稱為Pascal表達(dá)式,每個(gè)參數(shù)必須有顯示類型(手動(dòng)設(shè)置類型)晶密,默認(rèn)參數(shù)是后面加個(gè)=號(hào)擒悬。

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size()) {
      ...
}

重寫方法的時(shí)候是可以把默認(rèn)參數(shù)給替換掉的。

命名參數(shù)

可以在調(diào)用函數(shù)時(shí)使用命名的函數(shù)參數(shù)惹挟。當(dāng)一個(gè)函數(shù)有大量的參數(shù)或默認(rèn)參數(shù)時(shí)這非常方便茄螃。
如:

fun reformat(str: String, normalizeCase: Boolean = true, upperCaseFirstLetter: Boolean = true, divideByCamelHumps: Boolean = false, wordSeparator: Char = ' ') {
    println("str: $str, normalizeCase: Boolean = $normalizeCase, upperCaseFirstLetter: Boolean = $upperCaseFirstLetter, divideByCamelHumps: Boolean = $divideByCamelHumps, wordSeparator: Char = $wordSeparator")
}

調(diào)用默認(rèn)參數(shù)的時(shí)候,是這樣寫的:

reformat(str)

如果把最后一個(gè)參數(shù)不設(shè)置默認(rèn)參數(shù)连锯,那在調(diào)用的時(shí)候

reformat(str, true, true, false, '_')

這樣子就會(huì)很麻煩归苍,每次都要把那些默認(rèn)參數(shù)給寫上,在這里就可以使用命名參數(shù)了:

reformat(str, wordSeparator = '_') // 與reformat(str, true, true, false, '_')是一樣的

注意运怖,在Java中是不能使用命名參數(shù)的

中綴符號(hào)(Infix notation)

中綴表達(dá)式是操作符以中綴形式處于操作數(shù)的中間(例:3 + 4)拼弃,先來看一下Kotlin中的中綴函數(shù):

mapOf()方法中的to就是個(gè)中綴函數(shù):

val map: Map<Int, Int> = mapOf(1 to 1, 2 to 2)

Range里面的downTo也是個(gè)中綴函數(shù):

(10 downTo 1).forEach { print(it) } // 10987654321

使用中綴符號(hào)infix可以調(diào)用函數(shù),但必須符合一些條件:

  • 必須是成員方法或者擴(kuò)展函數(shù)
  • 函數(shù)只有一個(gè)參數(shù)
  • 使用infix關(guān)鍵字表示

下面來寫個(gè)中綴函數(shù):

// 定義擴(kuò)展函數(shù)
infix fun Int.iInfix(x: Int): Int  = this + x

fun main(args: Array<String>) {
    // 用中綴符號(hào)表示的擴(kuò)展函數(shù)使用
    println("2 iInfix 1:${2 iInfix 1}") // 打右≌埂:2 iInfix 1:3
    // 與下面是相同的
    println("2.iInfix(1):${2.iInfix(1)}") // 打游茄酢:2.iInfix(1):3
}

我們來看看編譯的代碼:


返回Unit的函數(shù)(Unit-returning functions)

如果一個(gè)函數(shù)不返回值,即Java中的void咏连,默認(rèn)的返回類型就是Unit盯孙,默認(rèn)不顯示:

fun printHello(name: String?): Unit { // Unit可以不顯示
    if (name != null)
        println("Hello ${name}")
    else
        println("Hi there!")
    // return Unit和 return是可選的
}

單表達(dá)式函數(shù)(Single-Expression functions)

如果一個(gè)函數(shù)只有一個(gè)并且是表達(dá)式函數(shù)體并且是返回類型自動(dòng)推斷的話,這樣的函數(shù)叫做當(dāng)單表達(dá)式函數(shù)祟滴,這個(gè)在前面也有說過:

fun double(x: Int): Int = x * 2
fun double(x: Int) = x * 2 // 這兩個(gè)是一樣的

可變參數(shù)(Variable number of arguments (Varargs))

在Java中使用可變參數(shù)是這樣寫的:

private void getStr(String... params) {
      ...
}

而在Kotlin中使用可變參數(shù)(通常是最后一個(gè)參數(shù))的話振惰,是用vararg關(guān)鍵字修飾的:

fun <T> asList(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // 在這里ts的類型是數(shù)組
        result.add(t)
    return result
}

使用的時(shí)候與Java一樣:

val list = asList(1, 2, 3)

當(dāng)我們調(diào)用vararg函數(shù),不僅可以接收可以一個(gè)接一個(gè)傳遞參數(shù)垄懂,例如asList(1, 2, 3)骑晶,也可以將一個(gè)數(shù)組傳遞進(jìn)去,在數(shù)組變量前面加spread操作符草慧,就是*號(hào):

val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4) // 表示(-1, 0, 1, 2, 3, 4)

函數(shù)使用范圍(Function Scope)

在前面說過Top-level桶蛔,函數(shù)使用在與類同一級聲明的,不需要再重新創(chuàng)建一個(gè)類來持有一個(gè)函數(shù)漫谷,稱為Top-level函數(shù)徽级。Kotlin中的函數(shù)除了Top-level函數(shù)外矗积,還有局部函數(shù)、成員函數(shù)和前面說過的擴(kuò)展函數(shù)。

局部函數(shù)

Kotlin的局部函數(shù)是指一個(gè)函數(shù)在另一個(gè)函數(shù)中:

fun dfs(graph: Graph) {
    fun dfs(current: Vertex, visited: Set<Vertex>) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

    dfs(graph.vertices[0], HashSet())
}

局部函數(shù)可以訪問外部函數(shù)的局部變量(即閉包)售碳,所以在上面的例子换团,visited可以是局部變量:

fun dfs(graph: Graph) {
    val visited = HashSet<Vertex>()
    fun dfs(current: Vertex) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v)
    }

    dfs(graph.vertices[0])
}

成員函數(shù)(Member Functions)

成員函數(shù)是定義在一個(gè)類或?qū)ο罄铮?/p>

class Sample() {
    fun foo() { print("Foo") }
}

調(diào)用:

Sample().foo()

尾遞歸函數(shù)(Tail recursive functions)

Kotlin支持稱為尾遞歸的函數(shù)式編程風(fēng)格眠砾,允許使用循環(huán)寫入的一些算法而不是使用遞歸函數(shù)寫入疮装,同時(shí)沒有堆棧溢出的風(fēng)險(xiǎn)。
函數(shù)用tailrec修飾符標(biāo)記并滿足所需的形式時(shí)缩宜,編譯器優(yōu)化遞歸肘迎,快速和高效循環(huán)甥温。
下面用個(gè)遞歸函數(shù)來獲取余弦的不動(dòng)點(diǎn)(一個(gè)數(shù)學(xué)常數(shù)0.7390851332151607)在Java中使用遞歸是這樣寫的:

private double findFixPoint(double x = 1.0) {
    if (x == Math.cos(x)) {
        return x;
    } else {
        findFixPoint(Math.cos(x));
    }
}

改成Kotlin代碼的話是這樣的:

private fun findFixPoint(): Double {
    var x = 1.0
    while (true) {
        val y = Math.cos(x)
        if (x == y) return y
        x = y
    }
}

改成尾遞歸的話:


tailrec

來看看編譯后是什么樣的:


要符合tailrec的條件的話,函數(shù)必須將其自身調(diào)用作為它執(zhí)行的最后一個(gè)操作妓布。如果在遞歸調(diào)用后有更多代碼時(shí)姻蚓,不能使用尾遞歸,并且不能用在 try/catch/finally 塊中匣沼。目前尾部遞歸只在 JVM 后端中支持狰挡。

高級函數(shù)與Lambdas表達(dá)式

高級函數(shù)是將函數(shù)作為參數(shù)或返回一個(gè)函數(shù),稱為高階函數(shù)释涛。如lock()函數(shù):

fun <T> lock(lock: Lock, body: () -> T): T {
   lock.lock()
   try {
       return body()
   }
   finally {
       lock.unlock()
   }
}

lock函數(shù)的參數(shù)body是函數(shù)類型()->T加叁,該body函數(shù)是一個(gè)沒有參數(shù),返回類型為T的函數(shù)唇撬。
調(diào)用的時(shí)候可以使用函數(shù)引用(::):

fun toBeSynchronized() = sharedResource.operation()  
  
val result = lock(lock, ::toBeSynchronized)  

傳遞Lambdas調(diào)用:

val result = lock(lock, { sharedResource.operation() })

在Kotlin中它匕,若函數(shù)最后一個(gè)參數(shù)為函數(shù)類型,調(diào)用時(shí)窖认,該參數(shù)可以放到函數(shù)的外面:

lock (lock) {
       sharedResource.operation()
}

另一個(gè)例子是map()

fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
    val result = arrayListOf<R>()
    for (item in this)
        result.add(transform(item))
    return result
}

it:單個(gè)參數(shù)的隱式名稱

若函數(shù)參數(shù)對應(yīng)的函數(shù)只有一個(gè)參數(shù)豫柬,在使用時(shí),可以省略參數(shù)定義(連同->)扑浸,直接使用it代替參數(shù):

val doubled = ints.map { it -> it * 2 }
ints.filter { it > 0 } // it表示 '(it: Int) -> Boolean'

這種方式可以寫成LINQ-style代碼:

strings.filter { it.length == 5 }
.sortBy { it }
.map { it.toUpperCase() }

函數(shù)引用(Function References)

函數(shù)可以作為參數(shù)使用烧给,當(dāng)把一個(gè)函數(shù)當(dāng)作一個(gè)值傳遞的時(shí)候,可以使用::操作符喝噪,將函數(shù)引用:

fun isOdd(x: Int) = x % 2 != 0

val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // 輸出 [1, 3]

::isOdd是函數(shù)類型(Int) -> Boolean的一個(gè)值础嫡。

當(dāng)上下文可以推導(dǎo)出函數(shù)的類型時(shí),::用于重載函數(shù):

fun isOdd(x: Int) = x % 2 != 0
fun isOdd(s: String) = s == "brillig" || s == "slithy" || s == "tove"

val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // 引用到 isOdd(x: Int)

也可以直接指定類型:

val predicate: (String) -> Boolean = ::isOdd   // 引用到 isOdd(x: String)

如果有一個(gè)函數(shù)有兩個(gè)函數(shù)參數(shù)仙逻,返回的類型也是一個(gè)函數(shù)類型

fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
    return { x -> f(g(x)) }
}

那么在引用的時(shí)候驰吓,會(huì)是怎么樣的呢涧尿?

fun isOdd(x: Int) = x % 2 != 0 // (Int) -> Boolean

fun length(s: String) = s.length // (String) -> Int

fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
    return { x -> f(g(x)) }
}

val oddLength = compose(::isOdd, ::length) // oddLength的類型是(String) -> Boolean

下劃線表示未使用的變量(1.1版開始)

如果Lambda中有參數(shù)未使用系奉,可以使用下劃線代替參數(shù)名:

    val map = mapOf(1 to 1, 2 to 2, 3 to 3)
    for ((key, value) in map) {
        print("$value!") // 打印1!2!3!
    }
    for ((_, value2) in map) {
        print("$value2!") // 打印1!2!3!
    }

我們來看看編譯后的區(qū)別

decompiled

可以看出,加了改成下劃線后姑廉,不會(huì)去獲取var2.getKey()).intValue()的值缺亮。

Lambda表達(dá)式和匿名函數(shù)(Lambda Expressions and Anonymous Functions)

“Lambda 表達(dá)式”(lambda expression)是一個(gè)匿名函數(shù),Lambda表達(dá)式基于數(shù)學(xué)中的λ演算得名桥言,直接對應(yīng)于其中的lambda抽象(lambda abstraction)萌踱,是一個(gè)匿名函數(shù),即沒有函數(shù)名的函數(shù)号阿。Lambda表達(dá)式可以表示閉包(注意和數(shù)學(xué)傳統(tǒng)意義上的不同)并鸵。——來自百度百科

max(strings, { a, b -> a.length < b.length })

可以看出max是一個(gè)高階函數(shù)扔涧,第二個(gè)參數(shù)是一個(gè)函數(shù)類型园担,等同于下面的函數(shù):

fun compare(a: String, b: String): Boolean = a.length < b.length

函數(shù)類型

對于函數(shù)接受另一個(gè)函數(shù)作為參數(shù)届谈,我們必須為該參數(shù)指定函數(shù)類型。
如上面的max的定義:

fun <T> max(collection: Collection<T>, less: (T, T) -> Boolean): T? {
    var max: T? = null
    for (it in collection)
        if (max == null || less(max, it))
            max = it
    return max
}

可以看出第二個(gè)參數(shù)less的類型是(T, T) -> Boolean弯汰,即以TT為參數(shù)艰山,返回值為Boolean類型的函數(shù)。
如果要定義每個(gè)參數(shù)的變量名咏闪,可以這樣寫:

val compare: (x: T, y: T) -> Int = ...

語法

一個(gè)Lambda表達(dá)式通常使用{ }包圍曙搬,參數(shù)是定義在()內(nèi),可以添加類型注解鸽嫂,實(shí)體部分跟在“->”后面纵装;如果Lambda的推斷返回類型不是Unit,那么Lambda主體中的最后一個(gè)(或單個(gè))表達(dá)式將被視為返回值据某。
一個(gè)最普通的Lambda表達(dá):

val sum: (Int, Int) -> Int = { x, y -> x + y }  

使用return標(biāo)簽時(shí)搂擦,可以隱式返回最后一個(gè)表達(dá)式的值:

// 下面兩個(gè)是等效的
ints.filter {
    val shouldFilter = it > 0 
    shouldFilter
}

ints.filter {
    val shouldFilter = it > 0 
    return@filter shouldFilter
}

如果函數(shù)接受另一個(gè)函數(shù)作為最后一個(gè)參數(shù),那么lambda表達(dá)式參數(shù)可以在()參數(shù)列表外部傳遞哗脖。

// 下面兩個(gè)是等效的
lock(lock, { sharedResource.operation() })
lock (lock) {
    sharedResource.operation()
}

匿名函數(shù)(Anonymous Functions)

Lambda表示在定義時(shí)瀑踢,可以明確定義返回值類型;但是在大部分情況下才避,沒有必要明確定義的橱夭,因?yàn)榉祷刂殿愋投伎梢宰詣?dòng)推斷出。
如果需要明確定義返回值類型桑逝,可以使用匿名函數(shù)代替:

fun(x: Int, y: Int): Int = x + y

匿名函數(shù)除了省略了函數(shù)名稱棘劣,其他跟一般函數(shù)的定義基本類似,函數(shù)體可以是一個(gè)表達(dá)式或一個(gè)代碼塊:

fun(x: Int, y: Int): Int {
    return x + y
}

若參數(shù)類型可以通過上下文推斷出來楞遏,也可以省略:

ints.filter(fun(item) = item > 0)

匿名函數(shù)的返回類型跟一般函數(shù)一樣:對應(yīng)只有一行執(zhí)行代碼的函數(shù)茬暇,編譯器可以自動(dòng)推斷出來返回類型,可以省略寡喝;對應(yīng)多方代碼塊的函數(shù)糙俗,需要顯示定義返回值類型(為Unit可以省略)。

Lambdas與匿名函數(shù)的區(qū)別

  • 匿名函數(shù)作為參數(shù)预鬓,一般定義在()中巧骚;而Lambda表達(dá)式可以定義到調(diào)用函數(shù)()外。
  • 另外區(qū)別在非局部返回(non-local returns)行為上:非標(biāo)簽注解的return(返回對應(yīng)的最內(nèi)層的函數(shù)(即fun)格二,在匿名函數(shù)中劈彪,退出該匿名函數(shù);而在Lambda表達(dá)中顶猜,退出包含該表達(dá)式的函數(shù)沧奴。

下面舉個(gè)例子來區(qū)分


    fun lambdaReturn() {
        val list = asList(1, 2, 3, 4)
        loge("test", list.toString())
        val lambdaList = list.map {
            it * 2
            return
        }
        loge("test", lambdaList.toString())
    }

    fun anonymousReturn() {
        val list = asList(1, 2, 3, 4)
        loge("test", list.toString())
        val lambdaList = list.map(fun(it: Int): Int {
            return it * 2
        })
        loge("test", lambdaList.toString())
    }

    fun <T> asList(vararg ts: T): List<T> {
        val result = ArrayList<T>()
        for (t in ts) // 可變參數(shù)ts是數(shù)組
            result.add(t)
        return result
    }

在調(diào)用lambdaReturn()函數(shù)的時(shí)候會(huì)打印:

lambdaReturn

而在調(diào)用anonymousReturn()函數(shù)的時(shí)候會(huì)打映ふ:

anonymousReturn

這里也就證明了滔吠,在Lambdas表達(dá)式中return會(huì)返回到外層的函數(shù)中远寸,而在匿名函數(shù)中return會(huì)返回的匿名函數(shù)的外層函數(shù)中。

閉包(Closures)

閉包是指可以包含自由(未綁定到特定對象)變量的代碼塊屠凶;這些變量不是在這個(gè)代碼塊內(nèi)或者任何全局上下文中定義的驰后,而是在定義代碼塊的環(huán)境中定義(局部變量)〈@ⅲ“閉包” 一詞來源于以下兩者的結(jié)合:要執(zhí)行的代碼塊(由于自由變量被包含在代碼塊中灶芝,這些自由變量以及它們引用的對象沒有被釋放)和為自由變量提供綁定的計(jì)算環(huán)境(作用域)。
——來自百度百科

Lambda表達(dá)式及匿名函數(shù)(以及局部函數(shù)唉韭,對象表達(dá)式)可以訪問包含它的外部范圍定義的變量(Java中只能是常量夜涕,在Kotlin中可以是變量):

var sum = 0
ints.filter {
    it > 0
}.forEach {
    sum += it
}
print(sum)

事實(shí)上函數(shù)、Lambda属愤、if語句女器、for循環(huán)、when語句等都是閉包住诸,但通常情況下驾胆,我們所說的閉包是 Lambda 表達(dá)式。
閉包可以在定義的時(shí)候直接執(zhí)行閉包操作贱呐,這種閉包一般用在初始化操作上:

{ x: Int, y: Int, z: String ->
    println("${x + y}_ $z")
}(4, 5, "test")

像我們寫構(gòu)造函數(shù)的時(shí)候丧诺,主構(gòu)造函數(shù)不包含任何代碼,初始化代碼必須寫在init代碼塊中奄薇,而init的代碼塊就是閉包:

init

屬性里面的setter也是閉包:

setter

在build.gradle里面都是閉包:

build.gradle

帶接收者的函數(shù)字面值(Function Literals with Receiver)

Kotlin提供了使用指定的接收者對象調(diào)用文本函數(shù)的功能驳阎,這就是文本擴(kuò)展函數(shù)。在文本函數(shù)中馁蒂,可以調(diào)用接收者對象上的方法呵晚。類似于擴(kuò)展函數(shù),可以調(diào)用方法內(nèi)的接收者對象的成員

val sum : Int.(other: Int) -> Int = { it + 1 }

sum的類型是Int.(one: Int) -> Int沫屡,傳入一個(gè)Int類型的值饵隙,返回Int類型,在閉包里面返回值谁鳍。

調(diào)用的時(shí)候與擴(kuò)展函數(shù)一樣:

val a = 1.sum(2) // a的值為3

如果用個(gè)匿名函數(shù)的話癞季,可以直接指定函數(shù)文本的接收者類型:

val sums = fun Int.(other: Int): Int {
    println(this)
    println(this + other)
    return this + other
}

調(diào)用

10.sums(10) 

打印結(jié)果


上面兩個(gè)的sumsums的區(qū)別:

sum
sums

Lambda表達(dá)式:

class HTML {
    fun body(one: Int) {
        println("body$one")
    }
}

fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()  // 創(chuàng)建接收器對象
    html.init()        // 將接收器對象傳遞給lambda
    // 等同于init(html)
    return html
}

使用的時(shí)候劫瞳,可以先聲明一個(gè)HTML.() -> Unit類型倘潜,然后調(diào)用html方法

val init: HTML.() -> Unit = {
    body(1)
}
// 因?yàn)閔tml方法返回的是一個(gè)HTML類型,所以可以在后面直接使用body方法
html(init).body(2)  

簡化

html {      
    body()   
}.body()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末志于,一起剝皮案震驚了整個(gè)濱河市涮因,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伺绽,老刑警劉巖养泡,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗜湃,死亡現(xiàn)場離奇詭異,居然都是意外死亡澜掩,警方通過查閱死者的電腦和手機(jī)购披,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肩榕,“玉大人刚陡,你說我怎么就攤上這事≈旰海” “怎么了筐乳?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乔妈。 經(jīng)常有香客問我蝙云,道長,這世上最難降的妖魔是什么路召? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任勃刨,我火速辦了婚禮,結(jié)果婚禮上股淡,老公的妹妹穿的比我還像新娘朵你。我一直安慰自己,他們只是感情好揣非,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布抡医。 她就那樣靜靜地躺著,像睡著了一般早敬。 火紅的嫁衣襯著肌膚如雪忌傻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天搞监,我揣著相機(jī)與錄音水孩,去河邊找鬼。 笑死琐驴,一個(gè)胖子當(dāng)著我的面吹牛俘种,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绝淡,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宙刘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了牢酵?” 一聲冷哼從身側(cè)響起悬包,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎馍乙,沒想到半個(gè)月后布近,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垫释,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年撑瞧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棵譬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡预伺,死狀恐怖茫船,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扭屁,我是刑警寧澤算谈,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站料滥,受9級特大地震影響然眼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜葵腹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一高每、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧践宴,春花似錦鲸匿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烤惊,卻和暖如春乔煞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柒室。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工渡贾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人雄右。 一個(gè)月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓空骚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親擂仍。 傳聞我的和親對象是個(gè)殘疾皇子囤屹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

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