1
1
---
2
2
layout : tour
3
- title : Higher-order Functions
3
+ title : 高阶函数
4
4
5
5
discourse : false
6
6
@@ -13,3 +13,93 @@ language: zh-cn
13
13
next-page : nested-functions
14
14
previous-page : mixin-class-composition
15
15
---
16
+
17
+ 高阶函数是指使用其他函数作为参数、或者返回一个函数作为结果的函数。在Scala中函数是“一等公民”,所以允许定义高阶函数。这里的术语可能有点让人困惑,我们约定,使用函数值作为参数,或者返回值为函数值的“函数”和“方法”,均称之为“高阶函数”。
18
+
19
+ 最常见的一个例子是Scala集合类(collections)的高阶函数` map `
20
+ ```
21
+ val salaries = Seq(20000, 70000, 40000)
22
+ val doubleSalary = (x: Int) => x * 2
23
+ val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
24
+ ```
25
+ 函数` doubleSalary ` 有一个整型参数` x ` ,返回` x * 2 ` 。一般来说,在` => ` 左边的元组是函数的参数列表,而右边表达式的值则为函数的返回值。在第3行,函数` doubleSalary ` 被应用在列表` salaries ` 中的每一个元素。
26
+
27
+ 为了简化压缩代码,我们可以使用匿名函数,直接作为参数传递给` map ` :
28
+ ```
29
+ val salaries = Seq(20000, 70000, 40000)
30
+ val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
31
+ ```
32
+ 注意在上述示例中` x ` 没有被显式声明为Int类型,这是因为编译器能够根据map函数期望的类型推断出` x ` 的类型。对于上述代码,一种更惯用的写法为:
33
+ ``` tut
34
+ val salaries = Seq(20000, 70000, 40000)
35
+ val newSalaries = salaries.map(_ * 2)
36
+ ```
37
+ 既然Scala编译器已经知道了参数的类型(一个单独的Int),你可以只给出函数的右半部分,不过需要使用` _ ` 代替参数名(在上一个例子中是` x ` )
38
+
39
+ ## 强制转换方法为函数
40
+ 你同样可以传入一个对象方法作为高阶函数的参数,这是因为Scala编译器会将方法强制转换为一个函数。
41
+ ```
42
+ case class WeeklyWeatherForecast(temperatures: Seq[Double]) {
43
+
44
+ private def convertCtoF(temp: Double) = temp * 1.8 + 32
45
+
46
+ def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
47
+ }
48
+ ```
49
+ 在这个例子中,方法` convertCtoF ` 被传入` forecastInFahrenheit ` 。这是可以的,因为编译器强制将方法` convertCtoF ` 转成了函数` x => convertCtoF(x) ` (注: ` x ` 是编译器生成的变量名,保证在其作用域是唯一的)。
50
+
51
+ ## 接收函数作为参数的函数
52
+ 使用高阶函数的一个原因是减少冗余的代码。比方说需要写几个方法以通过不同方式来提升员工工资,若不使用高阶函数,代码可能像这样:
53
+ ``` tut
54
+ object SalaryRaiser {
55
+
56
+ def smallPromotion(salaries: List[Double]): List[Double] =
57
+ salaries.map(salary => salary * 1.1)
58
+
59
+ def greatPromotion(salaries: List[Double]): List[Double] =
60
+ salaries.map(salary => salary * math.log(salary))
61
+
62
+ def hugePromotion(salaries: List[Double]): List[Double] =
63
+ salaries.map(salary => salary * salary)
64
+ }
65
+ ```
66
+
67
+ 注意这三个方法的差异仅仅是提升的比例不同,为了简化代码,其实可以把重复的代码提到一个高阶函数中:
68
+
69
+ ``` tut
70
+ object SalaryRaiser {
71
+
72
+ private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] =
73
+ salaries.map(promotionFunction)
74
+
75
+ def smallPromotion(salaries: List[Double]): List[Double] =
76
+ promotion(salaries, salary => salary * 1.1)
77
+
78
+ def bigPromotion(salaries: List[Double]): List[Double] =
79
+ promotion(salaries, salary => salary * math.log(salary))
80
+
81
+ def hugePromotion(salaries: List[Double]): List[Double] =
82
+ promotion(salaries, salary => salary * salary)
83
+ }
84
+ ```
85
+
86
+ 新的方法` promotion ` 有两个参数,薪资列表和一个类型为` Double => Double ` 的函数(参数和返回值类型均为Double),返回薪资提升的结果。
87
+
88
+ ## 返回函数的函数
89
+
90
+ 有一些情况你希望生成一个函数, 比如:
91
+
92
+ ``` tut
93
+ def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
94
+ val schema = if (ssl) "https://" else "http://"
95
+ (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
96
+ }
97
+
98
+ val domainName = "www.example.com"
99
+ def getURL = urlBuilder(ssl=true, domainName)
100
+ val endpoint = "users"
101
+ val query = "id=1"
102
+ val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
103
+ ```
104
+
105
+ 注意urlBuilder的返回类型是` (String, String) => String ` ,这意味着返回的匿名函数有两个String参数,返回一个String。在这个例子中,返回的匿名函数是` (endpoint: String, query: String) => s"https://www.example.com/$endpoint?$query" ` 。
0 commit comments