Skip to content

Commit 925b44f

Browse files
authored
Merge pull request scala#1155 from declan94/master
Tour:higher-order-functions for zh-cn
2 parents 3423ca7 + e22f219 commit 925b44f

File tree

1 file changed

+91
-1
lines changed

1 file changed

+91
-1
lines changed

_zh-cn/tour/higher-order-functions.md

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
layout: tour
3-
title: Higher-order Functions
3+
title: 高阶函数
44

55
discourse: false
66

@@ -13,3 +13,93 @@ language: zh-cn
1313
next-page: nested-functions
1414
previous-page: mixin-class-composition
1515
---
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

Comments
 (0)