spark源码阅读02-柯里化函数

前言

阅读spark源码时,withscore函数到处都是,翻到底部,看到如下这样的一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private[spark] def withScope[T](
sc: SparkContext,
name: String,
allowNesting: Boolean,
ignoreParent: Boolean)(body: => T): T = {
// Save the old scope to restore it later
....
....
try {
...
...
body
} finally {
// Remember to restore any state that was modified before exiting
sc.setLocalProperty(scopeKey, oldScopeJson)
sc.setLocalProperty(noOverrideKey, oldNoOverride)
}
}

可以看到withScopre函数有两个括号,从java转过来的可能为一脸懵逼,其实这就是scala中的柯里化函数。

什么是柯里化函数

柯里化(Currying)是一种将多参数函数转换为一系列单参数函数的技术。具体来说,一个接受多个参数的函数可以通过柯里化转换为一个只接受单个参数的函数,这个函数返回一个新的函数来接受下一个参数,依次类推,直到所有参数都被提供。

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
object Currying {

/**
* 普通相加函数
* */
def add(a:Int,b:Int):Int={
println("普通函数相加正在运行")
a+b
}

/**
* 柯里化函数相加
* */
def addCurring(a:Int)(b:Int): Int = {
println("柯里化函数相加正在运行")
a+b
}

/**
* 柯里化函数的等价正规函数写法
* */
def add2(x:Int)=(y:Int)=>x+y
def main(args: Array[String]): Unit = {
println("普通相加函数结果是:")
println(add(1,2))
println("柯里化相加函数结果是:")
println(addCurring(1)(2))

val addTemp = add2(3)
val rs = addTemp(4)
println("柯里化等价函数相加结果是:")
println(rs)
}
}

运行的结果是

1
2
3
4
5
6
7
8
普通相加函数结果是:
普通函数相加正在运行
3
柯里化相加函数结果是:
柯里化函数相加正在运行
3
柯里化等价函数相加结果是:
7

本质上,柯里化函数就是一个一个函数
def add(x:Int,y:Int)={x+y}
第一步 add(x) ,返回 def (y:Int)={x+y}这样一个函数
其中y为继续传入的参数,同时在y函数中引用第一个函数的变量。

柯里化函数的好处

为什么要用柯里化函数,老老实实的用多参数不好吗?
这里可以从柯里化函数的好处来进行解答。

借用大模型,有以下好处

  1. 延迟计算:

    • 柯里化允许你延迟函数的完全执行,直到所有必需的参数都准备好为止。这在需要根据运行时情况决定何时执行完整计算时特别有用。
  2. 参数复用:

    • 当你需要多次调用同一个函数,并且其中一些参数保持不变时,柯里化可以帮助减少重复代码。通过预先绑定这些不变的参数,你可以创建一个新函数,只需要提供变化的参数即可。
  3. 提高函数的适用性和通用性:

    • 柯里化可以使函数更加通用,因为它允许你创建新的函数,这些函数具有特定的预设参数。这有助于提高代码的重用性,并使函数更容易适应不同的应用场景。
  4. 简化嵌套函数的开发:

    • 在没有柯里化的情况下,你可能需要使用嵌套函数来实现类似的功能。柯里化可以让你避免这种嵌套,从而简化代码结构。
  5. 增强函数组合能力:

    • 柯里化函数更容易与其他函数组合起来,形成更复杂的逻辑流程。这是因为柯里化函数本身就是函数,可以作为其他函数的参数或返回值。
  6. 提高代码可读性和可维护性:

    • 通过使用柯里化,你可以创建更简洁、更清晰的函数签名。这有助于提高代码的可读性,并使得维护和理解代码变得更加容易。
  7. 减少内存占用:

    • 如果函数的某些参数很少改变,那么通过柯里化预先绑定这些参数可以减少内存中函数实例的数量。
  8. 易于单元测试:

    • 柯里化函数通常更小、更简单,因此更容易进行单元测试。你可以独立地测试每个柯里化函数的部分,确保它们按预期工作。
  9. 支持函数式编程范式:

    • 在像 Haskell 和 Scala 这样的函数式编程语言中,柯里化是其基本原理之一。它支持函数式编程的核心概念,如纯函数、不可变数据等。
  10. 更好的错误处理:

    • 由于柯里化函数可以逐步提供参数,因此在参数不全或者不符合要求的情况下可以更容易地捕获错误。

spark中的解释

回到withscope函数,

1
2
3
4
5
6
private[spark] def withScope[T](
sc: SparkContext,
name: String,
allowNesting: Boolean,
ignoreParent: Boolean)(body: => T): T = {
}

主要使用了了

参数复用:
当你需要多次调用同一个函数,并且其中一些参数保持不变时,柯里化可以帮助减少重复代码。通过预先绑定这些不变的参数,你可以创建一个新函数,只需要提供变化的参数即可。

这一优点,可以看到,第一个括号里的(sc,name,allowNesting,ignoreParent)基本不变,都是预先传入的,主要变的是运行提body。