Kotlin声明高阶函数

Kotlin声明高阶函数

1.函数类型

    把lambda表达式保存在局部变量中:

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

    在这个例子中,编译器推导出sum和action两个变量具有函数类型。我们再看看这些变量的显示类型声明是什么样的:

    val sum:(Int,Int)->Int={x,y->x+y}
    val action:()->Unit={ println(42)}

    声明函数类型,需要将函数参数类型放在括号中,紧接着是一个箭头和函数的返回类型。

    Unit类型用于表示函数不返回任何返回值。在声明一个普通函数时,Unit类型的返回值是可以省略的,但是一个函数类型的声明总是需要一个显示的声明类型,所以在这种场景下Unit是不能省略的。

    lambda表达式中{x,y->x+y}中是可以省略参数x,y的类型的,应为它们的类型已经在函数类型的变量声明部分指定了,不需要在lambda中去重复声明。

    函数的返回值也可以标记为可空类型。

var canReturnNull: (Int, Int) -> Int? = { null }

     可以定义一个函数类型的可空变量。为了明确表示是变量本身可空,而不是函数类型的返回类型可空,你需要将整个函数类型的定义包含在括号内并在括号后加一个问号:   

 var funOrNull: ((Int, Int) -> Int)? = null

    这个例子和前一个例子有微妙的区别。如果省略了括号,声明的将会是一个返回值可空的函数类型,而不是一个可空的函数类型变量。

2.调用作为参数的函数

定义一个简单高阶函数

  fun twoAndThree(operation: (Int, Int) -> Int) {
      val result = operation(2, 3)
      LogS(result)
   }
   twoAndThree { a, b -> a + b }//5
   twoAndThree { a, b -> a * b }//6

    调用作为参数的函数和调用普通函数的方法是一样的:filter函数。为了让事情简单起来。将实现基于String类型的filter该函数,但和 作用于集合的泛型版本的原理是相同的:

 fun String.filter(predicate:(Char)->Boolean):String

    filter函数以一个判断式作为参数。判断式的类型是一个函数,以字符作为参数并返回boolean类型的值。如果要让传递给判断式的字符出现在最终返回的字符串中,判断式需要返回true,反之返回false。

    fun String.filter(predicate: (Char) -> Boolean): String {
        val sb = StringBuilder()
        for (index in 0 until length) {
            val element = get(index)
            if (predicate(element)) sb.append(element)
        }
        return sb.toString()
    }

    LogS("aBc12c".filter { it in 'a'..'z' })//acc

    filter函数实现非常简单。它检查了每个字符是否满足判断式,如果满足就将字符添加到包含结果的StringBuilder中。

3.在Java中使用函数类

       在Java中可以很简单的调用使用函数类型的kotlin函数。Java8的lambda会被自动转换为函数类型的值。

   /*kotlin声明*/
    fun processsTheAnswer(f:(Int)->Int){
        LogS(f(42))
    }
    /*java调用*/
    processsTheAnswer(number->number+1)

    在旧版Java中,可以传递一个实现了函数接口中的invoke方法的匿名类的实例:

   /*java*/
        processsTheAnswer(
                new Function<Integer,Integer>(){
                    @Override
                    public Integer invoke(Integer number){
                        Logs(number);
                        return number+1;
                    }
                }
        )

    在Java中可以容易的使用Kotlin标准库中以lambda作为参数的拓展函数。但是注意它们看起来并没有Kotlin中那么直观———必须要显示地传递一个接受者对象作为第一个参数。

4.函数类型的参数默认值和null

    声明函数类型的参数的时候可以指定参数的默认值。要知道默认值的用处,我们回头看一看joinToString函数:

   fun <T> Collection<T>.joinToString(
            separator: String = ",",
            prefix: String = "",
            postfix: String = ""
    ): String {
        val result = StringBuilder(prefix)
        for ((index, element) in this.withIndex()) {
            if (index > 0) result.append(separator)
            result.append(element)
        }
        result.append(postfix)
        return result.toString()
    }

    这个实现很灵活,但是它没有让你控制转换的关键点:集合中的元素如何转换为字符串的。代码中使用了StringBuilder.append(0:Any?),它总使用toString方法将对象转换为字符串。在大多数情况下这样就可以了。但并不总是这样。我们现在已经知道可以传递一个lambda去指定如何将对象转换为字符串。但要求所有调用者都传递lambda是比较烦人的事情,因为大部分调用使用默认行文就可以了。为了解决这个问题,可以定义一个函数类型的参数并用一个lambda作为它的默认值。

 fun <T> Collection<T>.joinToString(
            separator: String = ",",
            prefix: String = "",
            postfix: String = "",
            transform:(T)->String={it.toString()}
    ): String {
        val result = StringBuilder(prefix)
        for ((index, element) in this.withIndex()) {
            if (index > 0) result.append(separator)
            result.append(transform(element))
        }
        result.append(postfix)
        return result.toString()
    }
 val letteras= listOf<String>("Alpha","Beta")
 LogS(letteras.joinToString { it.toLowerCase() })//alpha,beta

    注意这是一个泛型函数:它有一个类型参数T表示集合中元素的类型。lambda transform将接收这个类型的参数。

    声明函数类型的默认值并不需要特殊的语法——只需要把lambda作为值放在=号后面。上面的例子展示了不同函数的调用方式:省略整个lambda,在括号以外传递lambda,或者以命名参数形式传递。

    另一重选择声明一个参数可空的函数类型。注意这里不能直接调用作为参数传递进来的函数:kotlin会因为检测到潜在的空指针异常而导致编译失败。一种可选的方法是显示的检查null:

fun foo(callback:(()->Unit)?){
        if (callback!=null){
            callback()   
        }
    }

    还有一个更简单的版本,它利用这样一个事实,函数类型是一个包含invoke方法的接口具体实现,作为一个普通方法,invoke可以通过安全调用语法被调用:callback?.invoke()。

   fun <T> Collection<T>.joinToString(
            separator: String = ",",
            prefix: String = "",
            postfix: String = "",
            transform:((T)->String)?={it.toString()}
    ): String {
        val result = StringBuilder(prefix)
        for ((index, element) in this.withIndex()) {
            if (index > 0) result.append(separator)
            var str=transform?.invoke(element)?:element.toString()
            result.append(str)
        }
        result.append(postfix)
        return result.toString()
    }

5.返回函数的函数

    从函数中返回函数并没有函数作为参数传递那么常用,但它仍然非常有用。想象一下程序中的一段逻辑可能会因为程序的状态或其他条件产生变化——比如说,运输费用的计算依赖于选择的运输方式。可以定义一个函数来选择恰当的逻辑变体并将它作为另一个函数返回:

   enum class Delivery {STANDARD, EXPEDITED }

    class Order(val itemCount: Int)

    fun getShippingCostCalculator(
            delivery: Delivery
    ): (Order) -> Double {
        if (delivery == Delivery.EXPEDITED) {
            return { order -> 6 + 2.1 * order.itemCount }
        }
        return { order -> 1.2 * order.itemCount }
    }
      val calculator=getShippingCostCalculator(Delivery.EXPEDITED)
      LogS(calculator(Order(3)))//12.3

    声明一个返回另一个函数的函数,需要指定一个函数类型作为返回类型。

    在UI代码中定义一个返回函数的函数:

 data class Person(
            val firstName: String,
            val lastName: String,
            var phoneNumber: String?
    )

    class ContractListFilters {
        var prefix: String = ""
        var onlyWithPhoneNumber: Boolean = false

        fun getprediccate(): (Person) -> Boolean {
            val startsWithPrefix = {
                p: Person ->
                p.firstName.startsWith(prefix) || p.lastName.startsWith(prefix)
            }
            if (!onlyWithPhoneNumber) {
                return startsWithPrefix
            }
            return {
                startsWithPrefix(it)
                        && it.phoneNumber != null
            }
        }
    }
   val contacts = listOf(Person("Dmitry", "Jemerov", "123-4567"),
                Person("Svetlana", "Isakova", null))
        val contactListFilters=ContractListFilters()
        with(contactListFilters){
            prefix="Dm"
            onlyWithPhoneNumber=true
        }
        LogS(contacts.filter(
                contactListFilters.getprediccate()
        ))//[Person(firstName=Dmitry, lastName=Jemerov, phoneNumber=123-4567)]

    getPredicate方法返回一个函数的值,这个值被传递给filter作为参数,Kotlin的函数类型可以让这一切变得很简单,就跟处理其他类型的值一样,比如字符串。

    高阶函数是一个改进代码结构和减少重复代码的利器。

6.通过lambda去除重复代码

    函数类型和lambda表达式一起组成了一个创建可重用代码的好工具。许多以前只能通过重复笨重的结构来避免的重复代码,现在可以通过使用简便的lambda表达式被消除。

    定义站点访问技术:    

    data class SiteVisit(
            val path: String,
            val duration: Double,
            val os: OS
    )

    enum class OS {WINDOWS, LINUX, MAC, IOS, ANDROID }

    val log=listOf(
            SiteVisit("/",34.0,OS.WINDOWS),
            SiteVisit("/",34.0,OS.MAC),
            SiteVisit("/",34.0,OS.WINDOWS),
            SiteVisit("/",34.0,OS.IOS),
            SiteVisit("/",34.0,OS.ANDROID)
    )

    使用硬编码的过滤器分析站点访问数据

     val averageEindowsDuration=log
                .filter { it.os==OS.WINDOWS }
                .map(SiteVisit::duration)
                .average()

     LogS(averageEindowsDuration)//29.0

    用一个普通方法去除重复代码

fun List<SiteVisit>.averafeDurarionFor(os: OS) = filter { it.os == os }.map(SiteVisit::duration).average()
LogS(log.averafeDurarionFor(OS.WINDOWS))//29.0

    用一个复杂的硬编码函数分析站点访问数据

  val averageMobileDuration=log
                .filter { it.os in setOf(OS.IOS,OS.ANDROID) }
                .map(SiteVisit::duration)
                .average()

  LogS(averageEindowsDuration)//29.0

    用一个高阶函数去除重复代码

 fun List<SiteVisit>.averageDurationFor(predicate:(SiteVisit)->Boolean)=
            filter(predicate).map(SiteVisit::duration).average()
LogS(log.averageDurationFor { it.os in setOf(OS.ANDROID,OS.IOS) })//29.0
    函数类型可以帮助去除重复代码。如果你禁不住复制粘贴了一段代码,那么很可能这段代码是可以避免的,使用lambda,不仅可以抽取重复的数据,也可以抽取重复的行为。



内容概要:本文围绕“新型电力系统下多分布式电源接入配电网承载力评估方法”的研究,系统性地介绍了基于Matlab的仿真建模与代码实现方案,旨在评估高比例分布式电源(如光伏、风电等)接入背景下配电网的接纳能力。研究融合了智能优化算法(如蜣螂优化、灰狼优化、遗传算法)、多目标优化、鲁棒优化及双层优化模型,结合潮流计算、稳定性分析与故障仿真,构建了完整的承载力评估体系。文档不仅提供核心算法实现,还拓展至微电网调度、储能配置、电氢耦合系统、电动汽车协同等前沿方向,强调“复现+创新”相结合的科研路径,助力研究者快速掌握高水平论文复现技巧并激发原创思路。; 适合人群:具备电力系统、自动化或相关专业背景,熟悉Matlab/Simulink仿真环境,正在从事科研或工程应用的研究生及初级科研人员(工作1-3年);; 使用场景及目标:①复现高水平期刊中关于配电网承载力的优化模型;②开展高比例可再生能源接入下的配电网规划与运行研究;③学习并应用智能优化算法解决复杂电力系统问题;④获取完整科研资源包以加速课题进展与论文撰写; 阅读建议:建议读者关注公众号“荔枝科研社”获取网盘资源,下载全套代码与模型文件,按照文档结构循序渐进学习,重点理解算法设计逻辑与仿真建模细节,结合所提供的复现案例深化对优化模型与工程应用场景的理解,提升科研效率与创新能力。
内容概要:本文系统研究了综合能源系统中的容量配置与运行调度问题,采用双层优化方法构建模型并通过Matlab代码实现求解。上层优化侧重于设备容量的科学配置,以降低投资成本并提升系统经济性;下层优化聚焦于多能源协同运行调度,综合考虑光伏、储能、电动汽车等多种能源形式的动态特性,旨在实现系统在不同运行工况下的能效最大化、运行可靠性与低碳化目标。研究融合智能优化算法(如遗传算法、粒子群算法)与电力系统建模技术,深入探讨了多能耦合、不确定性处理及复杂约束下的优化机制,并提供了完整的仿真案例与代码资源,涵盖微电网调度、风光储协同、电动汽车接入等典型应用场景,形成了具有较强实用价值的科研技术体系。; 适合人群:具备电力系统分析、优化算法理论及Matlab编程基础的研究生、科研人员和工程技术人员,特别适用于从事综合能源系统规划、微电网运行、智能调度与能源互联网等领域研究的专业人士。; 使用场景及目标:① 掌握双层优化在综合能源系统中的建模方法与求解流程;② 利用所提供Matlab代码进行科研复现、算法改进与系统仿真验证;③ 拓展应用于电动汽车集群调度、可再生能源消纳、多能互补系统优化等实际工程与学术研究场景; 阅读建议:建议结合文档中列出的相关研究方向与配套代码资源,按照主题分类循序渐进地学习,优先理解双层架构的设计逻辑与上下层耦合机制,并借助提供的网盘资料开展仿真实验与参数调试,以深化对优化模型与算法实现的理解,提升科研创新能力。
【重要提示】本资源设置为0积分下载,若非0积分请勿轻易下载 亲爱的CSDN用户: 首先感谢你点进这个资源页面。我需要提前说明一个重要情况: 本资源原本已设置为“0积分下载”,即作者希望完全免费共享。但CSDN平台有时会根据文件的下载热度、文件大小、用户权限等因素,自动将部分资源的积分调整为非0数值(如1积分、2积分、5积分等)。这是平台系统的自动行为,而非作者本人的设定。 因此,如果你当前看到该资源的下载所需积分不是0(例如显示为1、2、3……),请谨慎决定是否下载。 如果你按照非0积分支付并下载后发现资源内容不符合预期、链接失效,或者实际上该资源本应是免费的,作者无法为此承担积分损失或退还操作。强烈建议:仅在页面显示为0积分时进行下载。 另外,本资源描述中并未直接提供具体的下载地址或外部链接,因为它本身是一个通过CSDN官方上传通道提交的文件/内容包。如果你看到描述中没有外部网盘地址,这是正常的——资源文件应通过CSDN内置的“下载”按钮获取。若因平台积分显示异常导致你支付了积分,请优先联系CSDN客服咨询积分退还政策,作者没有权限修改平台自动设定的积分值。 感谢你的理解与支持。技术分享本应开放,但受限于平台规则,特此提醒如上。祝学习进步!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值