From fc28af613ee3e415d77e3eddc733af8327f061e4 Mon Sep 17 00:00:00 2001 From: xiangflight Date: Mon, 22 Jul 2019 19:37:00 +0800 Subject: [PATCH] =?UTF-8?q?[revision=2013](=E6=88=AA=E8=87=B3=20Runnable)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/13-Functional-Programming.md | 29 +++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/book/13-Functional-Programming.md b/docs/book/13-Functional-Programming.md index 4ed2ab9..648c39a 100644 --- a/docs/book/13-Functional-Programming.md +++ b/docs/book/13-Functional-Programming.md @@ -7,11 +7,11 @@ > 函数式编程语言操纵代码片段就像操作数据一样容易。 虽然 Java 不是函数式语言,但 Java 8 Lambda 表达式和方法引用 (Method References) 允许你以函数式编程。 -在计算机时代早期,内存是稀缺和昂贵的。几乎每个人都用汇编语言编程。人们对编译器有所了解,但仅仅想到编译生成的代码肯定会比手工编码多了很多字节。 +在计算机时代早期,内存是稀缺和昂贵的。几乎每个人都用汇编语言编程。人们对编译器有所了解,但仅仅想到编译生成的代码肯定会比手工编码多很多字节。 -通常,只是为了使程序适合有限的内存,程序员通过修改内存中的代码来保存代码空间,以便在程序执行时执行不同的操作。这种技术被称为**自修改代码** (self-modifying code)。只要程序足够小,少数人可以维护所有棘手和神秘的汇编代码,你就可以让它运行起来。 +通常,只是为了使程序适合有限的内存,程序员通过修改内存中的代码来节省代码空间,以便在程序执行时执行不同的操作。这种技术被称为**自修改代码** (self-modifying code)。只要程序足够小,少数人可以维护所有棘手和神秘的汇编代码,你就可以让它运行起来。 -随着内存和处理器变得更便宜、更快。C 语言出现并被大多数汇编程序员认为更“高级”。人们发现使用 C 可以显著提高生产力。同时,创建自修改代码仍然不难。 +随着内存和处理器变得更便宜、更快。C 语言出现并被大多数汇编程序员认为更“高级”。人们发现使用 C 可以显著提高生产力。同时,使用 C 创建自修改代码仍然不难。 随着硬件越来越便宜,程序的规模和复杂性都在增长。这一切只是让程序工作变得困难。我们想方设法使代码更加一致和易懂。使用纯粹的自修改代码造成的结果就是:我们很难确定程序在做什么。它也难以测试:除非你想一点点测试输出,代码转换和修改等等过程? @@ -25,7 +25,7 @@ OO(object oriented,面向对象)是抽象数据,FP(functional programm 纯粹的函数式语言在安全性方面更进一步。它强加了额外的约束,即所有数据必须是不可变的:设置一次,永不改变。将值传递给函数,该函数然后生成新值但从不修改自身外部的任何东西(包括其参数或该函数范围之外的元素)。当强制执行此操作时,你知道任何错误都不是由所谓的副作用引起的,因为该函数仅创建并返回结果,而不是其他任何错误。 -更好的是,“不可变对象和无副作用”范例解决了并发编程中最基本和最棘手的问题之一(当程序的某些部分同时在多个处理器上运行时)。这是可变共享状态的问题,这意味着代码的不同部分(在不同的处理器上运行)可以尝试同时修改同一块内存(谁赢了?没人知道)。如果函数永远不会修改现有值但只生成新值,则不会对内存产生争用,这是纯函数式语言的定义。 因此,经常提出纯函数式语言作为并行编程的解决方案(还有其他可行的解决方案)。 +更好的是,“不可变对象和无副作用”范式解决了并发编程中最基本和最棘手的问题之一(当程序的某些部分同时在多个处理器上运行时)。这是可变共享状态的问题,这意味着代码的不同部分(在不同的处理器上运行)可以尝试同时修改同一块内存(谁赢了?没人知道)。如果函数永远不会修改现有值但只生成新值,则不会对内存产生争用,这是纯函数式语言的定义。 因此,经常提出纯函数式语言作为并行编程的解决方案(还有其他可行的解决方案)。 需要提醒大家的是,函数式语言背后有很多动机,这意味着描述它们可能会有些混淆。它通常取决于各种观点:“为并行编程”,“代码可靠性”和“代码创建和库复用”。[^1] 同时,函数式编程的参数能帮助程序员创建更快更健壮的代码 —— 部分仍然只是假设。虽然已有一些好的范例[^2],但我们还不能证明纯函数式语言就是解决编程问题的最佳方法。 @@ -102,13 +102,13 @@ Hello Hello there Hello there ``` -**Strategy** 接口提供了单一 `approach()` 方法来承载函数式功能。通过实现不同的 **Strategy** 对象,我们可以创建不同的行为。 +**Strategy** 接口提供了单一的 `approach()` 方法来承载函数式功能。通过创建不同的 **Strategy** 对象,我们可以创建不同的行为。 传统上,我们通过创建一个实现 **Strategy** 接口的类来实现此行为,比如在 **Soft**。 - **[1]** 在 **Strategize** 中,**Soft** 作为默认策略,在初始化构造函数中赋值的。 -- **[2]** 一种略显冗长且更自发的方法是创建一个**匿名内部类**。即使这样,仍有相当数量的冗余代码。你总是要仔细观察:“哦,原来这样,这里使用了匿名内部类。” +- **[2]** 一种略显简短且更自发的方法是创建一个**匿名内部类**。即使这样,仍有相当数量的冗余代码。你总是要仔细观察:“哦,原来这样,这里使用了匿名内部类。” - **[3]** Java 8 的 Lambda 表达式。由箭头 `->` 分隔开参数和函数体,箭头左边是参数,箭头右侧是从 Lambda 返回的表达式,即函数体。这实现了与定义类、匿名内部类相同的效果,但代码少得多。 @@ -116,12 +116,12 @@ Hello there Hello there - **[5]** 在使用默认的 **Soft** **strategy** 之后,我们逐步遍历数组中的所有 **Strategy**,并使用 `changeStrategy()` 方法将每个 **Strategy** 放入 变量 `s` 中。 -- **[6]** 现在,每次调用 `communic()` 都会产生不同的行为,具体取决于此刻正在使用的策略**代码对象**。我们传递的是行为,而非仅数据。[^3] - -在 Java 8 之前,我们能够通过 **[1]** 和 **[2]** 的方式传递功能。然而,它的读写语法非常笨拙,并且我们别无选择。方法引用和 Lambda 表达式的出现让我们可以在需要时**传递功能**,而不是仅在必要才这么做。 +- **[6]** 现在,每次调用 `communicate()` 都会产生不同的行为,具体取决于此刻正在使用的策略**代码对象**。我们传递的是行为,而非仅数据。[^3] +在 Java 8 之前,我们能够通过 **[1]** 和 **[2]** 的方式传递功能。然而,这种语法的读写非常笨拙,并且我们别无选择。方法引用和 Lambda 表达式的出现让我们可以在需要时**传递功能**,而不是仅在必要才这么做。 + ## Lambda表达式 @@ -129,9 +129,9 @@ Lambda 表达式是使用**最小可能**语法编写的函数定义: 1. Lambda 表达式产生函数,而不是类。 在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类,因此在幕后执行各种操作使 Lambda 看起来像函数 —— 但作为程序员,你可以高兴地假装它们“只是函数”。 -2. Lambda 语法尽可能多,这正是为了使 Lambda 易于编写和使用。 +2. Lambda 语法尽可能少,这正是为了使 Lambda 易于编写和使用。 -我们在 `Strategize.java` 中看到了一个 Lambda 表达式,但还有其他语法变体: +我们在 **Strategize.java** 中看到了一个 Lambda 表达式,但还有其他语法变体: ```java // functional/LambdaExpressions.java @@ -255,7 +255,7 @@ public class RecursiveFactorial { 这里,`fact` 是一个静态变量。 注意使用三元 **if-else**。 递归函数将一直调用自己,直到 `i == 0`。所有递归函数都有“停止条件”,否则无限递归并报异常。 -我们可以将 `Fibonacci` 序列改为递归 Lambda 表达式来实现,这次使用实例变量: +我们可以将 `Fibonacci` 序列改为使用递归 Lambda 表达式来实现,这次使用实例变量: ```java // functional/RecursiveFibonacci.java @@ -295,9 +295,10 @@ public class RecursiveFibonacci { 55 ``` -将 `Fibonacci` 序列对中的最后两个元素求和来产生下一个元素。 +将 `Fibonacci` 序列中的最后两个元素求和来产生下一个元素。 + ## 方法引用 @@ -366,7 +367,7 @@ Help! **[3]** `hello()` 也符合 `call()` 的签名。 -**[4]** 就像是 `help()`,静态内部类中的非静态方法。 +**[4]** `help()` 也符合,它是静态内部类中的非静态方法。 **[5]** `assist()` 是静态内部类中的静态方法。