由于 Kotlin 支持函数式编程的许多概念,我想知道是否也有一种方法可以在 Kotlin 中部分应用函数?
部分应用有用的一个例子是:
// In one class
fun doSomething(cb: (a, b) -> Unit) {
<some logic here to compute someField>
doSomethingElse(cb.applyPartially(someField))
}
// In another class
fun doSomethingElse(cb: (b) -> Unit) {
<some logic here to compute someOtherField>
cb(someOtherField)
}
开箱即用,不。但使用辅助函数并不难:
fun add(a: Int, b:Int): Int {
return a + b
}
fun <A, B, C> partial2(f: (A, B) -> C, a: A): (B) -> C {
return { b: B -> f(a, b)}
}
val add1 = partial2(::add, 1)
val result = add1(2) //3
因此,partial2 接受一个有 2 个参数的函数和第一个参数,并应用它来获得一个有 1 个参数的函数。您必须为您需要的所有类型编写这样的帮助程序。
或者,您可以使用扩展方法来完成:
fun <A,B,C> Function2<A,B,C>.partial(a: A): (B) -> C {
return {b -> invoke(a, b)}
}
val abc: (Int) -> Int = (::add).partial(1)
库
org.funktionale
已被弃用,功能已迁移到 arrow-kt.io
。
https://arrow-kt.io/learn/collections-functions/utils/
Kotlin 有一个非常漂亮且轻便的库:
org.funktionale
。在模块 funktionale-currying
中,您将找到 lambda 的扩展方法:curried()
和 uncurried()
。
示例:
val add = { x: Int, y: Int -> x + y }.curried()
val add3 = add(3)
fun test() {
println(add3(5)) // prints 8
}
确实如此,但你必须使用类似的签名
(T1) -> (T2) -> R
这是一个允许部分应用的柯里化比较器的示例(为了可读性而添加的
typealias
,也可以重写,不带任何内容):
typealias CurriedComparator<T> = (T) -> (T) -> Int
fun main() {
val integers = mutableListOf(42, 0, 1, 3, 2)
val naturalOrder: CurriedComparator<Int> = { left ->
{ right ->
// JVM only, essentially the same as `left - right`
Comparator.naturalOrder<Int>().compare(left, right)
}
}
integers.sortWith { left, right ->
// Note the partial application:
naturalOrder(left)(right)
}
println(integers)
}
如上所述,双参数函数的
curried
操作(来自这个答案)可以这样实现:
val <T1, T2, R> ((T1, T2) -> R).curried: (T1) -> (T2) -> R
get() = { arg0: T1 ->
{ arg1: T2 ->
this(arg0, arg1)
}
}
使用示例:
val difference = { a: Int, b: Int -> a - b }.curried(11)(7)
println(difference) // 4
您必须重载
invoke()
运算符,例如。克:
val compareInts: (Int, Int) -> Int =
{ left, right ->
left - right
}
operator fun <T1, T2, R> ((T1, T2) -> R).invoke(t1: T1): (T2) -> R =
{ t2 ->
this(t1, t2)
}
// This is the "normal" invocation:
val a: Int = compareInts(41, 42)
// This is the partial application using the overloaded `invoke()` operator:
val b: Int = compareInts(41)(42)
check(a == b)
部分应用的能力是有代价的。一旦我们遵循上述路径,我们将立即失去对:
的访问权限说到没有Y组合器的递归,它仍然是可能的,但是(在2参数函数的情况下)你必须重写
val comparator: (String) -> (String) -> Int = { /*...*/ }
如
fun comparator(left: String): (String) -> Int { /*...*/ }