闭包
闭包是一段可执行的代码块,类似于方法也可以传递参数;可在闭包中访问属性值,也就是说可以修改闭包作用域中的所有变量值;
简单实例
// name为闭包参数可有多个, '->' 之后为闭包执行代码体
def clos = { name -> println("${name} for better life!") } // name为闭包参数
clos.call('YY') // 执行闭包(显示调用)
clos('CC') // 省略call() (隐士调用)
单个隐藏参数 it
// 单个隐参数 it
def cols2 = {println("${it} for better life!")} // 访问it隐藏属性
cols2('YY')
闭包作为函数方法参数传递
def my_clos = { param1 -> println("Welcome ${param1}") }
def method(clos) {
def name = 'better'
clos(name) // 调用闭包
}
method(my_clos) // 迪用闭包
//=============== 特殊的语法格式:以后DSL会经常看到 ===========================
// 如果闭包是方法的最后一个参数,可将闭包写入括号外,如下:
// 闭包在实参参数的外部, '()' 也可以省略
method() { param1 -> println("Welcome ${param1}") }
// 省略括号
method { param1 -> println("Welcome ${param1}") }
// 例子:求1到100的和
println("===== 使用闭包求 1 到 100 的和 ===========")
def result = 0
1.upto(100) { result = result + it } // 最后一个形参为闭包,放到括号外
println(result)
集合与字符串
举例:each方法原型为:each(Closure closure)
,源码为:
public static Iterator each(Iterator 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个参数, 查看api
k, v -> println "key:$k, value:$v"
}
}
常用的一些闭包方法:
- find(): 找到就返回;
- findAll(): 找所有满足闭包条件的;
- any(): 只要一个满足闭包条件就返回true,否则false;
- every():必须每个item满足闭包条件才返回true,否则false;
collect与inject
collect: 闭包变更,返回新集合;
inject:传入初始值,遍历集合,不断与后一个累计处理
collect的简单用法:
println([1, 2, 3, 4].collect { it * 2 }) // 变更 (2,4,6,8)
collect的高级用法:map 转换的例子
@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 转换方法
* @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}")
闭包的其他特性
闭包是一种对象,所以可以作为方法的参数,如下:
闭包作为方法的参数
@Test
void test3() {
def isEven = { item -> item % 2 == 0 }
def isOdd = { item -> !isEven(item) }
println(filter([1, 2, 3, 4], isEven))
}
/**
* 闭包作为方法的参数
* @param list
* @param clos
*/
def filter(list, clos) {
return list.findAll(clos)
}
闭包作为另一个闭包的参数
@Test
void test4() {
// 闭包1
def isEven = { item -> item % 2 == 0 }
// 闭包2,使用闭包1作为参数
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..
闭包的更多信息
闭包与方法
// 闭包
def twice = { n -> return 2 * n }
// 方法
def twice(n) { return 2 * n }
默认参数,与方法类型
// 默认参数
def greetting = { msg, name = 'better' -> println("${msg} ---> ${name}") }
println(greetting("Hello"))
return 语句
闭包中return的语法比较重要,如:为确定某个item是否在某个列表之中,通常使用 find()方法;
如下:
def hasMemberA(item, list) {
def size = list.size()
for (index in 0..
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)}") // 永远false
hasMemberB()
方法之所以返回false,我们看看each闭包的实现,迭代器被用于检遍历列表中对象,并调用闭包;示例中的实参的闭包语句,实际上是在闭包中执行的,跟方法没有关系,这样while循环一直执行,知道遍历完毕,所以hasMembersB()
方法返回了false;
public static Iterator each(Iterator 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个属性,用于确定由哪个对象处理该闭包内的方法调用;
一般情况下,delegate会设置owner,但可对其进行修改,发挥闭包的一些其他作用;
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
从输出可以看到,闭包被创建成了内部类,在这里,this
owner
delegate
指向同一对象;
闭包内方法调用的顺序为先 this,this无法处理,再 owner,再delegate;
修改闭包delegate例子(这个例不咋的)
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() // 设置代理
closure()
}
}
def f1() {
println("f1 of Script called...")
}
@Test
void test3() {
new Example().foo() {
f1()
f2()
}
}
输出:
f1 of Script called...
f2 of Handler called...
以上代码中,闭包内的方法调用首先被路由到闭包上下文对象this
,没找到,则路由到delegate
上下文 with 方法
Groovy支持创建一个上下文(context),在with的作用域内调用的任何方法,都会被定向到该上下文对象,如下:
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]
}
调用with
方法时,会将该闭包的delegate属性设置到调用with的对象上,delegate,会负责处理this不处理的方法;
尾递归
递归在输入规模较大的情况下,容易Stack Overflow,通过尾递归,可解决这个问题;
@Test
void test4() {
println(factorial(5,1)) // 调用
}
def factorial = { int number, BigInteger theFactorial ->
number == 1 ? theFactorial :
factorial.trampoline(number - 1, number * theFactorial)
}.trampoline()
说明: theFactorial 表示通过此递归计算的部分结果
如果 number 为1,则返回 theFactorial的值作为结果,否则调用 trampoline方法
但是:调用此尾递归是需要传递2个参数,我们可以将第二个参数设置一个默认值,但还是不能确保调用方随便传参,这样,可封装个方法如下:
// 这样外界就不能修改 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)
}
?问题,如果使用尾递归实现汉诺塔程序?
def hannuo(n, from, to, helper) {
if (n == 0) {
return
} else {
hannuo(n - 1, from, helper, to) // 把n-1个从from挪到to,借助helper
print("$from->$to,") // 把第n个盘子直接从from挪到to
hannuo(n - 1, helper, to, from) // //把n-1个从helper挪到from上,借助to
}
}
尾递归使用例子
卖杆问题(《Groovy程序设计》一书中的例子)
不同长度的杆,零售价格不一样,求如何分割杆?