閉包
閉包是一段可執(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à)格不一樣溉箕,求如何分割桿?