Skip to content

Tour:higher-order-functions for zh-cn #1155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 14, 2018
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 96 additions & 1 deletion _zh-cn/tour/higher-order-functions.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
layout: tour
title: Higher-order Functions
title: 高阶函数

discourse: false

Expand All @@ -13,3 +13,98 @@ language: zh-cn
next-page: nested-functions
previous-page: mixin-class-composition
---

高阶函数是指使用其他函数作为参数、或者返回一个函数作为结果的函数。在Scala中函数是一等类型值,所以允许定义高阶函数。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-在Scala中函数是一等类型值,所以允许定义高阶函数
+Scala中的函数也是这样的“一等公民”,故亦可定义高阶函数

这里的术语可能有点让人困惑:使用函数作为参数,或者返回值为函数的“函数”和“方法”,我们均称之为“高阶函数”。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

原文是一个段落,不知道这里另起一行有没有影响,会不会在两句话之间加一个空格。

这里的术语可能有点让人困惑,我们约定,使用函数值作为参数,或者返回值为函数值的“函数”和“方法”,均称之为“高阶函数”。

在《Pragmatic Scala》,将一个函数作为一个值来使用的时候,都是用 function value 的,这里建议翻译成 函数值 。这样就不那么绕口。

后面我提一个PR,去建议改一下英文原文。


一个最简单的例子是Scala中对集合的高阶函数`map`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-一个最简单的例子是Scala中对集合的高阶函数`map`
+最常见的高阶函数的例子就是`map`函数,在Scala中它用来操作集合

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map is a member method of Scala collections, your translation seems like map is a function applies to Scala collections, maybe a little confusing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked again, and you're right. However, can you rethink the "对集合的高阶函数".

Copy link
Member Author

@declan94 declan94 Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made a new commitment, in which I wrote "最常见的一个例子是Scala中容器类(Collections)的高阶函数map" for here.

```
val salaries = Seq(20000, 70000, 40000)
val doubleSalary = (x: Int) => x * 2
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
```
函数`doubleSalary`有一个整型参数`x`,返回`x * 2`。一般来说,在`=>`左边的元组是函数的参数列表,而在右边的表达式是函数的返回值。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-而在右边的表达式是函数的返回值
+而右边表达式的值则为函数的返回值

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion, thx!

在第3行,函数`doubleSalary`被应用在数组`salaries`中的每一个元素。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-在第3行,函数`doubleSalary`被应用在数组`salaries`中的每一个元素
+在第3行,函数`doubleSalary`被应用在列表`salaries`中的每一个元素


为了简化压缩代码,我们可以使用匿名函数,直接作为参数传递给`map`:
```
val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
```
注意在上述示例中`x`没有被显式声明为Int类型,这是因为编译器能够根据map函数期望的类型推断出`x`的类型。对于上述代码,一种更惯用的写法为:
```tut
val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(_ * 2)
```
既然Scala编译器已经知道了参数的类型(一个单独的Int),你可以只给出函数的右半部分,不过需要使用`_`来替代唯一的参数名(在上一个例子中是`x`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-不过需要使用`_`来替代唯一的参数名
+唯一要注意的是需要使用`_`来替代参数名

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your advice. But I don't think the translation should follow the original text word by word and my translation seems to be more concise.

Copy link
Member

@dongxuwang dongxuwang Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the first half of your concise version. While you're not supposed to put "唯一的" there, because it sounds like "only the single parameter functions can be replaced by _". Multi-parameters ones can be after all, like reduce(_ + _). How do you think? 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, thx~ Actually I did think only single parameter functions can replace its parameter by '_' 😅Have checked, I was wrong.


## 强制转换方法为函数
你同样可以传入一个对象方法作为高阶函数的参数,这是因为Scala编译器会将方法强制转换为一个函数。
```
case class WeeklyWeatherForecast(temperatures: Seq[Double]) {

private def convertCtoF(temp: Double) = temp * 1.8 + 32

def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
}
```
在这个例子中,方法`convertCtoF`被传入`forecastInFahrenheit`。
这是可以的,因为编译器强制将方法`convertCtoF`转成了函数`x => convertCtoF(x)`
(注: `x`是编译器生成的变量名,保证在其作用域是唯一的)。

## 接收函数作为参数的函数
使用高阶函数的一个原因是减少冗余的代码。比如说你想要写一个函数,提高一个人的工资。如果没有高阶函数,代码可能像这样:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-比如说你想要写一个函数,提高一个人的工资。如果没有高阶函数
+比方说需要写几个方法以通过不同方式来提升员工工资,若不使用高阶函数

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, your translation is more in line with Chinese language habits. Thanks!

```tut
object SalaryRaiser {

def smallPromotion(salaries: List[Double]): List[Double] =
salaries.map(salary => salary * 1.1)

def greatPromotion(salaries: List[Double]): List[Double] =
salaries.map(salary => salary * math.log(salary))

def hugePromotion(salaries: List[Double]): List[Double] =
salaries.map(salary => salary * salary)
}
```

注意着三个方法的差异仅仅是提升的比例不同,为了简化代码,你可以像这样把重复的代码提到一个高阶函数中:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed now, Thx


```tut
object SalaryRaiser {

private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] =
salaries.map(promotionFunction)

def smallPromotion(salaries: List[Double]): List[Double] =
promotion(salaries, salary => salary * 1.1)

def bigPromotion(salaries: List[Double]): List[Double] =
promotion(salaries, salary => salary * math.log(salary))

def hugePromotion(salaries: List[Double]): List[Double] =
promotion(salaries, salary => salary * salary)
}
```

新的方法`promotion`有两个参数,薪资列表和一个`Double => Double`的函数(输入Double参数返回Double的函数),返回薪资提升的结果。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一个类型为Double => Double的函数

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

参数类型和结果类型都是Double

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


## 返回函数的函数

有一些情况你希望生成一个函数, 比如:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-有一些情况你希望生成一个函数, 比如:
+有些情况下需要生成一个函数,比如下面这个返回函数的方法:


```tut
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
val schema = if (ssl) "https://" else "http://"
(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
}

val domainName = "www.example.com"
def getURL = urlBuilder(ssl=true, domainName)
val endpoint = "users"
val query = "id=1"
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
```

注意urlBuilder的返回类型是`(String, String) => String`,这意味着返回的匿名函数有两个String参数,返回一个String。
在这个例子中,返回的匿名函数是`(endpoint: String, query: String) => s"https://www.example.com/$endpoint?$query"`。