Kotlin 进阶之路(五) Lambda 编程

Kotlin 进阶之路(五) Lambda 编程

5.1 Lambda 表达式入门

  • Lambda 表达式介绍

1 无参数有返回值

定义无参数有返回值的 Lambda 表达式,只需要将函数体写在 {} 中,函数体可以是表达式或语句块

调用语法格式

{函数体}()

fun main(args: Array<String>) {
	{
		println("Lambda表达式无参数有返回值")
	}()
	//Lambda表达式无参数有返回值

2 有参数有返回值

定义有参数有返回值的 Lambda 表达式,需要指定参数名称以及参数类型,参数之间使用英文 , 分割,且参数类型可以省略,函数体会自动校对。Lambda 表达式中的 -> 用于表示箭头,用于指定参数或数据指向。

调用语法格式

{参数名: 参数类型, 参数名: 参数类型 … -> 函数体}(参数1, 参数2, …)

fun main(args: Array<String>) {
    val sum = {a : Int, b : Int -> a+b}(6,8)
    println("sum=$sum")//14
	var sum2 = {a : Int, b : Int -> a+b}
    println("sum=" + sum2(6,8))//14
}

  • Lambda 表达式返回值

Lambda 表达式省略了返回值的类型和方法名,接下来分析是如何声明返回值的类型和返回值?

fun main(args: Array<String>) {
    println("-----------------------1------------------------")
    val result1 = {
        println("输出语句1")
        "字符串"}()
    println("返回值: $result1")
    println("返回值类型: ${result1.javaClass}")
    println("-----------------------2------------------------")
    val  result2 = {
        println("输出语句1")
        println("输出语句2")
        18
    }()
    println("返回值: $result2")
    println("返回值类型: ${result2.javaClass}")
    println("-----------------------3------------------------")
    val  result3 = {
        println("输出语句1")
        println("输出语句2")
        true
    }()
    println("返回值: $result3")
    println("返回值类型: ${result3.javaClass}")
    /*-----------------------1------------------------
    输出语句1
    返回值: 字符串
    返回值类型: class java.lang.String
    -----------------------2------------------------
    输出语句1
    输出语句2
    返回值: 18
    返回值类型: int
    -----------------------3------------------------
    输出语句1
    输出语句2
    返回值: true
    返回值类型: boolean*/
}

从结果可以得出,在每次调用 Lambda 表达式时,不管方法体里面的语句执行多少条,返回值的类型和值都是由方法体中最后一条语句决定的。

5.2 高阶函数的使用

Lambda 表达式作为函数的实际参数或返回值使用时,成为高阶函数

  • 函数作为参数使用
fun IntRange.pickNum(function: (Int) -> Boolean) : List<Int>{
    val resultList = mutableListOf<Int>()
    for(i in this){                 //this 指向定义的区间(IntRange)范围1~20
        if(function(i)){            //判断传递过来的Lambda表达式是否满足条件
            resultList.add(i)       //符合条件的数据添加到集合中
        }
    }
    return resultList
}

fun main(args: Array<String>) {
    val list = 1..20
    println("---------能被5整除的数-----------")
    println(list.pickNum({ x: Int -> x % 5 == 0}))
    println("---------能被10整除的数-----------")
    println(list.pickNum({ x : Int -> x % 10 == 0}))
	/*---------能被5整除的数-----------
	[5, 10, 15, 20]
	---------能被10整除的数-----------
	[10, 20]*/
}

function: (Int) -> Boolean 作为参数,形式参数名 function 可随意修改, 形参类型 Int,
函数返回值 Boolean

  • 函数作为参数优化

1.省略小括号

如果函数只有一个参数,且这个参数类型是一个函数类型,则在调用函数时可以去掉函数名后面的小括号

省略前
list.pickNum({ x: Int -> x % 5 == 0})

省略后
list.pickNum{ x: Int -> x % 5 == 0}

2.将参数移动到小括号外面

如果一个函数有多个参数,但最后一个参数类型是函数类型,则在调用函数时可以将最后一个参数从括号中移除,并去掉参数之间的符号 ,

fun IntRange.pickNum(need: Int, function: (Int) -> Boolean) : List<Int>{
    val resultList = mutableListOf<Int>()
    for(i in this){                 
        if(function(i)){            
            resultList.add(i)      
        }
    }
    return resultList
}

省略前
list.pickNum(1, { x: Int -> x % 5 == 0})

省略后
list.pickNum(1) { x: Int -> x % 5 == 0}

3.使用 it 关键字

无论函数包含多少个参数,如果其中有参数是函数类型,并且函数类型满足只接收一个参数的要求,可用 it 关键字代替函数的形参及箭头

省略前
list.pickNum{ x: Int -> x % 5 == 0}

省略后
list.pickNum{ it % 5 == 0}

总结:

在 Kotlin 中,从函数定义形式方面来讲,函数可以有普通的定义方式,可以用表达式函数体,可以把 Lambda 赋值给变量,从函数放置的位置来讲,函数可以放置在类的外面(顶层函数)、可以放置在方法的内部(嵌套函数)、可以作为参数传递、可以作为函数的返回值。函数的功能非常强大与灵活,并且地位大大提升。

  • 函数作为返回值
enum class USER{//声明枚举
    NORMAL, VIP
}
fun getPrice(userType : USER) : (Double) -> Double{
    if (userType == USER.NORMAL){
        return {it}
    }
    return {price -> 0.88*price}
}

fun main(args: Array<String>) {
    val normalUserPrice = getPrice(USER.NORMAL)(200.0)
    println("普通用户价格: $normalUserPrice")
    val vipPrice = getPrice(USER.VIP)(200.0)
    println("超级会员价格: $vipPrice")
	/*
	普通用户价格: 200.0
	超级会员价格: 176.0
	*/
}

5.3 标准库中的高阶函数

  • 高阶函数操作集合

1.查找元素操作

来看下 Collections 中提供的一些用于查找、匹配集合中元素的方法

方法声明功能描述
Iterable.find(predicate: (T) -> Boolean): T?查找并返回指定条件的第一个元素,没有找到符合条件的元素返回 NULL
Iterable.first(predicate: (T) -> Boolean): T查找并返回指定条件的第一个元素,如果没有找到抛出异常
List.last(predicate: (T) -> Boolean): T查找并返回指定条件的最后一个元素,如果没有找到抛出异常
Iterable.single(predicate: (T) -> Boolean): T查找并返回指定条件的元素,并且只能有一个元素,反之抛出异常
Iterable.takeWhile(predicate: (T) -> Boolean): List查找并返回指定条件的列表,如果没有找到则返回一个空的列表,第一个元素必须满足条件,否则不会继续查找
Iterable.filter(predicate: (T) -> Boolean): List查找并返回指定条件的列表,如果没有找到则返回一个空的列表,只要满足条件即可
Iterable.count(predicate: (T) -> Boolean): Int查找(统计)出当前集合中满足指定条件的个数
fun main(args: Array<String>) {
    val list = listOf(-2, -1, 0, 1, 2)
    println("--------find--------")
    println("找出大于0的元素: ${list.find{ it > 0 }}")//找出大于0的元素: 1
    println("找出等于3的元素: ${list.find{ it == 3 }}")//找出等于3的元素: null
    println("--------first--------")
    println("大于0的元素: ${list.first{ it > 0 }}")//大于0的元素: 1
    println("大于0的元素: ${list.last{ it > 0 }}")//大于0的元素: 2
    //println("等于3的元素: ${list.first{ it == 3 }}")//Exception in thread "main" java.util.NoSuchElementException:
    // Collection contains no element matching the predicate.

    println("大于1的元素: ${list.single{ it > 1 }}")
    //println("大于-2的元素: ${list.single{ it > -2 }}")//Exception in thread "main" java.lang.IllegalArgumentException:
    // Collection contains more than one matching element.

    println("大于-3的元素: ${list.takeWhile{ it > -3 }}")//大于-3的元素: [-2, -1, 0, 1, 2]
    println("大于0的元素: ${list.takeWhile{ it > 0 }}")//大于0的元素: []  必须是第一个元素满足条件才能继续往下查找
    println("小于0的元素: ${list.takeWhile{ it < 0 }}")//小于0的元素: [-2, -1]

    println("大于-3的元素: ${list.filter{ it > -3 }}")//大于-3的元素: [-2, -1, 0, 1, 2]
    println("大于0的元素: ${list.filter{ it > 0 }}")//大于0的元素: [1, 2]
    println("小于0的元素: ${list.filter{ it < 0 }}")//小于0的元素: [-2, -1]

    val listSuper = listOf(60,80,100,120,140)
    println("查找大于100的元素个数: ${listSuper.count{ it > 100}}")//查找大于100的元素个数: 2
    println("查找小于60的元素个数: ${listSuper.count{ it < 60}}")//查找小于60的元素个数: 0
}

2.比较元素操作

来看下 Collections 中提供的一些用于比较集合中元素的方法

方法声明功能描述
Iterable.maxBy(selector: (T) -> R): T?查找并返回集合中的最大值
Iterable.minBy(selector: (T) -> R): T?查找并返回集合中的最小值
Iterable.distinctBy(selector: (T) -> K): List去除集合中重复的元素
fun main(args: Array<String>) {

    val list = listOf(-2,0,0,1,1,2)
    println("-----------查找最大值------------")
    println(list.maxBy { it })//2
    println("-----------查找最小值------------")
    println(list.minBy { it })//-2
    println("-----------集合去重-----------")
    println(list.distinctBy { it })//[-2, 0, 1, 2]
}

  • 标准库中的高阶函数

我们再来看下 Standard 类中的高阶函数

方法声明功能描述适用场景
repeat(times: Int, action: (Int) -> Unit)用于重复执行 action() 函数 times 次,其中 times 表示重复执行的次数适用于重复执行一个函数的场景
run(block: () -> R): Rrun()函数只接收一个 Lambda() 函数为参数,返回值为最后一行语句的值或 return 表达式的值适用于 let() with()的任何场景
T.run(block: T.() -> R): R调用指定的函数块,用 this 代表函数块中当前的引用对象,并且调用函数块的方法时,this 可省略。该函数的返回值是函数块中的最后一行语句的值或 return 表达式的值适用于 let() with()的任何场景
T.let(block: (T) -> R): R调用指定的函数块,该函数的返回值是函数块中的最后一行语句的值或 return 表达式的值适用于处理变量不为 null 的场景或确定一个变量的作用域
T.apply(block: T.() -> Unit): T调用指定的函数块,用 this 代表函数块中当前的引用对象,并且调用函数块的方法时,this 可省略。apply() 函数必须要有返回值,并且返回值是当前的引用对象适用于对实例化对象中的属性进行赋值时,或动态创建一个 View,为该 View 绑定数据时
with(receiver: T, block: T.() -> R): R将对象作为函数的参数,在函数内可以通过 this 指代该对象。返回值是函数块中的最后一行语句的值或 return 表达式的值适用于调用同一个类的多个方法时,可以省去类名,直接调用类的方法即可
fun main(args: Array<String>) {
    println("-----------第1个参数为2时------------")
    repeat(2, { print("中国")})//中国中国
    println("-----------第1个参数为1时------------")
    repeat(1){ print("中国")}//中国
    println("-----------第1个参数为0时-----------")
    repeat(0){ print("中国")}

    val list = ArrayList<String>()
    list.run {
        this.add("土星")
        add("天王星")
        add("海王星")
    }
    println(list)//[土星, 天王星, 海王星]

    val list2 = ArrayList<String>()
    val value = list2.run {
        add("金星")
        println("集合数据: $list2")
        return@run size  //结束当前 run 函数,继续执行外部的语句
        add("火星")
        println("集合数据: $list2")
    }
    println("返回值是: $value")
    println("集合数据: $list2")
    /*集合数据: [金星]
    返回值是: 1
    集合数据: [金星]*/
}

5.4 内联函数

Kotlin 中提供 inline 修饰符,被 inline 修饰的 Lambda (函数) 称为内联函数,使用内联函数可以降低程序的内存消耗。

  • 使用内联函数
inline fun <T> check(lock : Lock, body: () -> T) : T{
    lock.lock()
    try {
        return body()
    }finally {
        lock.unlock()
    }
}

fun main(args: Array<String>) {
   val ll = ReentrantLock()
   check(ll, { print("这是内联函数方法体") })//ll 是一个 Lock对象
}

上面的代码在调用内联函数时,编译器会对 check(ll, { print(“这是内联函数方法体”) }) 进行优化,去掉方法名称以避免入栈出栈操作,直接执行方法体内容。

	lock.lock()
    try {
        return "这是内联函数方法体"
    }finally {
        lock.unlock()
    }
  • 禁用内联函数

在使用内联函数时,参数也会随之内联,这样会出现一个问题,如果参数有 Lambda 表达式时,Lambda 表达式便不是一个函数的对象,从而也不能作为参数来传递。在此,我们可使用 noinline 修饰符来修饰参数,禁止参数产生内联关系。

inline fun checkT(noinline function: (Int) -> Boolean){
    test(function)
}

fun test(function: (Int) -> Boolean){
    println("编译通过")
}

fun main(args: Array<String>) {
    checkT { x: Int -> x == 2 }//编译通过
}

后续

Kotlin 进阶之路(六) 泛型

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
基于PyTorch的Embedding和LSTM的自动写诗实验LSTM (Long Short-Term Memory) 是一种特殊的循环神经网络(RNN)架构,用于处理具有长期依赖关系的序列数据。传统的RNN在处理长序列时往往会遇到梯度消失或梯度爆炸的问题,导致无法有效地捕捉长期依赖。LSTM通过引入门控机制(Gating Mechanism)和记忆单元(Memory Cell)来克服这些问题。 以下是LSTM的基本结构和主要组件: 记忆单元(Memory Cell):记忆单元是LSTM的核心,用于存储长期信息。它像一个传送带一样,在整个链上运行,只有一些小的线性交互。信息很容易地在其上保持不变。 输入门(Input Gate):输入门决定了哪些新的信息会被加入到记忆单元中。它由当前时刻的输入和上一时刻的隐藏状态共同决定。 遗忘门(Forget Gate):遗忘门决定了哪些信息会从记忆单元中被丢弃或遗忘。它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 输出门(Output Gate):输出门决定了哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。同样地,它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 LSTM的计算过程可以大致描述为: 通过遗忘门决定从记忆单元中丢弃哪些信息。 通过输入门决定哪些新的信息会被加入到记忆单元中。 更新记忆单元的状态。 通过输出门决定哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。 由于LSTM能够有效地处理长期依赖关系,它在许多序列建模任务中都取得了很好的效果,如语音识别、文本生成、机器翻译、时序预测等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cczhengv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值