|
@@ -1,20 +1,24 @@
|
|
|
|
|
|
-2021/12
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+2022/01
|
|
|
|
|
|
### 函数式编程笔记
|
|
|
|
|
|
-- 函数式编程主要解决的问题(通过历史预测未来)
|
|
|
+- 函数式编程主要解决的问题
|
|
|
|
|
|
- 代码复杂度
|
|
|
- - 线程管理
|
|
|
- - 迭代
|
|
|
+ - 线程管理、迭代
|
|
|
- 分布式计算颗粒度
|
|
|
-
|
|
|
+
|
|
|
- 编程语言是工具(简化程序复杂度)模块化问题
|
|
|
|
|
|
- 软件开发的工具,团队协作软件开发的工具
|
|
|
- 使用工具到顶了,功能复杂度hold人脑不住了,就要换新工具(人脑几十年的时间不会有什么进化)
|
|
|
- - 应用软件开发:结构化编程(模块化) -> 面向对象 -> 函数式编程;可以看到这个路线就是对“**状态**”的限制使用之路
|
|
|
+ - 应用软件开发:通过历史看未来,结构化编程(模块化) -> 面向对象 -> 函数式编程;可以看到这个路线就是**对“状态”的限制**使用之路
|
|
|
|
|
|
|
|
|
|
|
@@ -148,7 +152,7 @@
|
|
|
|
|
|
- 当我们知道有些代数结构具有一些很好的性质,满足某些定律。那如果我们写的程序具有这些代数结构,则这些程序也自然就具有同样的性质,满足同样的定律,可以应用同样的规则。例如**幺半群**![[公式]](https://www.zhihu.com/equation?tex=%28M%2C+id%2C+%5Coplus%29)满足结合律,即![[公式]](https://www.zhihu.com/equation?tex=a+%5Coplus+%28b+%5Coplus+c%29+%3D+%28a+%5Coplus+b%29+%5Coplus+c),也就是和结合的次序无关。我们知道列表类型 [a] 是一个幺半群,当我们将列表[a1, a2, a3, a4]内的所有元素相加时,我们可以先计算b = a1 + a2, c = a3 + a4,然后再计算sum = b + c。这样在多核CPU的情况下,我们可以让CPU1计算b = a1 + a2,让CPU2计算c = a3 + a4,再让CPU1计算最后的结果sum = b + c。这样我们就实现了4个元素的并行求和运算,有n个元素的求和运算也可按这种方式并行化。若我们的另一个数据类型也同样具有幺半群的代数结构,则这个数据类型的![[公式]](https://www.zhihu.com/equation?tex=%5Coplus)运算同样具有并行计算的能力。范畴论在这方面则起到了重要的理论指导作用,我们可以将范畴论中得到的范畴的代数性质和定律应用到函数式编程中,通过类型系统以新的方式来构造我们的程序。
|
|
|
|
|
|
- - 柯里化和反柯里化是一对**同构变换**,其变换得到的函数是唯一的。也可以将柯里化和反柯里化看成是普通函数和高阶函数之间的变换,柯里化具有升阶的作用,而反柯里化则具有降阶的作用。
|
|
|
+ - 柯里化和反柯里化是一对**同构变换**,其变换得到的函数是唯一的。也可以将柯里化和反柯里化看成是普通函数和高阶函数之间的变换,柯里化具有升阶的作用,而反柯里化则具有降阶的作用。这样单参数函数和多参数函数在参数形式上**就没有区别了**,具有统一的形式,从而可以放在同一个列表中,也可以串在一起。
|
|
|
|
|
|
- 范畴论是从拓扑学这一数学分支发展出来的一门抽象的数学理论,后来成为一个独立的分支,并成为了其他数学分支的基础。再后来,和**类型理论**结合,成为了**编程语言理论的数学基础**,因此我们才会在这里来介绍范畴论。但是,学习范畴论却是不需要学习拓扑学也是可以的,学习编程也是可以不学习范畴论。
|
|
|
|
|
@@ -197,31 +201,32 @@
|
|
|
|
|
|
- **函子(Functor)**
|
|
|
|
|
|
- - 函子是范畴之间的map关系。可以理解为范畴之间的态射。
|
|
|
- - 图中,函数`f`完成值的转换(`a`到`b`),将它传入函子,就可以实现范畴的转换(`Fa`到`Fb`)。
|
|
|
+ - 函子是范畴之间的关系。可以理解为范畴之间的态射。
|
|
|
+ - 图中,函数`f`完成object的转换(`a`到`b`),将它传入函子,就可以实现范畴的转换(`Fa`到`Fb`)。
|
|
|
- ![bg2017022211](./img_readbook/bg2017022211.jpg)
|
|
|
- - 具有`map`方法的数据结构,都可以当作函子的实现。代码中,`Functor`是一个函子,它的`map`方法接受函数`F`作为参数,然后返回一个新的函子,里面包含的值是被`F`处理过的(`F(this.val)`)。
|
|
|
- 范畴之间的态射,得到函子,由函子之间的态射,得到自然变换
|
|
|
- 函子 F 将简单类型a 构造为复杂类型 F a,将简单类型的函数 f 变换为复杂类型的函数 F f;**类型构造子只有同时变换类型和函数,且满足函子定律时,才是一个函子。函子的用处**是将简单类型的函数通过类型构造子提升为复杂类型的函数,且不需要写额外的代码,使得一些基本函数可以应用到多种复杂数据类型的成员中,提高了这些基本函数的**重用性**。
|
|
|
-
|
|
|
- - Maybe 函子
|
|
|
+- Maybe 函子
|
|
|
- Either 函子(运算`if...else`)
|
|
|
- ap函子,函子里面包含的值,完全可能是函数。我们可以想象这样一种情况,一个函子的值是数值,另一个函子的值是函数。
|
|
|
- 协变函子 F:C→D
|
|
|
-
|
|
|
- - 对于每一个 C 中的对象x,对应一个 D 中的F(x)。
|
|
|
+
|
|
|
+ - 对于每一个 C 中的对象x,对应一个 D 中的F(x)。
|
|
|
- 对于每一个C中的态射f:x→y,对应D的态射F(f):F(x)→F(y)。
|
|
|
- 逆变函子
|
|
|
-
|
|
|
- - 和协变函子类似,只不过态射的方向是从D到C。
|
|
|
+
|
|
|
+ - 和协变函子类似,只不过态射的方向是从D到C。
|
|
|
- 自然转换(Natural transformation)
|
|
|
- 自然转换是两个函子之间的关系。函子描述“自然构造(natural constructions)”,自然转换描述两个这样构造的"自然同态(natural homomorphisms)"。
|
|
|
+ -
|
|
|
|
|
|
|
|
|
|
|
|
+ - 编程实践
|
|
|
+
|
|
|
- Map-Reduce-Filter 模式
|
|
|
|
|
|
- - Continuation 顺延
|
|
|
+ - Continuation 延续
|
|
|
|
|
|
- ```java
|
|
|
System.out.println("Please enter your name: ");
|
|
@@ -651,9 +656,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
- - 例子
|
|
|
-
|
|
|
+- 例子
|
|
|
|
|
|
- ```java
|
|
|
abstract class MessageHandler {
|
|
@@ -681,6 +684,9 @@
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
- ```java
|
|
|
class MessageHandler {
|
|
@@ -833,6 +839,7 @@
|
|
|
|
|
|
```
|
|
|
|
|
|
+
|
|
|
- ```java
|
|
|
@Configuration
|
|
|
public class RouterConfig {
|
|
@@ -848,123 +855,23 @@
|
|
|
|
|
|
```
|
|
|
|
|
|
- - 再来一个例子
|
|
|
-
|
|
|
- - ```java
|
|
|
- @Component
|
|
|
- public class UserHandler {
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private ReactiveRedisConnection connection;
|
|
|
-
|
|
|
- public Mono<ServerResponse> getTime(ServerRequest request) {
|
|
|
- return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
|
|
|
- .body(Mono.just("Now is " + new SimpleDateFormat("HH:mm:ss").format(new Date())), String.class);
|
|
|
- }
|
|
|
- public Mono<ServerResponse> getDate(ServerRequest request) {
|
|
|
- return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
|
|
|
- .body(Mono.just("Today is " + new SimpleDateFormat("yyyy-MM-dd").format(new Date())), String.class);
|
|
|
- }
|
|
|
-
|
|
|
- public Mono<ServerResponse> sendTimePerSec(ServerRequest request) {
|
|
|
- return ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
|
|
|
- .body(Flux.interval(Duration.ofSeconds(1)).map(l -> new SimpleDateFormat("HH:mm:ss").format(new Date())), String.class);
|
|
|
- }
|
|
|
+ -
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
-
|
|
|
- public Mono<ServerResponse> register(ServerRequest request) {
|
|
|
- Mono<Map> body = request.bodyToMono(Map.class);
|
|
|
- return body.flatMap(map -> {
|
|
|
- String username = (String) map.get("username");
|
|
|
- String password = (String) map.get("password");
|
|
|
- String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());
|
|
|
- return connection.stringCommands()
|
|
|
- .set(ByteBuffer.wrap(username.getBytes()), ByteBuffer.wrap(hashedPassword.getBytes()));
|
|
|
- }).flatMap(aBoolean -> {
|
|
|
- Map<String, String> result = new HashMap<>();
|
|
|
- ServerResponse serverResponse = null;
|
|
|
- if (aBoolean){
|
|
|
- result.put("message", "successful");
|
|
|
- return ServerResponse.ok()
|
|
|
- .contentType(MediaType.APPLICATION_JSON_UTF8)
|
|
|
- .body(BodyInserters.fromObject(result));
|
|
|
- }else {
|
|
|
- result.put("message", "failed");
|
|
|
- return ServerResponse.status(HttpStatus.BAD_REQUEST)
|
|
|
- .contentType(MediaType.APPLICATION_JSON_UTF8)
|
|
|
- .body(BodyInserters.fromObject(request));
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- public Mono<ServerResponse> login(ServerRequest request) {
|
|
|
- Mono<Map> body = request.bodyToMono(Map.class);
|
|
|
- return body.flatMap(map -> {
|
|
|
- String username = (String) map.get("username");
|
|
|
- String password = (String) map.get("password");
|
|
|
- return connection.stringCommands().get(ByteBuffer.wrap(username.getBytes())).flatMap(byteBuffer -> {
|
|
|
- byte[] bytes = new byte[byteBuffer.remaining()];
|
|
|
- byteBuffer.get(bytes, 0, bytes.length);
|
|
|
- String hashedPassword = null;
|
|
|
- try {
|
|
|
- hashedPassword = new String(bytes, "UTF-8");
|
|
|
- } catch (UnsupportedEncodingException e) {
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
- Map<String, String> result = new HashMap<>();
|
|
|
- if (hashedPassword == null || !BCrypt.checkpw(password, hashedPassword)) {
|
|
|
- result.put("message", "账号或密码错误");
|
|
|
- return ServerResponse.status(HttpStatus.UNAUTHORIZED)
|
|
|
- .contentType(MediaType.APPLICATION_JSON_UTF8)
|
|
|
- .body(BodyInserters.fromObject(result));
|
|
|
- } else {
|
|
|
- result.put("token", "无效token");
|
|
|
- return ServerResponse.ok()
|
|
|
- .contentType(MediaType.APPLICATION_JSON_UTF8)
|
|
|
- .body(BodyInserters.fromObject(result));
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ```
|
|
|
-
|
|
|
- - ```java
|
|
|
-
|
|
|
- @Configuration
|
|
|
- public class RouterConfig {
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private HelloWorldHandler helloWorldHandler;
|
|
|
-
|
|
|
- @Bean
|
|
|
- public RouterFunction<?> helloRouter() {
|
|
|
- return RouterFunctions.route(RequestPredicates.GET("/hello"), helloWorldHandler::helloWorld);
|
|
|
- }
|
|
|
-
|
|
|
- @Autowired
|
|
|
- private UserHandler userHandler;
|
|
|
-
|
|
|
- @Bean
|
|
|
- public RouterFunction<ServerResponse> timerRouter() {
|
|
|
- return RouterFunctions.route(RequestPredicates.GET("/time"), userHandler::getTime)
|
|
|
- .andRoute(RequestPredicates.GET("/date"), userHandler::getDate);
|
|
|
- }
|
|
|
-
|
|
|
- @Bean
|
|
|
- public RouterFunction<?> routerFunction() {
|
|
|
- return RouterFunctions.route(RequestPredicates.GET("/hello"), helloWorldHandler::helloWorld)
|
|
|
- .andRoute(RequestPredicates.POST("/register"), userHandler::register)
|
|
|
- .andRoute(RequestPredicates.POST("/login"), userHandler::login)
|
|
|
- .andRoute(RequestPredicates.GET("/times"), userHandler::sendTimePerSec);
|
|
|
- }
|
|
|
- }
|
|
|
- ```
|
|
|
-
|
|
|
- -
|
|
|
|
|
|
|
|
|
|
|
@@ -1032,7 +939,7 @@
|
|
|
- 功能独立的模块(GUI和计算逻辑)(隔离)
|
|
|
- 多资源(多I/O设备)
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
- 复杂软件系统设计的要素
|
|
|
- 表示法(编程语言,影响复杂度)(表现力,阿拉伯数字表示法)
|
|
@@ -1085,8 +992,6 @@
|
|
|
- 函数重载带来的还有两个常见的特性,**多态性和强制类型转换**;在面向对象语言中,会有继承的**子类型的多态性以及强制转换**,比如一个接受接口类型参数的方法,可以通过传递一个子类类型来调用。还有一种多态称之为**类型化参数的多态性**(通常我们称之为“泛型”)
|
|
|
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- 控制流
|
|
|
|
|
|
- 顺序执行,选择,迭代,过程抽象,递归,并发,无顺序
|