zoukankan      html  css  js  c++  java
  • 谈谈Groovy闭包

    A closure is a function with variables bound to a context or environment in which it executes.

    概述###

    闭包和元编程是Groovy语言的两大精髓。Groovy的闭包大大简化了容器的遍历,提升了代码的可扩展性,使代码更加简洁优雅。闭包在Groovy编程中几乎无处不在。

    闭包就是一个闭合代码块,可以引用传入的变量。在 “Python使用闭包结合配置自动生成函数” 一文中,讲解了闭包的基本概念及如何使用闭包批量生产函数。本文谈谈Groovy的闭包及应用。

    概念###

    定义闭包####

    闭包在Groovy 的类型是 groovy.lang.Closure , 如下代码创建了一个使用 closure 来处理 Range [1,2,...,num] 的函数:

    def static funcWithClosure(int num, final Closure closure) {
            (1..num).collect { closure(it) }
        }
    

    使用该函数的代码如下:

    println funcWithClosure(5, {x -> x*x})
    

    如果闭包是最后一个参数,还可以写成:

    println funcWithClosure(5) { x -> x * 2 }
    

    闭包与函数的区别####

    有童鞋可能疑惑:闭包的形式很像函数,它与函数有什么区别呢?

    我们知道函数执行完成后,其内部变量会全部销毁,但闭包不会。闭包引用的外部变量会一直保存。闭包引用的外部变量具有“累积效应”,而函数没有。看下面一段代码:

        def static add(num) {
            def sum = 0
            sum += num
            return sum
        }
    
        def static addByClosure(init) {
            def addInner = {
                inc ->
                    init += inc
                    init
            }
            return addInner
        }
    
            println "one call: ${add(5)}"  // one call: 5
            println "two call: ${add(5)}"  // two call: 5
    
            def addClosure = addByClosure(0)
            println "one call: ${addClosure(5)}"  // one call: 5
            println "two call: ${addClosure(5)}"  // two call: 10
    

    第一个函数没有什么特别,进进出出,每次运行得到相同结果。 第二个函数,返回了一个闭包,这个闭包保存了传入的初始值,并且这个闭包能够将初始值加上后续传入给它的参数。划重点: 这里的初始值 init 是函数传入的参数,当这个参数被闭包引用后,它在函数第一次执行完成后值并没有被销毁,而是保存下来。

    柯里化####

    “函数柯里化(Currying)示例” 一文中讲述了函数柯里化的概念及Scala示例。Groovy 也提供了 curry 函数来支持 Curry.

    如下所示,计算 sumPower(num, p) = 1^p + 2^p + ... + num^p 。

    // sum(n, m) = 1^m + 2^m + ... + n^m
            def sumPower = {
                power, num ->
                    def sum = 0
                    1.upto(num) {
                        sum += Math.pow(it, power)
                    }
                    sum
            }
            def sumPower_2 = sumPower.curry(2)
            println "1^2 + 2^2 + 3^2 = ${sumPower_2(3)}"
            println "1^2 + 2^2 + 3^2 + 4^2 = ${sumPower_2(4)}"
    
            def sumPower_3 = sumPower.curry(3)
            println "1^3 + 2^3 + 3^3 = ${sumPower_3(3)}"
            println "1^3 + 2^3 + 3^3 + 4^3 = ${sumPower_3(4)}"
    

    sumPower.curry(2) 先赋值 power = 2 带入闭包块,得到一个闭包:

    def sumPower_2Explict = {
                num ->
                    def sum = 0
                    1.upto(num) {
                        sum += Math.pow(it, 2)
                    }
                    sum
    }
    

    再分别调用 sumPower_2Explict(3) = 14.0 , sumPower_2Explict(4) = 30.0

    柯里化使得闭包的威力更加强大了。 它是一个强大的函数工厂,可以批量生产大量有用的函数。

    应用###

    闭包可以很容易地实现以下功能:

    • 遍历容器
    • 实现模板方法模式,可用于资源释放等
    • 代码的可复用和可扩展
    • 构建 超轻量级框架

    遍历容器####

    如下代码所示,分别创建了一个Map, List 和 Range, 然后使用 each 方法遍历。一个闭合代码块,加上一个遍历变量,清晰简单。注意到,如果是一个单循环遍历,可以直接用 it 表示;如果是 Map 遍历,使用 key, value 二元组即可。

    
    class GroovyBasics {
        static void main(args) {
            def map = ["me":["name": 'qin', "age": 28], "lover":["name": 'ni', "age": 25]]
            map.each {
                key, value -> println(key+"="+value)
            }
    
            def alist = [1,3,5,7,9]
            alist.each {
                println(it)
            }
            (1..10).each { println(it) }
    
    
            def persons = [new Person(["name": 'qin', "age": 28]), new Person(["name": 'ni', "age": 25])]
            println persons.collect { it.name }
            println persons.find { it.age >=28 }.name
        }
    }
    
    

    再看一段代码:

    
            (1..10).groupBy { it % 3 == 0 } .each {
                key, value -> println(key.toString()+"="+value)
            }
    
    

    将 [1,10]之间的数按照是否被3除尽分组得到如下结果,使用链式调用连接的两个闭包实现,非常简明。

    false=[1, 2, 4, 5, 7, 8, 10]

    true=[3, 6, 9]


    模板方法模式####

    模板方法模式将算法的可变与不可变部分分离出来。 通常遵循如下模式: doCommon1 -> doDiff1 -> ... DoDiff2 -> ... -> DoCommon2 。 Java 实现模板方法模式,通常需要先定义一个抽象类,在抽象类中定义好算法的基本流程,然后定义算法里那些可变的部分,由子类去实现。可参阅:“设计模式之模板方法模式:实现可扩展性设计(Java示例)”

    使用闭包可以非常轻松地实现模板方法模式,只要将可变部分定义成 闭包即可。

    def static templateMethod(list, common1, diff1, diff2, common2) {
            common1 list
            diff1 list
            diff2 list
            common2 list
    }
    
    def common1 = { list -> list.sort() }
    def common2 = { println it }
    def diff1 = { list -> list.unique() }
    def diff2 = { list -> list }
    
    templateMethod([2,6,1,9,8,2,4,5], common1, diff1, diff2, common2)
    
    

    可复用与可扩展####

    可复用与可扩展性的前提是,将规则、流程中的可变与不可变分离出来,不可变代表着可复用部分,可变代表着可扩展的部分。由于闭包能够很容易地将可变与不可变分离,因此也很有益于实现代码的可复用与可扩展。

    小结###

    闭包和元编程是Groovy语言的两大精髓。本文讲解了Groovy闭包的定义、与函数的区别、柯里化及在遍历容器、实现模板方法模式等应用。使用闭包可提升代码的可复用和可扩展性,使代码更加简洁优雅,对于提升编程能力非常有益处。

    参考文献###


  • 相关阅读:
    CCleaner软件免费试用版与付费专业版区别
    TeamViewer 15.6.7新版本发布
    ABAPGIT用法
    ABAPGIT安装,配置及更新
    ABAPGIT安装,配置及更新
    SAP发布的WebService的登陆语言设置
    使用CG3Z向服务器添加文件时,报错:No physical path is configured for logical file name EHS_FTAPPL_2
    PO/PI 系统蓝图(SLD),企业服务存储库(ESR),整合目录(ID)基础配置
    S4 到 PO/PI proxy 配置手册
    在PI SLD中注册ABAP Technical System
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/9721739.html
Copyright © 2011-2022 走看看