Kotlin修煉指南(二):lambda表達(dá)式的精髓

(轉(zhuǎn)載)原文鏈接:https://mp.weixin.qq.com/s/Rxw_oUTQv_Tr61-z16YGdw


Kotlin修煉指南(二):lambda表達(dá)式的精髓

lambda表達(dá)式是Kotlin函數(shù)式編程的一個(gè)重要概念,要想掌握函數(shù)式編程,就必須熟練掌握l(shuí)ambda表達(dá)式,并掌握它的各種寫(xiě)法和實(shí)現(xiàn),這些都是掌握函數(shù)式編程的基礎(chǔ)。

lambda基本形式

lambda表達(dá)式有三大特征:

  1. lambda表達(dá)式存在于{}中

  2. 參數(shù)及參數(shù)類型(可省略)在->左邊

  3. 函數(shù)體在->右邊

lambda表達(dá)式返回值總是返回函數(shù)體內(nèi)部最后一行表達(dá)式的值

這三種形式的lambda表達(dá)式必須要能夠非常熟練的掌握,這樣才能進(jìn)一步的了解Kotlin和函數(shù)式編程。

無(wú)參數(shù)

無(wú)參數(shù)形式為:

val 函數(shù)名 = { 函數(shù)體 }

示例:

val hello = { println("hello kotlin") }// 等價(jià)于函數(shù)fun hello() {    println("hello kotlin")}

有參數(shù)

  1. 完整表達(dá)方式:

val 函數(shù)名 : (參數(shù)1類型, 參數(shù)2類型, ...) -> 返回值類型 = { 參數(shù)1, 參數(shù)2, ... -> 函數(shù)體 }

  1. 表達(dá)式返回值類型可自動(dòng)推斷形式

val 函數(shù)名 = { 參數(shù)1:類型1, 參數(shù)2:類型2, ... -> 函數(shù)體 }

示例:

val sum: (Int, Int) -> Int = { a, b -> a + b }// 等價(jià)于val sum = { a: Int, b: Int -> a + b }// 等價(jià)于函數(shù)fun sum(a: Int, b: Int): Int {    return a + b}

只有一個(gè)參數(shù)的時(shí)候,返回值中的參數(shù)形參可以省略,引用時(shí)通過(guò)it進(jìn)行引用

lambda的調(diào)用有兩種方式,一種是通過(guò)()來(lái)進(jìn)行調(diào)用,另一種是通過(guò)invoke()函數(shù)進(jìn)行調(diào)用,兩種方式?jīng)]有區(qū)別。

fun main(args: Array<String>) {    val lambda = { println("test") }    lambda()    lambda.invoke()}

在使用lambda表達(dá)式的時(shí)候,可以用下劃線(_)表示未使用的參數(shù),表示不處理這個(gè)參數(shù)。

匿名函數(shù)

匿名函數(shù)形式為:

val 函數(shù)名 = fun(參數(shù)1:類型1, 參數(shù)2:類型2, ...): 返回值類型 { 函數(shù)體 }

示例:

val sum = fun(a: Int, b: Int): Int {    return a + b}// 等價(jià)于函數(shù)fun sum(a: Int, b: Int): Int {    return a + b}

高階函數(shù)的演變

所謂高階函數(shù),實(shí)際上就是數(shù)學(xué)中的復(fù)合函數(shù)的概念,f(g(x))。

引用函數(shù)

fun cal(a: Int, b: Int, f: (c: Int, d: Int) -> Int): Int {    return f(a, b)}fun sum(a: Int, b: Int): Int {    return a + b}fun main(args: Array<String>) {    val result = cal(2, 3, ::sum)    println("result = $result")    // result = 8}

在cal函數(shù)中的最后一個(gè)參數(shù)是 f: (a: Int, b: Int) -> Int 表示該參數(shù)是一個(gè)函數(shù)引用,函數(shù)體內(nèi)調(diào)用了最后一個(gè)參數(shù)指向的函數(shù)。隨后定義了sum函數(shù),該函數(shù)就是cal函數(shù)的第三個(gè)參數(shù)。

::sum表示sum函數(shù)的引用,cal(2, 3, ::sum)這一句就相當(dāng)于執(zhí)行了sum(2, 3),所以輸出結(jié)果為5。

函數(shù)引用可以進(jìn)一步的簡(jiǎn)化函數(shù)的調(diào)用,類似下面這個(gè)例子:

class Test {    fun doSomething() {        println("test")    }    fun doTest(f: (Test) -> Unit) {        f(this)    }}fun main(args: Array<String>) {    val t = Test()    // 常規(guī)寫(xiě)法 傳入函數(shù)    t.doTest { test -> test.doSomething() }    // 使用引用函數(shù)(Test::doSomething實(shí)際上是對(duì)lambda表達(dá)式{test -> test.doSomething()}的簡(jiǎn)化)    t.doTest(Test::doSomething)}

參數(shù)lambda化

fun cal(a: Int, b: Int, f: (a: Int, b: Int) -> Int): Int {    return f(a, b)}fun main(args: Array<String>) {    val sum = { a: Int, b: Int -> a + b }    val result = cal(2, 3, sum)    println("result = $result")    // result = 5}

利用前面寫(xiě)的方式,將一個(gè)函數(shù)改寫(xiě)為lambda形式,作為參數(shù)直接賦值給cal函數(shù)。

那么更進(jìn)一步,可以省略這個(gè)lambda的變量,直接將lambda表達(dá)式傳入函數(shù)。

fun cal(a: Int, b: Int, f: (a: Int, b: Int) -> Int): Int {    return f(a, b)}fun main(args: Array<String>) {    val result = cal(2, 3, { a: Int, b: Int -> a + b })    println("result = $result")    // result = 5}

另外,在Kotlin中調(diào)用高階函數(shù)時(shí),如果最后一個(gè)參數(shù)為lambda表達(dá)式,可以將lambda表達(dá)式寫(xiě)在外面,而且如果沒(méi)有其它參數(shù)的話,小括號(hào)也是可以省略的。

fun cal(a: Int, b: Int, f: (a: Int, b: Int) -> Int): Int {    return f(a, b)}fun main(args: Array<String>) {    val result = cal(2, 3, { a: Int, b: Int -> a + b })    // 兩種寫(xiě)法等價(jià)    val result2 = cal(2, 3) { a: Int, b: Int -> a + b }    println("result = $result")    // result = 5}

函數(shù)變量

fun main(args: Array) {    val sumLambda = {a: Int, b: Int -> a + b}    var numFun: (a: Int, b: Int) -> Int    numFun = {a: Int, b: Int -> a + b}    numFun = sumLambda    numFun = ::sum    numFun(1,2)}

可以看到這個(gè)變量可以等于一個(gè)lambda表達(dá)式,也可以等于另一個(gè)lambda表達(dá)式變量,還可以等于一個(gè)普通函數(shù),但是在函數(shù)名前需要加上(::)來(lái)獲取函數(shù)引用。

lambda表達(dá)式實(shí)例

下面通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)看下一些具體的lambda表達(dá)式是怎么寫(xiě)的。

// 匿名函數(shù)val sum = fun(a: Int, b: Int): Int {    return a + b}// 具名函數(shù)fun namedSum(a: Int, b: Int): Int {    return a + b}// 高階函數(shù)fun highSum(a: Int, b: Int, f: (Int, Int) -> Int): Int {    return f(a, b)}fun main(args: Array<String>) {    // 通過(guò)()來(lái)執(zhí)行匿名函數(shù)sum    val add = sum(1, 2)    println(add)    // 通過(guò)lambda表達(dá)式來(lái)完成函數(shù)highSum    val add2 = highSum(3, 4) { a, b -> a + b }    println(add2)    // 通過(guò)函數(shù)引用來(lái)完成函數(shù)highSum    val add3 = highSum(5, 6, ::namedSum)    println(add3)    // forEach參數(shù)接收一個(gè)函數(shù)    args.forEach({ it: String -> println(it) })    // 去掉返回值,自動(dòng)推斷    args.forEach({ it -> println(it) })    // 只有一個(gè)參數(shù)的時(shí)候可以省略it    args.forEach({ println(it) })    // lambda表達(dá)式在最后一個(gè)參數(shù)可以外移    args.forEach() { println(it) }    // 函數(shù)若無(wú)參數(shù)可以去掉()    args.forEach { println(it) }    // 引用函數(shù)    args.forEach(::println)}

函數(shù)類型與實(shí)例化

類似Int、String這樣的數(shù)據(jù)類型,函數(shù)的類型表示為:

(Type1, Type2, ...) -> Type// 例如(Int) -> Int// 所以才有了這樣的函數(shù)fun test(a: Int, f: (Int) -> Int): Int {    return f(a)}

其中(Int) -> Int的地位和Int、String的地位是等價(jià)的。

函數(shù)既然是一種類型,那么函數(shù)也和Int、String一樣,是具有可實(shí)例化的實(shí)例的,例如Int的實(shí)例1、String的實(shí)例“xys”,那么獲取函數(shù)的實(shí)例,主要客源通過(guò)下面三種方式:

  1. :: 雙冒號(hào)操作符表示對(duì)函數(shù)的引用

  2. lambda表達(dá)式

  3. 匿名函數(shù)

fun main(args: Array<String>) {    // 引用函數(shù)    println(test(1, 2, ::add))    // 匿名函數(shù)    val add = fun(a: Int, b: Int): Int {        return a + b    }    println(test(3, 4, add))    // lambda表達(dá)式    println(test(5, 6, { a, b -> a + b }))// lambda作為最后一個(gè)參數(shù)可以提到括號(hào)外    println(test(5, 6) { a, b -> a + b })}fun test(a: Int, b: Int, f: (Int, Int) -> Int): Int {    return f(a, b)}fun add(a: Int, b: Int): Int {    return a + b}

lambda表達(dá)式的類型

通過(guò)下面的例子,可以了解下lambda表達(dá)式的類型,代碼如下所示。

// 無(wú)參,返回String() -> String// 兩個(gè)整型參數(shù),返回字符串類型(Int, Int) -> String// 傳入了一個(gè)lambda表達(dá)式和一個(gè)整型,返回Int(()->Unit, Int) -> Int

開(kāi)發(fā)者可以通過(guò)類似上面的形式來(lái)表達(dá)lambda表達(dá)式的類型,不過(guò)和Int、String一樣,lambda表達(dá)式也有自己的類,即Function類。

Kotlin封裝了Function0到Function22,一共23個(gè)Function類型,分別表示參數(shù)個(gè)數(shù)從0到22。

lambda表達(dá)式的return

除非使用標(biāo)簽指定了返回點(diǎn),否則return從最近的使用fun關(guān)鍵字聲明的函數(shù)返回。

fun main(args: Array<String>) {    var sum: (Int) -> Unit = tag@{        print("Test return $it")        return@tag    }    sum(3)}

SAM轉(zhuǎn)換

SAM = Single Abstract Method,即唯一抽象方法

SAM轉(zhuǎn)換是為了在Kotlin代碼中調(diào)用Java代碼所提供的一個(gè)語(yǔ)法糖,即為Java的單一方法的接口,提供lambda形式的實(shí)現(xiàn),例如Android中最常見(jiàn)的view.setOnClickListener:

// SAM convert in KTview.setOnClickListener{    view -> doSomething}// Java接口public interface OnClickListener {     void onClick(View v);}

SAM轉(zhuǎn)換是專門(mén)為Java提供的語(yǔ)法糖,用于將lambda表達(dá)式轉(zhuǎn)換成相應(yīng)的匿名類的實(shí)例。在Kotlin中實(shí)現(xiàn)相同的功能,只需要使用函數(shù)參數(shù)即可。

帶接收者的lambda表達(dá)式

lambda表達(dá)式實(shí)際上有兩種形式,一種是前面介紹的基本形式,還有一種是帶接收者的形式,兩種lambda表達(dá)式如下所示。

普通lambda表達(dá)式:{ () -> R }

即函數(shù)無(wú)入?yún)?,返回值為R類型。

帶接收者的lambda表達(dá)式:{ T.() -> R }

即申明一個(gè)T類型的接收者對(duì)象,且無(wú)入?yún)?,返回值為R類型。

Kotlin中的拓展函數(shù),實(shí)際上就是使用的帶接收者的lambda表達(dá)式,

帶接收者的lambda與普通的lambda的區(qū)別主要在于this的指向區(qū)別,T.() -> R里的this代表的是T的自身實(shí)例,而() -> R里,this代表的是外部類的實(shí)例。

使用typealias給重復(fù)申明的lambda表達(dá)式設(shè)置別名

fun fun1(f: (Int) -> Unit) {    f(1)}fun fun2(f: (Int) -> Unit) {    f(2)}// 使用typealiastypealias intFun = (Int) -> Unitfun fun3(f: intFun) {    f(3)}fun fun4(f: intFun) {    f(4)}fun main(args: Array<String>) {    fun1 { println(it) }    fun2 { println(it) }    fun3 { println(it) }    fun4 { println(it) }}

閉包

如果一個(gè)函數(shù)內(nèi)部申明或者返回了一個(gè)函數(shù),那么這個(gè)函數(shù)被稱之為閉包。

函數(shù)內(nèi)部的變量可以被函數(shù)內(nèi)部申明的函數(shù)所訪問(wèn)、修改,這就讓閉包可以攜帶狀態(tài)(所有的中間值都會(huì)被放入內(nèi)存中)。

開(kāi)發(fā)者可以通過(guò)閉包讓函數(shù)具有狀態(tài),從而可以封裝函數(shù)的狀態(tài),讓函數(shù)具有面向?qū)ο蟮奶匦浴?/p>

為什么需要閉包

在了解閉包之前,需要先了解下變量的作用域,在kotlin中,變量的作用域只有兩種,即全局變量和局部變量。

  • 全局變量,函數(shù)內(nèi)部和函數(shù)外部均可以直接訪問(wèn)。

  • 局部變量,只有函數(shù)內(nèi)部可以訪問(wèn)。

那么如何在函數(shù)外部訪問(wèn)函數(shù)內(nèi)部的局部變量呢,這就需要通過(guò)閉包來(lái)進(jìn)行訪問(wèn),閉包的設(shè)計(jì)就是為了能讓開(kāi)發(fā)者讀取某個(gè)函數(shù)內(nèi)部的變量。

所以閉包就是能夠讀取其它函數(shù)的局部變量的函數(shù)。

閉包讓函數(shù)攜帶狀態(tài)

fun test(): () -> Int {    var a = 1    println(a)    return fun(): Int {        a++        println(a)        return a    }}fun main(args: Array<String>) {    val t = test()    t()    t()}// output123

變量t的類型實(shí)際上是一個(gè)匿名函數(shù),所以在調(diào)用t函數(shù)執(zhí)行的時(shí)候,實(shí)際上執(zhí)行的是返回的匿名函數(shù),同時(shí),由于閉包可以攜帶外包的變量值,所以a的狀態(tài)值被傳遞了下來(lái)。

閉包可以訪問(wèn)函數(shù)體之外的變量,這被稱之為變量捕獲。閉包會(huì)將捕獲的變量保存在一個(gè)特殊的內(nèi)存區(qū)域,從而實(shí)現(xiàn)閉包攜帶狀態(tài)的功能。

kotlin實(shí)現(xiàn)接口回調(diào)

單方法回調(diào)

class Test {    private var callBack: ((str: String) -> Unit)? = null    fun setCallback(myCallBack: ((str: String) -> Unit)) {        this.callBack = myCallBack    }}

使用函數(shù)替代了接口的實(shí)現(xiàn)。

回調(diào)寫(xiě)法的演進(jìn)

Java思想的kotlin寫(xiě)法

interface ICallback {    fun onSuccess(msg: String)    fun onFail(msg: String)}class TestCallback {    var myCallback: ICallback? = null    fun setCallback(callback: ICallback) {        myCallback = callback    }    fun init() {        myCallback?.onSuccess("success message")    }}fun main(args: Array<String>) {    val testCallback = TestCallback()    testCallback.setCallback(object : ICallback {        override fun onSuccess(msg: String) {            println("success $msg")        }        override fun onFail(msg: String) {            println("fail $msg")        }    })    testCallback.init()}

使用lambda表達(dá)式替代匿名內(nèi)部類實(shí)現(xiàn)。

class TestCallback {    var mySuccessCallback: (String) -> Unit? = {}    var myFailCallback: (String) -> Unit? = {}    fun setCallback(successCallback: (String) -> Unit, failCallback: (String) -> Unit) {        mySuccessCallback = successCallback        myFailCallback = failCallback    }    fun init() {        mySuccessCallback("success message")        myFailCallback("fail message")    }}fun main(args: Array<String>) {    val testCallback = TestCallback()    testCallback.setCallback({ println("success $it") }, { println("fail $it") })    testCallback.init()}

這樣去掉了接口和匿名內(nèi)部類。

高階函數(shù)的使用場(chǎng)景

高階函數(shù)的一個(gè)重要使用場(chǎng)景就是集合的操作,里面下面這個(gè)例子,分別使用Java和Kotlin實(shí)現(xiàn)了「找最大值」的方法。

data class Test(val name: String, val age: Int)fun main(args: Array<String>) {    // Java寫(xiě)法    val testList = listOf(Test("xys", 18), Test("qwe", 12), Test("rty", 10), Test("zxc", 2))    findMax(testList)    // Kotlin寫(xiě)法    println(testList.maxBy { it.age })    println(testList.maxBy(Test::age))}fun findMax(test: List<Test>) {    var max = 0    var currentMax: Test? = null    for (t in test) {        if (t.age > max) {            max = t.age            currentMax = t        }    }    println(currentMax)}

函數(shù)式集合操作

fliter & map

filter用于數(shù)據(jù)的篩選,類似的還有filterIndexed,即帶Index的過(guò)濾器、filterNot,即過(guò)濾所有不滿足條件的數(shù)據(jù)。

map用于對(duì)數(shù)據(jù)進(jìn)行變換,代表了一種一對(duì)一的變換關(guān)系,它可以對(duì)集合中的數(shù)據(jù)做一次變換,類似的還有mapIndexed()。

fun main(args: Array<String>) {    val test = listOf(1, 3, 5, 7, 9)    // filter函數(shù)遍歷集合并選出應(yīng)用給定lambda后會(huì)返回true的那些元素    println("大于5的數(shù) ${test.filter { it > 5 }}")    // map函數(shù)對(duì)集合中的每一個(gè)元素應(yīng)用給定的函數(shù)并把結(jié)果收集到一個(gè)新集合    println("平方操作 ${test.map { it * it }}")    val testList = listOf(Test("xys", 18), Test("qwe", 12), Test("rty", 10), Test("zxc", 2))    // 將一個(gè)列表轉(zhuǎn)換為另一個(gè)列表    println("只展示name ${testList.map { it.name }}")    // filter與map鏈?zhǔn)讲僮?   println("展示age大于10的name ${testList.filter { it.age > 10 }.map { it.name }}")}data class Test(val name: String, val age: Int)

all & any & count & find

fun main(args: Array<String>) {    val test = listOf(1, 3, 5, 7, 9)    // all判斷是否全部符合lambda表達(dá)式的條件    println("是否全部符合>10 ${test.all { it > 10 }}")    // any判斷是否存在有符合lambda表達(dá)式的條件的數(shù)據(jù)    println("是否存在>8 ${test.any { it > 8 }}")    // count獲取符合lambda表達(dá)式條件的數(shù)據(jù)個(gè)數(shù)    println("大于5的個(gè)數(shù) ${test.count { it > 5 }}")    // find獲取符合lambda表達(dá)式條件的第一個(gè)數(shù)據(jù)    println("第一個(gè)大于5 ${test.find { it > 5 }}")    println("最后一個(gè)大于5 ${test.findLast { it > 5 }}")}

groupBy & partition & flatMap

flatMap()代表了一個(gè)一對(duì)多的關(guān)系,可以將每個(gè)元素變換為一個(gè)新的集合,再將其平鋪成一個(gè)集合。

groupBy()方法會(huì)返回一個(gè)Map<k,list>的Map對(duì)象,其中Key就是我們分組的條件,value就是分組后的集合。</k,list

fun main(args: Array<String>) {    val test = listOf("a", "ab", "b", "bc")    // groupBy按照l(shuí)ambda表達(dá)式的條件重組數(shù)據(jù)并分組    println("按首字母分組 ${test.groupBy(String::first)}")    // partition按照條件進(jìn)行分組,該條件只支持Boolean類型條件,first為滿足條件的,second為不滿足的    test.partition { it.length > 1 }.first.forEach { print("$it、") }    println()    test.partition { it.length > 1 }.second.forEach { print("$it、") }    println()    // flatMap首先按照l(shuí)ambda表達(dá)式對(duì)元素進(jìn)行變換,再將變換后的列表合并成一個(gè)新列表    println(test.flatMap { it.toList() })}

sortedBy

sortedBy()用于根據(jù)指定的規(guī)則進(jìn)行順序排序,如果要降序排序,則需要使用sortedByDescending(),

fun main(args: Array<String>) {    val test = listOf(3, 2, 4, 6, 7, 1)    println(test.sortedBy { it })}

take & slice

take()和slice()用于進(jìn)行數(shù)據(jù)切片,從某個(gè)集合中返回指定條件的新集合。類似的還有takeLast()、takeIf()等。

fun main(args: Array<String>) {    val test = listOf(3, 2, 4, 6, 7, 1)    // 獲取前3個(gè)元素的新切片    println(test.take(3))    // 獲取指定index組成的新切片    println(test.slice(IntRange(2, 4)))}

reduce

fun main(args: Array<String>) {    val test = listOf("a", "ab", "b", "bc")    // reduce函數(shù)將一個(gè)集合的所有元素通過(guò)傳入的操作函數(shù)實(shí)現(xiàn)數(shù)據(jù)集合的累積操作效果。    println(test.reduce { acc, name -> "$acc$name" })}

作用域函數(shù)

前面文章提到的作用域函數(shù)就是高階函數(shù)的一個(gè)重要實(shí)例。

lambda表達(dá)式的其它特性

惰性序列操作

當(dāng)一些集合函數(shù)進(jìn)行鏈?zhǔn)秸{(diào)用的時(shí)候,每個(gè)函數(shù)的調(diào)用結(jié)果都將保存為一個(gè)新的臨時(shí)列表,因此,大量的鏈?zhǔn)讲僮鲿?huì)產(chǎn)生大量的中間變量,從而導(dǎo)致性能問(wèn)題,為了提高效率,可以把鏈?zhǔn)讲僮鞲臑樾蛄校╯equance)。

調(diào)用擴(kuò)展函數(shù)asSequence把任意集合轉(zhuǎn)換成序列,調(diào)用toList來(lái)做反向的轉(zhuǎn)換

fun main(args: Array<String>) {    val testList = listOf(Test("xys", 18), Test("qwe", 12), Test("rty", 10), Test("zxc", 2))    // 函數(shù)的鏈?zhǔn)秸{(diào)用    println("集合調(diào)用 展示age大于10的name ${    testList.filter { it.age > 10 }            .map { it.name }}")    // 函數(shù)的序列操作    println("序列操作 展示age大于10的name ${    testList.asSequence()            .filter { it.age > 10 }            .map { it.name }            .toList()}")}data class Test(val name: String, val age: Int)

一個(gè)完整的序列包括兩個(gè)操作,即中間序列和末端序列,中間序列操作始終都是惰性的,末端序列操作觸發(fā)所有的惰性計(jì)算。

                       //中間操作            //末端操作testList.asSequence(). filter {..}.map {..}.toList()

因此,通過(guò)序列的這種方式,就避免了產(chǎn)生大量中間集合,從而提高了性能。

延遲計(jì)算

fun main(args: Array<String>) {    val addResult = lateAdd(2, 4)    print(addResult())}fun lateAdd(a: Int, b: Int): Function0<Int> {    fun add(): Int {        return a + b    }    return ::add}

在lateAdd內(nèi)部定義了一個(gè)局部函數(shù),最后返回了該局部函數(shù)的引用,對(duì)結(jié)果使用()操作符拿到最終的結(jié)果,達(dá)到延遲計(jì)算的目的。

fun main(args: Array<String>) {    val funs = mapOf("sum" to ::sum)    val mapFun = funs["sum"]    if (mapFun != null) {        val result = mapFun(1, 2)        println("sum result -> $result")    }}fun sum(a: Int, b: Int): Int {    return a + b}

掌握了lambda表達(dá)式,就等于戰(zhàn)士有了槍,多練習(xí),多思考,才能掌握l(shuí)ambda表達(dá)式的精髓,為后面掌握函數(shù)式編程,打下堅(jiān)實(shí)的基礎(chǔ)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容