Skip to content

Commit fc28af6

Browse files
committed
[revision 13](截至 Runnable)
1 parent 4cae1fc commit fc28af6

File tree

1 file changed

+15
-14
lines changed

1 file changed

+15
-14
lines changed

docs/book/13-Functional-Programming.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

88
> 函数式编程语言操纵代码片段就像操作数据一样容易。 虽然 Java 不是函数式语言,但 Java 8 Lambda 表达式和方法引用 (Method References) 允许你以函数式编程。
99
10-
在计算机时代早期,内存是稀缺和昂贵的。几乎每个人都用汇编语言编程。人们对编译器有所了解,但仅仅想到编译生成的代码肯定会比手工编码多了很多字节
10+
在计算机时代早期,内存是稀缺和昂贵的。几乎每个人都用汇编语言编程。人们对编译器有所了解,但仅仅想到编译生成的代码肯定会比手工编码多很多字节
1111

12-
通常,只是为了使程序适合有限的内存,程序员通过修改内存中的代码来保存代码空间,以便在程序执行时执行不同的操作。这种技术被称为**自修改代码** (self-modifying code)。只要程序足够小,少数人可以维护所有棘手和神秘的汇编代码,你就可以让它运行起来。
12+
通常,只是为了使程序适合有限的内存,程序员通过修改内存中的代码来节省代码空间,以便在程序执行时执行不同的操作。这种技术被称为**自修改代码** (self-modifying code)。只要程序足够小,少数人可以维护所有棘手和神秘的汇编代码,你就可以让它运行起来。
1313

14-
随着内存和处理器变得更便宜、更快。C 语言出现并被大多数汇编程序员认为更“高级”。人们发现使用 C 可以显著提高生产力。同时,创建自修改代码仍然不难。
14+
随着内存和处理器变得更便宜、更快。C 语言出现并被大多数汇编程序员认为更“高级”。人们发现使用 C 可以显著提高生产力。同时,使用 C 创建自修改代码仍然不难。
1515

1616
随着硬件越来越便宜,程序的规模和复杂性都在增长。这一切只是让程序工作变得困难。我们想方设法使代码更加一致和易懂。使用纯粹的自修改代码造成的结果就是:我们很难确定程序在做什么。它也难以测试:除非你想一点点测试输出,代码转换和修改等等过程?
1717

@@ -25,7 +25,7 @@ OO(object oriented,面向对象)是抽象数据,FP(functional programm
2525

2626
纯粹的函数式语言在安全性方面更进一步。它强加了额外的约束,即所有数据必须是不可变的:设置一次,永不改变。将值传递给函数,该函数然后生成新值但从不修改自身外部的任何东西(包括其参数或该函数范围之外的元素)。当强制执行此操作时,你知道任何错误都不是由所谓的副作用引起的,因为该函数仅创建并返回结果,而不是其他任何错误。
2727

28-
更好的是,“不可变对象和无副作用”范例解决了并发编程中最基本和最棘手的问题之一(当程序的某些部分同时在多个处理器上运行时)。这是可变共享状态的问题,这意味着代码的不同部分(在不同的处理器上运行)可以尝试同时修改同一块内存(谁赢了?没人知道)。如果函数永远不会修改现有值但只生成新值,则不会对内存产生争用,这是纯函数式语言的定义。 因此,经常提出纯函数式语言作为并行编程的解决方案(还有其他可行的解决方案)。
28+
更好的是,“不可变对象和无副作用”范式解决了并发编程中最基本和最棘手的问题之一(当程序的某些部分同时在多个处理器上运行时)。这是可变共享状态的问题,这意味着代码的不同部分(在不同的处理器上运行)可以尝试同时修改同一块内存(谁赢了?没人知道)。如果函数永远不会修改现有值但只生成新值,则不会对内存产生争用,这是纯函数式语言的定义。 因此,经常提出纯函数式语言作为并行编程的解决方案(还有其他可行的解决方案)。
2929

3030
需要提醒大家的是,函数式语言背后有很多动机,这意味着描述它们可能会有些混淆。它通常取决于各种观点:“为并行编程”,“代码可靠性”和“代码创建和库复用”。[^1] 同时,函数式编程的参数能帮助程序员创建更快更健壮的代码 —— 部分仍然只是假设。虽然已有一些好的范例[^2],但我们还不能证明纯函数式语言就是解决编程问题的最佳方法。
3131

@@ -102,36 +102,36 @@ Hello
102102
Hello there Hello there
103103
```
104104

105-
**Strategy** 接口提供了单一 `approach()` 方法来承载函数式功能。通过实现不同的 **Strategy** 对象,我们可以创建不同的行为。
105+
**Strategy** 接口提供了单一的 `approach()` 方法来承载函数式功能。通过创建不同的 **Strategy** 对象,我们可以创建不同的行为。
106106

107107
传统上,我们通过创建一个实现 **Strategy** 接口的类来实现此行为,比如在 **Soft**
108108

109109
- **[1]****Strategize** 中,**Soft** 作为默认策略,在初始化构造函数中赋值的。
110110

111-
- **[2]** 一种略显冗长且更自发的方法是创建一个**匿名内部类**。即使这样,仍有相当数量的冗余代码。你总是要仔细观察:“哦,原来这样,这里使用了匿名内部类。”
111+
- **[2]** 一种略显简短且更自发的方法是创建一个**匿名内部类**。即使这样,仍有相当数量的冗余代码。你总是要仔细观察:“哦,原来这样,这里使用了匿名内部类。”
112112

113113
- **[3]** Java 8 的 Lambda 表达式。由箭头 `->` 分隔开参数和函数体,箭头左边是参数,箭头右侧是从 Lambda 返回的表达式,即函数体。这实现了与定义类、匿名内部类相同的效果,但代码少得多。
114114

115115
- **[4]** Java 8 的**方法引用**,由 `::` 区分。在 `::` 的左边是类或对象的名称,在 `::` 的右边是方法的名称,但没有参数列表。
116116

117117
- **[5]** 在使用默认的 **Soft** **strategy** 之后,我们逐步遍历数组中的所有 **Strategy**,并使用 `changeStrategy()` 方法将每个 **Strategy** 放入 变量 `s` 中。
118118

119-
- **[6]** 现在,每次调用 `communic()` 都会产生不同的行为,具体取决于此刻正在使用的策略**代码对象**。我们传递的是行为,而非仅数据。[^3]
120-
121-
在 Java 8 之前,我们能够通过 **[1]****[2]** 的方式传递功能。然而,它的读写语法非常笨拙,并且我们别无选择。方法引用和 Lambda 表达式的出现让我们可以在需要时**传递功能**,而不是仅在必要才这么做。
119+
- **[6]** 现在,每次调用 `communicate()` 都会产生不同的行为,具体取决于此刻正在使用的策略**代码对象**。我们传递的是行为,而非仅数据。[^3]
122120

121+
在 Java 8 之前,我们能够通过 **[1]****[2]** 的方式传递功能。然而,这种语法的读写非常笨拙,并且我们别无选择。方法引用和 Lambda 表达式的出现让我们可以在需要时**传递功能**,而不是仅在必要才这么做。
123122

124123
<!-- Lambda Expressions -->
124+
125125
## Lambda表达式
126126

127127

128128
Lambda 表达式是使用**最小可能**语法编写的函数定义:
129129

130130
1. Lambda 表达式产生函数,而不是类。 在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类,因此在幕后执行各种操作使 Lambda 看起来像函数 —— 但作为程序员,你可以高兴地假装它们“只是函数”。
131131

132-
2. Lambda 语法尽可能多,这正是为了使 Lambda 易于编写和使用。
132+
2. Lambda 语法尽可能少,这正是为了使 Lambda 易于编写和使用。
133133

134-
我们在 `Strategize.java` 中看到了一个 Lambda 表达式,但还有其他语法变体:
134+
我们在 **Strategize.java** 中看到了一个 Lambda 表达式,但还有其他语法变体:
135135

136136
```java
137137
// functional/LambdaExpressions.java
@@ -255,7 +255,7 @@ public class RecursiveFactorial {
255255

256256
这里,`fact` 是一个静态变量。 注意使用三元 **if-else**。 递归函数将一直调用自己,直到 `i == 0`。所有递归函数都有“停止条件”,否则无限递归并报异常。
257257

258-
我们可以将 `Fibonacci` 序列改为递归 Lambda 表达式来实现,这次使用实例变量:
258+
我们可以将 `Fibonacci` 序列改为使用递归 Lambda 表达式来实现,这次使用实例变量:
259259

260260
```java
261261
// functional/RecursiveFibonacci.java
@@ -295,9 +295,10 @@ public class RecursiveFibonacci {
295295
55
296296
```
297297

298-
`Fibonacci` 序列对中的最后两个元素求和来产生下一个元素
298+
`Fibonacci` 序列中的最后两个元素求和来产生下一个元素
299299

300300
<!-- method references-->
301+
301302
## 方法引用
302303

303304

@@ -366,7 +367,7 @@ Help!
366367

367368
**[3]** `hello()` 也符合 `call()` 的签名。
368369

369-
**[4]** 就像是 `help()`,静态内部类中的非静态方法
370+
**[4]** `help()` 也符合,它是静态内部类中的非静态方法
370371

371372
**[5]** `assist()` 是静态内部类中的静态方法。
372373

0 commit comments

Comments
 (0)