0%

600015.XSHG,华夏银行,银行I,银行II

基本信息

2003-09-12 上市

财务

分红融资

派现融资比0.57,不及格

估值

2024-08-14

银行有工商银行和农业银行了,融资派现比只有0.57,不合格,不分析

2024-07-22

银行有苏州银行和工商银行了,不分析

主营

从页岩气中提取石油并销售
油气销售 净利润占比 97.23%

分红

派现融资比0.3179,因为上市没几年。
分红率40%可以预期。

财务

营收

营收稳定增长,利润稳定增长。
24年中报2268亿,同比增长18%。

净利

24年中报797亿,同比增长25%。

未来经营猜想

海南页岩气扩张,石油产出增加,基于油价,低油价低利润,高油价高利润。
24年中以80元每桶的价格,超过了22年110元每桶的价格,扩张,降成本可期。

估值

2024-09-02

当前市值1.4万亿,股价28.9,总股本475.5亿。
假设全年1547,未来平均维持住该利润,分红43%,那么加权利润为 841.56亿,按5%到10%倒退,那么市值为8415.6到16831.2亿。对应股价 17.69到35.39
仓位:
有成长,质优,给与20%的仓位。

2024-08-14

按22年1238亿净利润为基准,分红率40%,那么加权现金流为 12380.4+12380.6/4=680.9亿。

按5%到10%的估值,那么估值为6809亿到13618亿。
基于确定性,给20%仓位。

601288.XSHG,农业银行,银行I,银行II

财务

营收开始放缓
净利润今年也放缓

分红融资

派现融资比2.98,优秀
20年分红647.8亿,30%
21年分红 723.8亿, 30%
22年分红 777.7亿, 30%
23年分红 808.1亿, 30%

估值

2024-08-30

当前市值1.6万,股价4.69,总股本3499.8亿。
预估未来无增长或微跌,维持净2600亿,加权现金流 2600*(0.7/5+0.3)=1144亿,按5%到10%估算,对应市值11440到22880亿,对应股价3.26到6.53
仓位给与10%
- 营收下降
- 招行,工行,农行三大航。

2024-08-14

当前市值1.6万亿,股价4.66,总股本3499.8亿。
预估未来无增长或微跌,维持净利润回到20年的2159亿,加权现金流 2159*(0.7/4+0.3)=1025.52亿,按5%到10%估算,对应市值10255.2到20510.5亿,对应股价2.93到5.86
仓位给与10%
- 因为营收净利出现了下降的情况

2024-07-22

银行已经有两个了,不分析

600971.XSHG,恒源煤电,采掘I,煤炭开采II

基本信息

2004-08-17 上市
当前市值109.8亿,股价9.15,总股本12亿。

财务

营收

24年38.93,同比下降8%

净利

24年中报7.58亿,同比下降32%。

分红

派现融资比1.38,不合格
累计分红54.88亿,近3年分红28.2亿。
20年后分红率51.38%,43.25%,47.49%,50%。

估值

2024-08-28

分红融资比1.38,比较一般。
假设回到18年到20年,利润11亿,分红40%,加权净利润为 5.72亿 按5%到10%,57.2亿到114.4亿之间。

仓位:净利下降32%,不观察。

2024-07-23

一季报下降19%
假设回到18年到20年,利润11亿,分红50%,5.5亿,按5%到10%,55亿到110亿之间。

300532.XSHE,今天国际,计算机I,IT服务II

分红融资

16年上市
派现融资比0.8,40分

估值

2024-08-14

维持,派现融资不够吸引,加上业绩下降风险,不估算。

2024-07-22

公司董事长被拘留
新增订单降速,24年大概率不会好看
风险太大,就不估值了。

601398.XSHG,工商银行,银行I,银行II

分红融资

派现融资比4.85,优秀
20年948亿 30%
21年 1045亿 30%
22年 1082亿 30%
23年 1092亿 30%

估值

2024-08-30

当前市值22000亿,股价6.23,总股本3564.1亿
利润维持住3600亿,分红维持住30%,那么就是1080亿,加权现金流为1584亿,按5%到10%股息率计算,市值为15840亿到31680亿。对应股价4.44到8.88
仓位:
由于有工商,农业,招商,给与10%的仓位。

2024-08-14

当前市值21000亿,股价6.02.
利润维持住3600亿,分红维持住30%,那么就是1080亿,加权现金流为1710亿,按5%到10%股息率计算,市值为17100亿到34200亿。对应股价4.8到9.6

确定性给与20%仓位

2024-07-22

利润维持住3600亿,分红维持住30%,那么就是1080亿,按5%到10%股息率计算,市值为10800亿到21600亿。

601577.XSHG,长沙银行,银行I,银行II

分红融资

派现融资比0.52,18年上市,及格吧。

估值

2024-08-14

银行有苏州银行和工商银行了,派现融资比0.52,不分析

2024-07-22

银行有苏州银行和工商银行了,不分析

002032.XSHE,苏泊尔,家用电器I,小家电II

分红融资

2004年8月17日上市
近5年现利比 1.11,优秀。
当前市值 416.5亿,股价 51.96,总股本 8.02亿。
派现融资比11.89,累计分红 135亿,近3年分红 83亿。
20年后分红率 56%,80%,166%,99%。

主营

营收

烹饪电器 41.74%,88.92亿,毛利 25.37%
炊具及用具28.43%,60.56亿,毛利 28.67%
实物料理电器 16.35%,34.84亿,毛利22.08%

财务

营收

19年后几乎持平,19年198亿,21年213亿。
24年一季度 53.78%已,同比增长 8.3%
24年三季度165.1亿,同比增长7.45%

净利

稳定顶账,23年21.8亿,同比增长 5.4%
24年一季度 9.4亿,同比增长 6.8%
24年三季度14.33亿,同比增长5.19%

估值

2024-11-05

当前市值432亿,股价53.9,总股本8.02亿。
增长仍在继续,建设未来净利维持住21.8亿,分红80%,未分红按20%,加权净利润为 18.31亿,由于还在低速增长,按4%到7%估算,那么市值为 261.57亿到 457.75亿。对应股价 32.61到 57.07之间

2024-08-24

当前市值 416.5亿,股价 51.96,总股本 8.02亿。

增长仍在继续,建设未来净利维持住21.8亿,分红80%,加权净利润为 18.53亿,按5%到10%估算,那么市值为 185.3亿到 370.6亿。对应股价 23.1到 46.2之间

按确定性,给与10%仓位。

2025-10-27

当前市值 379.28亿,股价 47.33,总股本 8.01亿。

q3恶化,连续两个季度恶化,营收54.2已,去年55.48亿,同比下降2.3,净利润同比下降10%+,对于消费,营收下降需要特别注意,同时应收上升,经营恶化可能只是开始。
假设未来净利维回到18年的持住18.46亿,分红80%,加权净利润为 14.768亿,按5%到10%估算,那么市值为 147.68亿到 295.36亿。对应股价 18.43到 36.87 之间
营收下降,最好再8折或6折保留空间。

按确定性,给与10%仓位。

前言

阅读spark源码时,withscope代码随处可见,
比如最简单的map

1
2
3
4
def map[U: ClassTag](f: T => U): RDD[U] = withScope {
val cleanF = sc.clean(f)
new MapPartitionsRDD[U, T](this, (_, _, iter) => iter.map(cleanF))
}

当时就有一个疑问,这个的作用是啥?

withscope 的作用

官方的注释解释

Execute the given body such that all RDDs created in this body will have the same scope.
If nesting is allowed, any subsequent calls to this method in the given body will instantiate child scopes that are nested within our scope. Otherwise, these calls will take no effect.
Additionally, the caller of this method may optionally ignore the configurations and scopes set by the higher level caller. In this case, this method will ignore the parent caller’s intention to disallow nesting, and the new scope instantiated will not have a parent. This is useful for scoping physical operations in Spark SQL, for instance.
Note: Return statements are NOT allowed in body.

机器翻译一下

执行给定的主体内容,确保在该主体内容中创建的所有 RDD 都具有相同的范围。如果允许嵌套,则在给定主体内容中的任何后续调用将会实例化嵌套在我们当前范围内的子范围。否则,这些调用将不会产生效果。 此外,调用此方法的主体可以选择忽略上级调用者设置的配置和范围。在这种情况下,此方法会忽略父级调用者禁止嵌套的意图,并且新实例化的范围将没有父级。这对于在 Spark SQL 中为物理操作设定范围是有用的。 注意:主体内容中不允许使用返回语句。

额,有点生硬,找下网上的解释

在 Apache Spark 中,withScope 主要用于构建和跟踪 RDD (Resilient Distributed Datasets) 的作用域层次结构。它被广泛应用于 Spark 的内部实现中,以记录 RDD 的操作历史和父子 lineage 关系,这对于调试、性能优化和监控非常重要。

withScope 的主要功能包括:

  1. 作用域层次结构:
    • withScope 保证了在给定的代码块内创建的所有 RDD 都具有相同的作用域。
    • 如果允许嵌套,则可以创建子作用域来表示更细粒度的操作。
  2. 操作序列记录:
    • 它记录 RDD 操作的序列,帮助追踪 RDD 的来源和转换过程。
  3. 依赖关系分析:
    • 帮助理解和分析 RDD 之间的依赖关系。
  4. Spark UI 展示:
    • 支持 Spark UI 中的可视化展示,使得用户能够看到数据流的图形表示。

其实,还是有那么点不理解,问题不大,我们先看源码。

withscope的源码

withscope到底干了啥?
传到底层后,源码如下

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
private[spark] def withScope[T](
sc: SparkContext,
name: String,
allowNesting: Boolean,
ignoreParent: Boolean)(body: => T): T = {
// Save the old scope to restore it later
val scopeKey = SparkContext.RDD_SCOPE_KEY
val noOverrideKey = SparkContext.RDD_SCOPE_NO_OVERRIDE_KEY
val oldScopeJson = sc.getLocalProperty(scopeKey)
val oldScope = Option(oldScopeJson).map(RDDOperationScope.fromJson)
val oldNoOverride = sc.getLocalProperty(noOverrideKey)
try {
if (ignoreParent) {
// Ignore all parent settings and scopes and start afresh with our own root scope
// 如果忽略父级,忽略所有父级设置和scope,从新开始一个新的根作用域
sc.setLocalProperty(scopeKey, new RDDOperationScope(name).toJson)
} else if (sc.getLocalProperty(noOverrideKey) == null) {
// Otherwise, set the scope only if the higher level caller allows us to do so
//如果允许嵌套,则仅在上级调用者允许我们时才设置作用域,其实就是把父级scope传给子级
sc.setLocalProperty(scopeKey, new RDDOperationScope(name, oldScope).toJson)
}
// Optionally disallow the child body to override our scope
// 禁止子主体覆盖范围,也就是不会更新LocalProperty
if (!allowNesting) {
sc.setLocalProperty(noOverrideKey, "true")
}
body
} finally {
// Remember to restore any state that was modified before exiting
// 代码执行完了恢复scope
sc.setLocalProperty(scopeKey, oldScopeJson)
sc.setLocalProperty(noOverrideKey, oldNoOverride)
}
}

简单来说,withscope就是往LocalProperty里存一个RDDOperationScope对象的json字符串,这个对象里包含了作用域的名字和父级作用域。

withscope的写入的东西长什么样?

我们以workcount代码为例来查看

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
35
36
37
38
39
40
41
42
43
// scalastyle:off println
package my.test

import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.{FileSystem, Path}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object day01 {
def main(args: Array[String]): Unit = {

val conf = new SparkConf()

/**
* 如果这个参数不设置,默认认为你运行的是集群模式
* 如果设置成local代表运行的是local模式
*/
conf.setMaster("local[2]")
//设置任务名
conf.setAppName("WordCount")
//创建SparkCore的程序入口
val sc = new SparkContext(conf)
//读取文件 生成RDD
val file: RDD[String] = sc.textFile("file:///D:\\git\\spark_learning\\in\\word.txt")
//把每一行数据按照,分割
val word: RDD[String] = file.flatMap(_.split(","))
//让每一个单词都出现一次
val wordOne: RDD[(String, Int)] = word.map((_, 1))
//单词计数
val wordCount: RDD[(String, Int)] = wordOne.reduceByKey(_ + _)
//按照单词出现的次数 降序排序
val sortRdd: RDD[(String, Int)] = wordCount.sortBy(tuple => tuple._2, false)
//将最终的结果进行保存
val outputDir = new Path("file:///D:/result.txt")
val fs = FileSystem.get(new Configuration())
if (fs.exists(outputDir)) {
fs.delete(outputDir, true) // true 表示递归删除目录
}
sortRdd.saveAsTextFile("file:///D:/result.txt")

sc.stop()
}
}

同时修改withscope源码,加上日志打印

1
2
3
4
5
6
7
// Optionally disallow the child body to override our scope
if (!allowNesting) {
sc.setLocalProperty(noOverrideKey, "true")
}
log.info("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
log.info(sc.getLocalProperty(scopeKey))
body

运行代码,关于日志的输出如下

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"0","name":"textFile"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO SparkContext: Created broadcast 0 from textFile at day01.scala:24
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"0","name":"textFile"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"1","name":"flatMap"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"2","name":"map"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"3","name":"reduceByKey"}
24/08/08 18:52:52 INFO FileInputFormat: Total input files to process : 1
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"3","name":"reduceByKey"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"3","name":"reduceByKey"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"4","name":"sortBy"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"4","name":"sortBy"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"4","name":"sortBy"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"4","name":"sortBy"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"4","name":"sortBy"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"4","name":"sortBy"}
24/08/08 18:52:52 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:52 INFO RDDOperationScope: {"id":"4","name":"sortBy"}
24/08/08 18:52:52 INFO SparkContext: Starting job: sortBy at day01.scala:32

24/08/08 18:52:53 INFO DAGScheduler: Job 0 finished: sortBy at day01.scala:32, took 0.736760 s
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"5","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"6","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"7","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"8","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"9","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"10","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"11","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"12","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"4","name":"sortBy"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"13","name":"saveAsTextFile"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"13","name":"saveAsTextFile"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"13","name":"saveAsTextFile"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"13","name":"saveAsTextFile"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"13","name":"saveAsTextFile"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"13","name":"saveAsTextFile"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"13","name":"saveAsTextFile"}
24/08/08 18:52:53 INFO deprecation: mapred.output.dir is deprecated. Instead, use mapreduce.output.fileoutputformat.outputdir

24/08/08 18:52:53 INFO DAGScheduler: Job 1 finished: runJob at SparkHadoopWriter.scala:83, took 0.083756 s
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"14","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"15","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"16","name":"checkpoint"}
24/08/08 18:52:53 INFO RDDOperationScope: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
24/08/08 18:52:53 INFO RDDOperationScope: {"id":"17","name":"checkpoint"}
24/08/08 18:52:53 INFO SparkHadoopWriter: Start to commit write Job job_20240808185253177739536364190249_0010.
24/08/08 18:52:53 INFO SparkHadoopWriter: Write Job

可以看到,写入的基本都很简单,一个id,一个name,无了,没有parent

为什么没有parent?

1
2
3
4
5
6
  body
} finally {
// Remember to restore any state that was modified before exiting
sc.setLocalProperty(scopeKey, oldScopeJson)
sc.setLocalProperty(noOverrideKey, oldNoOverride)
}

结合之前的源码,body函数执行结束后,就会重置scope
也就是说body函数内需要再次调用withscope才会有parent
而我们的wordcount里的map,flatmap里都不会再次调用withscope。

前言

阅读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。