fun <A, B, C> compose(f: (A) -> B, g: (B) -> C): (A) -> C = {
g(f(it))
}
infix fun <A, B, R> ((A) -> B).andThen(f: (B) -> R): (A) -> R = fun(a) = f(this(a))
这是谁?它想干啥?
先一个一个来看:
fun <A, B, C> compose(f: (A) -> B, g: (B) -> C): (A) -> C = {
g(f(it))
}
对于compose
函数,接收两个参数f
、g
,返回另外一个函数。
而f
和g
这个两个参数都是函数类型:
f
函数接收一个A
类型参数,返回一个B
类型;
g
函数接收一个B
类型参数,返回一个C
类型。
compose
的返回值也是一个函数类型,接收一个A
类型参数,返回一个C
类型。
compose
的方法体被省略,因为是一个表达式,可以使用"="赋值。而对于{g(f(it))}
表达式,使用下面的方式应更加清晰:
fun <A, B, C> compose(g: (A) -> B, f: (B) -> C): (A) -> C {
return { a: A ->
val b:B = g(a)
val c:C = f(b)
c
}
}
在compose
函数体中:
1. 执行`g`函数返回一个`B`类型的变量`b`,
2. 把`b`变量作为`f`函数的参数返回一个`C`类型的变量`c`
3. 再返回这个变量`c`。
整个方法做了两件事:把A
通过g
函数转换成B
;在把B
通过f
函数转换成C
。
这就叫做组合函数,把两个函数组合起来,返回一个组合后的函数。
当函数g
的返回值是函数f
的参数时,可以把这两个函数组合成一个新的函数。这个新函数的参数是函数g
的参数,返回值是函数f
的返回值。
最难理解的应该是返回值是一个函数,但是想想高阶函数的定义,应该就清晰多了。
当然,这只是入门级的定义,我们可以在表达式中进行任意的转换(只要存在这种转换方式),因此,g
和f
的参数与返回值类型不一定需要有显示的关联。此处不再细说。
而如果使用内联函数来定义,需要禁止f
和g
两个函数的局部return
功能:
inline fun <A, B, C> compose(crossinline f: (A) -> B, crossinline g: (B) -> C): (A) -> C = {
g(f(it))
}
还可以使用java
的方式来定义,以面向对象的思维来考虑,这种方试应该比较容易理解:
public static <A, B, C> Function<A, C> compose(Function<A, B> f, Function<B, C> g) {
return a -> g.apply(f.apply(a));
}
要怎么使用呢?
【案例】获取一个字符的长度,然后判断这个长度是否是偶数。
val result = compose(String::length) { it % 2 == 0 }("abcd")
compose
函数返回的是一个函数,因此我们可以执行这个函数得到结果。
这种方式可能也比较难理解,或许拆开更容易理解:
val compose = compose<String, Int, Boolean>({ it.length }, { it % 2 == 0 })
val result = compose("abcd")
还可以使用函数引用的方式:
fun main() {
fun even(a: Int) = a % 2 == 0
val result = compose(String::length, ::even)("abcd")
}
对于andThen
这个函数就更有意思了:
infix fun <A, B, R> ((A) -> B).andThen(f: (B) -> R): (A) -> R = fun(a) = f(this(a))
它是一个扩展函数,还是(A)->B
这个函数类型的扩展,甚至还是一个中缀函数。
把它转换成下面的方式应该就清晰多了:
infix fun <A, B, R> ((A) -> B).andThen(f: (B) -> R): (A) -> R {
return { a: A ->
val b: B = this(a)
val r: R = f(b)
r
}
}
andThen
返回一个(A)->R
类型的函数,在方法体中没有做其它操作,直接返回了这个类型。
this(a)
又是什么呢?
andThen
是(A)->B
类型的函数的扩展,因此方法体中的this
就表示(A)->B
这个函数对象。
this(a)
就表示调用了(A)->B
这个函数对象,它返回一个B
类型的值b
;
再把b
代入参数f
中返回一个R
类型的r
;
再把这个r
返回。
整个执行流程跟compose
函数是一样的,都是先执行某个函数,根据这个函数的返回值来执行另一个函数。
compose
函数跟andThen
函数的作用也是一样的,只是调用方式不一样。
那它是怎么简化来的呢?
最初的方式如下,它返回一个匿名函数:
infix fun <A, B, R> ((A) -> B).andThen(f: (B) -> R): (A) -> R {
return fun(a: A): R {
val b = this(a)
return f(b)
}
}
把这个局部函数简化一下:
//1. 合并匿名函数体中的函数调用,这个匿名函数的参数类型也可以省略
infix fun <A, B, R> ((A) -> B).andThen(f: (B) -> R): (A) -> R {
return fun(a): R {
return f(this(a))
}
}
//2. 但表达式的函数体用=表示
infix fun <A, B, R> ((A) -> B).andThen(f: (B) -> R): (A) -> R {
return fun(a) = f(this(a))
}
此时andThen
函数体还是一个单表达式,还能够合并:
infix fun <A, B, R> ((A) -> B).andThen(f: (B) -> R): (A) -> R = fun(a) = f(this(a))
甚至还可以使用返回类型推断,但此时局部函数的参数类型不能省略:
infix fun <A, B, R> ((A) -> B).andThen(f: (B) -> R) = fun(a: A) = f(this(a))
还可以不用匿名函数,使用lambda的形式,这看起来就跟compose
一样了:
inline infix fun <A, B, R> ((A) -> B).andThen(crossinline f: (B) -> R): (A) -> R = {
f(this(it))
}
还是以【获取一个字符的长度,然后判断这个长度是否是偶数】为例:
fun main() {
val result = { str: String -> str.length }.andThen{ length: Int -> length % 2 == 0 } ("abcd")
}
看起来不是很直观,美化一下,把这两个lambda提取出来:
fun main() {
val length: (String) -> Int = { it.length }
val even: (Int) -> Boolean = { it % 2 == 0 }
val result = length.andThen(even)("abcd")
}
还可以使用中缀表达式的方式:
fun main() {
val length: (String) -> Int = { it.length }
val even: (Int) -> Boolean = { it % 2 == 0 }
val result = (length andThen even)("abcd")
}
又或者使用局部函数来定义,然后使用函数引用的方式:
fun main() {
fun length(s: String) = s.length
fun even(i: Int) = i % 2 == 0
val result = (::length andThen ::even)("abcd")
}
如果考虑让菜鸟看不懂,还可以使用匿名函数的形式:
val result = ((fun(s: String) = s.length) andThen (fun(i: Int) = i % 2 == 0))("abcd")
可以看出,kotlin确实可以为所欲为。
在java中,也有andThen
这个函数,它被定义在Function
接口中:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (t) -> {
return after.apply(this.apply(t));
};
}
既然andThen
都有了,那又怎么少的了beforeAnd
呢?
infix fun <A, B, R> ((B) -> R).beforeAnd(f: (A) -> B): (A) -> R = {
this(f(it))
}
网友评论