Groovy 閉包

閉包

閉包是一段可執(zhí)行的代碼塊,類似于方法也可以傳遞參數(shù);可在閉包中訪問屬性值,也就是說可以修改閉包作用域中的所有變量值;

簡單實(shí)例

// name為閉包參數(shù)可有多個(gè)菲嘴, '->' 之后為閉包執(zhí)行代碼體
def clos = { name -> println("${name} for better life!") }   // name為閉包參數(shù)
clos.call('YY')    // 執(zhí)行閉包(顯示調(diào)用)
clos('CC')    // 省略call() (隱士調(diào)用)

單個(gè)隱藏參數(shù) it

// 單個(gè)隱參數(shù) it
def cols2 = {println("${it} for better life!")}    // 訪問it隱藏屬性
cols2('YY')

閉包作為函數(shù)方法參數(shù)傳遞

def my_clos = { param1 -> println("Welcome ${param1}") }
def method(clos) {
    def name = 'better'
    clos(name)   // 調(diào)用閉包
}
method(my_clos)  // 迪用閉包

//=============== 特殊的語法格式:以后DSL會經(jīng)常看到 ===========================
//  如果閉包是方法的最后一個(gè)參數(shù)汰翠,可將閉包寫入括號外龄坪,如下:
// 閉包在實(shí)參參數(shù)的外部, '()' 也可以省略
method() { param1 -> println("Welcome ${param1}") }
// 省略括號
method { param1 -> println("Welcome ${param1}") }

// 例子:求1到100的和
println("===== 使用閉包求 1 到 100 的和 ===========")
def result = 0
1.upto(100) { result = result + it }    // 最后一個(gè)形參為閉包,放到括號外
println(result)

集合與字符串

舉例:each方法原型為:each(Closure closure)复唤,源碼為:

 public static <T> Iterator<T> each(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
        while (self.hasNext()) {
            Object arg = self.next();
            closure.call(arg);
        }
        return self;
    }

例子

    @Test
    void testClosure3() {
        def list = [1, 2, 3, 4, 5]
        list.each {
            if (it % 2 == 0) {
                println(it)     // 2 4
            }
        }

        def map = [k1: 'better', k2: 'zhaoyu']
        map.each {
                // 這里的閉包有2個(gè)參數(shù)健田, 查看api
            k, v -> println "key:$k, value:$v"
        }
    }

常用的一些閉包方法:

  • find(): 找到就返回;
  • findAll(): 找所有滿足閉包條件的佛纫;
  • any(): 只要一個(gè)滿足閉包條件就返回true妓局,否則false;
  • every():必須每個(gè)item滿足閉包條件才返回true呈宇,否則false好爬;

collect與inject

collect: 閉包變更,返回新集合甥啄;
inject:傳入初始值存炮,遍歷集合,不斷與后一個(gè)累計(jì)處理

collect的簡單用法:
println([1, 2, 3, 4].collect { it * 2 }) // 變更 (2,4僵蛛,6尚蝌,8)

collect的高級用法:map 轉(zhuǎn)換的例子

   @Test
    void test() {
        def doubles = { item -> item * 2 }
        def triples = { item -> item * 3 }
        def isEven = { item -> item % 2 == 0 }

        println("Doubles: ${map(doubles, [1, 2, 3, 4])}")
        println("Triples: ${map(triples, [1, 2, 3, 4])}")
        println("Evens: ${map(isEven, [1, 2, 3, 4])}")
    }

    /**
     * map 轉(zhuǎn)換方法
     * @param clos 閉包
     * @param list 集合
     */
    def map(clos, list) {
        return list.collect(clos)
    }

inject的例子

// 累加
def clos = (0..100).inject(0) { prev, element -> prev + element }
println("Sum : ${clos}")

閉包的其他特性

閉包是一種對象迎变,所以可以作為方法的參數(shù)充尉,如下:

閉包作為方法的參數(shù)

@Test
 void test3() {
     def isEven = { item -> item % 2 == 0 }
     def isOdd = { item -> !isEven(item) }
     println(filter([1, 2, 3, 4], isEven))
 }

/**
 * 閉包作為方法的參數(shù)
 * @param list
     * @param clos
     */
def filter(list, clos) {
    return list.findAll(clos)
}

閉包作為另一個(gè)閉包的參數(shù)

@Test
void test4() {
    // 閉包1
    def isEven = { item -> item % 2 == 0 }
    // 閉包2,使用閉包1作為參數(shù)
    def takeWhile = { closure, list ->
        def result = []
        for (item in list) {
            if (closure(item)) {
                result << item
            }
        }
        return result
    }

    println(takeWhile(isEven, [1, 2, 3, 4]))
}

返回值為閉包

@Test
void test5() {
    def twice = multiply(2)
    println("twice(4): ${twice(4)}")

    // == 閉包返回閉包 ==
    def multiplication = { x -> return { y -> x * y } }
    def quadruple = multiplication(4)
    println("quadruple(3): ${quadruple(3)}")

}
// 方法返回閉包
def multiply(x) {
    return { y -> return x * y }
}

閉包的嵌套

// 閉包嵌套閉包
@Test
void test6() {
    // 選擇排序閉包
    def selectionSoft = { list ->
        def swap = { sList, p, q ->
            def temp = sList[p]
            sList[p] = sList[q]
            sList[q] = temp
        }

        // 找最小的閉包
        def minimumPosition = { pList, from ->
            def mPos = from
            def nextFrom = 1 + from
            for (j in nextFrom..<pList.size()) {
                if (pList[j] < pList[mPos]) {
                    mPos = j
                }
            }
            return mPos
        }

        def size = list.size() - 1
        for (k in 0..<size) {
            def minPos = minimumPosition(list, k)
            swap(list, minPos, k)       // 交換
        }

        return list
    }

    def table = [-11, 2, -20, 15, 0]
    def sorted = selectionSoft(table)
    println(sorted)
}

閉包的更多信息

閉包與方法

// 閉包
def twice = { n -> return 2 * n }
// 方法
def twice(n) { return 2 * n }

默認(rèn)參數(shù)衣形,與方法類型

// 默認(rèn)參數(shù)
def greetting = { msg, name = 'better' -> println("${msg} ---> ${name}") }
println(greetting("Hello"))

return 語句

閉包中return的語法比較重要驼侠,如:為確定某個(gè)item是否在某個(gè)列表之中,通常使用 find()方法谆吴;
如下:

def hasMemberA(item, list) {
    def size = list.size()
    for (index in 0..<size) {
        if (item == list[index]) {
            return true
        }
    }
    return false
}

def hasMemberB(item, list) {
    // 使用閉包
    list.each { it ->
        if (item == it) {
            return true
        }
    }
    return false
}

def numbers = [11,12,13,14]
println("hasMemberA(11, numbers) = ${hasMemberA(11, numbers)}")     // true
println("hasMemberB(11, numbers) = ${hasMemberB(11, numbers)}")     // 永遠(yuǎn)false

hasMemberB() 方法之所以返回false倒源,我們看看each閉包的實(shí)現(xiàn),迭代器被用于檢遍歷列表中對象句狼,并調(diào)用閉包笋熬;示例中的實(shí)參的閉包語句,實(shí)際上是在閉包中執(zhí)行的腻菇,跟方法沒有關(guān)系胳螟,這樣while循環(huán)一直執(zhí)行,知道遍歷完畢筹吐,所以hasMembersB()方法返回了false;

public static <T> Iterator<T> each(Iterator<T> self, @ClosureParams(FirstGenericType.class) Closure closure) {
        while(self.hasNext()) {
            Object arg = self.next();
            closure.call(arg);
        }
        return self;
    }

判斷閉包是否提供

通過if判斷

   @Test
    void test1() {
        method() {
            println("Hello")
        }
    }
    def method(closure) {
        if(closure) {   // 判斷閉包是否提供了
            closure()
        } else {
            println("use default closure")
        }
    }

閉包委托

this, owner,delegate是閉包的3個(gè)屬性糖耸,用于確定由哪個(gè)對象處理該閉包內(nèi)的方法調(diào)用;
一般情況下丘薛,delegate會設(shè)置owner嘉竟,但可對其進(jìn)行修改,發(fā)揮閉包的一些其他作用洋侨;

    def examingClosure(closure) {
        closure()
    }

    @Test
    void test2() {
        examingClosure() {
            println("First Closure: ")
            println("class is ${getClass().name}")
            println("this is ${this}, super: ${this.getClass().superclass.name}")
            println("owner is ${owner}, super:${owner.getClass().superclass.name}")
            println("delegate is ${delegate}, super: ${delegate.getClass().superclass.name}")
        }
    }

輸出如下:

First Closure:
class is book.bibao.Test2$_test2_closure2
this is book.bibao.Test2@59717824, super: java.lang.Object
owner is book.bibao.Test2@59717824, super:java.lang.Object
delegate is book.bibao.Test2@59717824, super: java.lang.Object

從輸出可以看到舍扰,閉包被創(chuàng)建成了內(nèi)部類,在這里希坚,this owner delegate 指向同一對象妥粟;
閉包內(nèi)方法調(diào)用的順序?yàn)橄?this,this無法處理吏够,再 owner勾给,再delegate;

修改閉包delegate例子(這個(gè)例不咋的)

  class Handler {
        def f1() { println("f1 of Handler called...") }
        def f2() { println("f2 of Handler called...") }
    }

    class Example {
        def f1() { println("f1 of Example called...") }
        def f2() { println("f2 of Example called...") }

        def foo(closure) {
            closure.delegate = new Handler()        // 設(shè)置代理
            closure()
        }
    }

    def f1() {
        println("f1 of Script called...")
    }

    @Test
    void test3() {
        new Example().foo() {
            f1()
            f2()
        }
    }

輸出:

f1 of Script called...
f2 of Handler called...

以上代碼中锅知,閉包內(nèi)的方法調(diào)用首先被路由到閉包上下文對象this,沒找到播急,則路由到delegate

上下文 with 方法

Groovy支持創(chuàng)建一個(gè)上下文(context),在with的作用域內(nèi)調(diào)用的任何方法售睹,都會被定向到該上下文對象桩警,如下:

def list = [1,2]
list.add(1)
list.add(2)
println(list)

// 使用with
list.with {
     add(3)
     add(4)
}
println(list)

list.with {
    println("this is ${this}")       // this is book.bibao.With_Test@71c8becc
    println("own is ${owner}")  // own is book.bibao.With_Test@71c8becc
    println("delegate is ${delegate}") //delegate is [1, 2, 1, 2, 3, 4]
 }

調(diào)用with方法時(shí),會將該閉包的delegate屬性設(shè)置到調(diào)用with的對象上昌妹,delegate捶枢,會負(fù)責(zé)處理this不處理的方法握截;

尾遞歸

遞歸在輸入規(guī)模較大的情況下,容易Stack Overflow烂叔,通過尾遞歸谨胞,可解決這個(gè)問題;

  @Test
    void test4() {
        println(factorial(5,1))  // 調(diào)用
    }
    def factorial = { int number, BigInteger theFactorial ->
        number == 1 ? theFactorial :
                factorial.trampoline(number - 1, number * theFactorial)
    }.trampoline()

說明: theFactorial 表示通過此遞歸計(jì)算的部分結(jié)果
如果 number 為1蒜鸡,則返回 theFactorial的值作為結(jié)果胯努,否則調(diào)用 trampoline方法

但是:調(diào)用此尾遞歸是需要傳遞2個(gè)參數(shù),我們可以將第二個(gè)參數(shù)設(shè)置一個(gè)默認(rèn)值逢防,但還是不能確保調(diào)用方隨便傳參叶沛,這樣,可封裝個(gè)方法如下:

  // 這樣外界就不能修改 theFactorial 了
   def factorial(int n) {
        def inner_factorial
        inner_factorial = { int number, BigInteger theFactorial = 1 ->
            number == 1 ? theFactorial :
                    inner_factorial.trampoline(number - 1, number * theFactorial)
        }.trampoline()
        inner_factorial(n)
    }

?問題忘朝,如果使用尾遞歸實(shí)現(xiàn)漢諾塔程序?

def hannuo(n, from, to, helper) {
        if (n == 0) {
            return
        } else {
            hannuo(n - 1, from, helper, to)  // 把n-1個(gè)從from挪到to灰署,借助helper
            print("$from->$to,")             // 把第n個(gè)盤子直接從from挪到to
            hannuo(n - 1, helper, to, from)  // //把n-1個(gè)從helper挪到from上,借助to
        }
    }

尾遞歸使用例子

賣桿問題(《Groovy程序設(shè)計(jì)》一書中的例子)
不同長度的桿局嘁,零售價(jià)格不一樣溉箕,求如何分割桿?

最后編輯于
?著作權(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)店門贫橙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贪婉,“玉大人,你說我怎么就攤上這事卢肃∑S兀” “怎么了?”我有些...
    開封第一講書人閱讀 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)容