|
@@ -10,21 +10,22 @@
|
|
|
- 迭代
|
|
|
- 分布式计算颗粒度
|
|
|
|
|
|
-- 编程语言是工具(简化程序复杂度)
|
|
|
+- 编程语言是工具(简化程序复杂度)模块化问题
|
|
|
|
|
|
- 软件开发的工具,团队协作软件开发的工具
|
|
|
- 使用工具到顶了,功能复杂度hold人脑不住了,就要换新工具(人脑几十年的时间不会有什么进化)
|
|
|
- - 应用软件开发:结构化编程 -> 面向对象 -> 函数式编程;可以看到这个路线就是对“**状态**”的限制使用之路
|
|
|
+ - 应用软件开发:结构化编程(模块化) -> 面向对象 -> 函数式编程;可以看到这个路线就是对“**状态**”的限制使用之路
|
|
|
|
|
|
|
|
|
|
|
|
- **莱布尼兹**(与**牛顿**)曾经有两个梦想:
|
|
|
+
|
|
|
- 创建一种“普遍语言”(universal language)使得任何问题都可以用这种语言表述;
|
|
|
- 对前一个问题的回答就是自弗雷格、罗素开始,经公理集合论运动的最终结果:以一阶谓词逻辑为语言所形式化阐述的集合论,现在已经成为数学的普遍语言,现代逻辑学、特别是将符号逻辑应用于数学领域所产生的数理逻辑,其最重要的目标就是为整个数学提供一个严格精确的语言。这是我们在学习**数理逻辑**时应当把握的方向。
|
|
|
- 找到一种"判定方法"(decision method)以解决所有可以在“普遍语言”中所表述的问题
|
|
|
- 第二个问题则是现代哲学和**计算机科学**最关注的问题
|
|
|
- 1928年提出的一个重要的数学问题:“**判定性问题**” (decision problem),所有计算问题都可以规约到相应的一个判定性问题。“任意正整数是不是素数”这个问题就是可判定的。不可判定问题:“停机问题”、“理发师悖论”-理发师不帮自己理发的人理发,那他该不该帮自己理发?
|
|
|
- - 可计算理论的研究对象有三个 : ( 1) 判定问题; ( 2) 可计算性;( 3) 计算复杂性(P=?NP)。
|
|
|
+ - 可计算理论的研究对象有三个 : **( 1) 判定问题; ( 2) 可计算性;( 3) 计算复杂性(P=?NP)**。
|
|
|
- 1936年,阿隆佐邱奇**Alonzo Church** 和 阿兰图灵**Alan Turing** 证明了:为“判定性问题”设计一个通用算法这件事是不可能的。
|
|
|
- 与此同时,对于“可判定”,他们各自详细阐述了两个计算模型:Alonzo Church的**Lambda演算(λ演算)**,Alan Turing的理论机器(之后被称作**图灵机**)
|
|
|
- ![](./img_readbook/p55783884.jpg)
|
|
@@ -59,7 +60,7 @@
|
|
|
- **抽象体(Abstraction)**: 形式:**λx.M** 它表示获取一个参数x并返回M的lambda函数,M是一个合法lambda表达式,且符号λ和.表示绑定变量x于该函数抽象的函数体M。简单来说就是**表示一个形参为x的函数M**
|
|
|
- **应用(Application)** 形式:**M N** 它表示将函数M应用于参数N,其中M、N均为合法lambda表达式。简单来说就是**给函数M输入实参N**
|
|
|
- Alpha「转换」(α 转换): 一个lambda函数抽象在更名绑定变量前后是等价的。**λx.x ≡ λy.y**
|
|
|
- - Beta「规约」(β-Reduction):是把标识符用参数值替换;执行任何可以由机器来完成的计算。
|
|
|
+ - Beta「规约」(β-Reduction):是**把标识符用参数值替换**;执行任何可以由机器来完成的计算。
|
|
|
|
|
|
```lambda
|
|
|
(lambda x . x + 1) 3 == 3 + 1
|
|
@@ -115,7 +116,7 @@
|
|
|
- λ演算引出了函数式编程
|
|
|
- Lisp(1958) 第二个高级编程语言的,它就是启发自λ演算。
|
|
|
- Haskell(1985年,Miranda发行,Haskell语言是1990年在编程语言Miranda的基础上标准化的)
|
|
|
- - Python(1991 1994 Python1.0版本发布,这个版本的主要新功能是lambda, map, filter和reduce)
|
|
|
+ - Python(1991 1994 Python1.0版本发布,这个版本的主要新功能是**lambda, map, filter和reduce**)
|
|
|
- Scala(2003 更接近Java习惯,集成了面向对象和函数式编程范式)
|
|
|
- Clojure(2007 Clojure采用Lisp语法)
|
|
|
- Groovy(2003 动态类型化,闭包,像java风格的脚本语言,接近Ruby或Python)
|
|
@@ -139,18 +140,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
-- 范畴论简介,函数式编程的理论基础;
|
|
|
+- 范畴论简介
|
|
|
|
|
|
- - 伴随着范畴论的发展,就发展出一整套函数的运算方法。这套方法起初只用于数学运算,后来有人将它在计算机上实现了,就变成了今天的"函数式编程"。
|
|
|
+ - Haskell 语言使用了一些范畴论的概念
|
|
|
|
|
|
- - **函数式编程只是范畴论的运算方法**
|
|
|
+ - 范畴论对编程有理论意义
|
|
|
|
|
|
- - 为什么函数式编程要求函数必须是纯的,不能有副作用?因为它是一种数学运算
|
|
|
+ - 当我们知道有些代数结构具有一些很好的性质,满足某些定律。那如果我们写的程序具有这些代数结构,则这些程序也自然就具有同样的性质,满足同样的定律,可以应用同样的规则。例如**幺半群**![[公式]](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)运算同样具有并行计算的能力。范畴论在这方面则起到了重要的理论指导作用,我们可以将范畴论中得到的范畴的代数性质和定律应用到函数式编程中,通过类型系统以新的方式来构造我们的程序。
|
|
|
|
|
|
-
|
|
|
+ - 柯里化和反柯里化是一对**同构变换**,其变换得到的函数是唯一的。也可以将柯里化和反柯里化看成是普通函数和高阶函数之间的变换,柯里化具有升阶的作用,而反柯里化则具有降阶的作用。
|
|
|
+
|
|
|
+ - 范畴论是从拓扑学这一数学分支发展出来的一门抽象的数学理论,后来成为一个独立的分支,并成为了其他数学分支的基础。再后来,和**类型理论**结合,成为了**编程语言理论的数学基础**,因此我们才会在这里来介绍范畴论。但是,学习范畴论却是不需要学习拓扑学也是可以的,学习编程也是可以不学习范畴论。
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
- - 范畴(category) ;随便什么东西,只要能找出它们之间的关系,就能定义一个"范畴"
|
|
|
+ - 范畴(category) ;随便什么东西,只要能找出它们之间的关系,满足下面条件,就是一个"范畴"
|
|
|
|
|
|
- ![bg2017022210](./img_readbook/bg2017022210.jpg)
|
|
|
|
|
@@ -164,266 +169,802 @@
|
|
|
|
|
|
![bg2017022209](./img_readbook/bg2017022209.png)
|
|
|
|
|
|
- ```javascript
|
|
|
- const compose = function (f, g) {
|
|
|
- return function (x) {
|
|
|
- return f(g(x));
|
|
|
- };
|
|
|
- }
|
|
|
- ```
|
|
|
-
|
|
|
|
|
|
-
|
|
|
+
|
|
|
- 存在恒等态射(identity):
|
|
|
+
|
|
|
+ - 对于任何态射f:a→b,有1b∘f=f=f∘1a ;可以简单的认为是;f(x)
|
|
|
|
|
|
- - 对于任何态射f:a→b,有1b∘f=f=f∘1a ;可以简单的认为是;f(x)=x
|
|
|
|
|
|
- - 柯里化
|
|
|
|
|
|
- - `f(x)`和`g(x)`合成为`f(g(x))`,有一个隐藏的前提,就是`f`和`g`都只能接受一个参数。这时就需要函数柯里化了。所谓"柯里化",就是把一个多参数的函数,转化为单参数函数。
|
|
|
+ - 态射的种类:
|
|
|
|
|
|
- ```javascript
|
|
|
- // 柯里化之前
|
|
|
- function add(x, y) {
|
|
|
- return x + y;
|
|
|
- }
|
|
|
-
|
|
|
- add(1, 2) // 3
|
|
|
-
|
|
|
- // 柯里化之后
|
|
|
- function addX(y) {
|
|
|
- return function (x) {
|
|
|
- return x + y;
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- addX(2)(1) // 3
|
|
|
+ - 单态射:不存在两个a中元素 map 到同一个b中的元素。
|
|
|
+ - 满态射:每一个b中的元素,都在a中有至少一个 source mapper。
|
|
|
+ - 双态射:即是单态射,又是满态射。
|
|
|
+ - 同构:a,b两个对象的元素存在一对一的 map 关系。同构 = 双态射 + 存在逆态射。
|
|
|
+ - 自态射:表示一个态射源对象和目标对象是同一个, f:a→a
|
|
|
+ - 自同构:如果f既是一种自态射,又是具有同构性。
|
|
|
+ - 撤回射:如果存在一个f的右逆,其含义是:g 可以通过f找到 source element。f必定是一个满态射
|
|
|
+ - 部分射:如果f的左逆是存在的,也就是说,如果存在 g : b→a, g ∘ f = 1a。 f 是另一个态射g的部分射,其含义是:f 确定了g的同构部分。f必定是一个单态射。
|
|
|
+ - 同态:是一个态射,表示一个**数学结构**A到另一个数学结构B的map关系,并且维持了数学结构上的的每一种操作*。 f:A→B,有: f(x∗y)=f(x)∗f(y)
|
|
|
+
|
|
|
+ - 同一种操作在不同的数学结构上定义可以不同。
|
|
|
+ - 比如:指数函数是一个同态。e(x+y)=exey→f(x∗y)=f(x)∗f(y)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ - **函子(Functor)**
|
|
|
+
|
|
|
+ - 函子是范畴之间的map关系。可以理解为范畴之间的态射。
|
|
|
+ - 图中,函数`f`完成值的转换(`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 函子
|
|
|
+ - Either 函子(运算`if...else`)
|
|
|
+ - ap函子,函子里面包含的值,完全可能是函数。我们可以想象这样一种情况,一个函子的值是数值,另一个函子的值是函数。
|
|
|
+ - 协变函子 F:C→D
|
|
|
+
|
|
|
+ - 对于每一个 C 中的对象x,对应一个 D 中的F(x)。
|
|
|
+ - 对于每一个C中的态射f:x→y,对应D的态射F(f):F(x)→F(y)。
|
|
|
+ - 逆变函子
|
|
|
+
|
|
|
+ - 和协变函子类似,只不过态射的方向是从D到C。
|
|
|
+ - 自然转换(Natural transformation)
|
|
|
+ - 自然转换是两个函子之间的关系。函子描述“自然构造(natural constructions)”,自然转换描述两个这样构造的"自然同态(natural homomorphisms)"。
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ - Map-Reduce-Filter 模式
|
|
|
+
|
|
|
+ - Continuation 顺延
|
|
|
+
|
|
|
+ - ```java
|
|
|
+ System.out.println("Please enter your name: ");
|
|
|
+ System.in.readLine();
|
|
|
```
|
|
|
|
|
|
-
|
|
|
+ - ```java
|
|
|
+ System.out.println("Please enter your name: ", System.in.readLine);
|
|
|
+ ```
|
|
|
|
|
|
- - 使用代码,定义一个简单的范畴
|
|
|
+ -
|
|
|
|
|
|
- ```javascript
|
|
|
- class Category {
|
|
|
- constructor(val) {
|
|
|
- this.val = val;
|
|
|
+ - <https://github.com/thoas/go-funk>
|
|
|
+
|
|
|
+ - funk.Contains 包含
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ // slice of string
|
|
|
+ funk.Contains([]string{"foo", "bar"}, "bar") // true
|
|
|
+
|
|
|
+ // string
|
|
|
+ funk.Contains("florent", "rent") // true
|
|
|
+ funk.Contains("florent", "foo") // false
|
|
|
+
|
|
|
+ // even map
|
|
|
+ funk.Contains(map[int]string{1: "Florent"}, 1) // true
|
|
|
+ funk.Contains(map[int]string{1: "Florent"}, func(key int, name string) bool {
|
|
|
+ return key == 1 // or `name == "Florent"` for the value type
|
|
|
+ }) // true
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Intersect 交集
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ funk.Intersect([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{2, 4}
|
|
|
+ funk.Intersect([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"foo", "bar"}
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Difference 集合差
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ funk.Difference([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{1, 3}, []int{6}
|
|
|
+ funk.Difference([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"hello"}, []string{}
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.IndexOf 首次出现值的索引,如果找不到值,则返回 -1
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ // slice of string
|
|
|
+ funk.IndexOf([]string{"foo", "bar"}, "bar") // 1
|
|
|
+ funk.IndexOf([]string{"foo", "bar"}, func(value string) bool {
|
|
|
+ return value == "bar"
|
|
|
+ }) // 1
|
|
|
+ funk.IndexOf([]string{"foo", "bar"}, "gilles") // -1
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.LastIndexOf 找到值的最后一次出现的索引,如果找不到该值,则返回 -1
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ // slice of string
|
|
|
+ funk.LastIndexOf([]string{"foo", "bar", "bar"}, "bar") // 2
|
|
|
+ funk.LastIndexOf([]string{"foo", "bar"}, func(value string) bool {
|
|
|
+ return value == "bar"
|
|
|
+ }) // 2
|
|
|
+ funk.LastIndexOf([]string{"foo", "bar"}, "gilles") // -1
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.ToMap 将切片或结构体数组转换为基于透视字段的Map
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ f := &Foo{
|
|
|
+ ID: 1,
|
|
|
+ FirstName: "Gilles",
|
|
|
+ LastName: "Fabio",
|
|
|
+ Age: 70,
|
|
|
}
|
|
|
-
|
|
|
- addOne(x) {
|
|
|
- return x + 1;
|
|
|
+
|
|
|
+ b := &Foo{
|
|
|
+ ID: 2,
|
|
|
+ FirstName: "Florent",
|
|
|
+ LastName: "Messa",
|
|
|
+ Age: 80,
|
|
|
}
|
|
|
- }
|
|
|
- ```
|
|
|
+
|
|
|
+ results := []*Foo{f, b}
|
|
|
+
|
|
|
+ mapping := funk.ToMap(results, "ID") // map[int]*Foo{1: f, 2: b}
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.ToSet 将数组或切片转换为Set(零值的Map)
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ f := Foo{
|
|
|
+ ID: 1,
|
|
|
+ FirstName: "Gilles",
|
|
|
+ LastName: "Fabio",
|
|
|
+ Age: 70,
|
|
|
+ }
|
|
|
+
|
|
|
+ b := Foo{
|
|
|
+ ID: 2,
|
|
|
+ FirstName: "Florent",
|
|
|
+ LastName: "Messa",
|
|
|
+ Age: 80,
|
|
|
+ }
|
|
|
+
|
|
|
+ mapping := funk.ToSet([]Foo{f, b}) // map[Foo]stuct{}{f: struct{}{}, b: struct{}{}}
|
|
|
+
|
|
|
+ mapping := funk.ToSet([4]int{1, 1, 2, 2}) // map[int]struct{}{1: struct{}{}, 2: struct{}{}}
|
|
|
+
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Filter 根据谓词筛选切片
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ r := funk.Filter([]int{1, 2, 3, 4}, func(x int) bool {
|
|
|
+ return x%2 == 0
|
|
|
+ }) // []int{2, 4}
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Reduce 根据累积函数或数字操作符迭代。
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ // Using operation runes. '+' and '*' only supported.
|
|
|
+ r := funk.Reduce([]int{1, 2, 3, 4}, '+', float64(0)) // 10
|
|
|
+ r := funk.Reduce([]int{1, 2, 3, 4}, '*', 1) // 24
|
|
|
+
|
|
|
+ // Using accumulator function
|
|
|
+ r := funk.Reduce([]int{1, 2, 3, 4}, func(acc float64, num int) float64 {
|
|
|
+ return acc + float64(num)
|
|
|
+ }, float64(0)) // 10
|
|
|
+
|
|
|
+ r := funk.Reduce([]int{1, 2, 3, 4}, func(acc string, num int) string {
|
|
|
+ return acc + fmt.Sprint(num)
|
|
|
+ }, "") // "1234"
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Find 根据谓词在切片中查找元素
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ r := funk.Find([]int{1, 2, 3, 4}, func(x int) bool {
|
|
|
+ return x%2 == 0
|
|
|
+ }) // 2
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Map 操作(映射、切片)将其转换为另一种Set(映射、切片)
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ r := funk.Map([]int{1, 2, 3, 4}, func(x int) int {
|
|
|
+ return x * 2
|
|
|
+ }) // []int{2, 4, 6, 8}
|
|
|
+
|
|
|
+ r := funk.Map([]int{1, 2, 3, 4}, func(x int) string {
|
|
|
+ return "Hello"
|
|
|
+ }) // []string{"Hello", "Hello", "Hello", "Hello"}
|
|
|
+
|
|
|
+ r = funk.Map([]int{1, 2, 3, 4}, func(x int) (int, int) {
|
|
|
+ return x, x
|
|
|
+ }) // map[int]int{1: 1, 2: 2, 3: 3, 4: 4}
|
|
|
+
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.FlatMap 操作(映射、切片)并将其转换为另一种类型的平展Set
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ r := funk.FlatMap([][]int{{1, 2}, {3, 4}}, func(x []int) []int {
|
|
|
+ return append(x, 0)
|
|
|
+ }) // []int{1, 2, 0, 3, 4, 0}
|
|
|
+
|
|
|
+ mapping := map[string][]int{
|
|
|
+ "Florent": {1, 2},
|
|
|
+ "Gilles": {3, 4},
|
|
|
+ }
|
|
|
+
|
|
|
+ r = funk.FlatMap(mapping, func(k string, v []int) []int {
|
|
|
+ return v
|
|
|
+ }) // []int{1, 2, 3, 4}
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Get 检索Struct或Map路径的值
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ var bar *Bar = &Bar{
|
|
|
+ Name: "Test",
|
|
|
+ Bars: []*Bar{
|
|
|
+ &Bar{
|
|
|
+ Name: "Level1-1",
|
|
|
+ Bar: &Bar{
|
|
|
+ Name: "Level2-1",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ &Bar{
|
|
|
+ Name: "Level1-2",
|
|
|
+ Bar: &Bar{
|
|
|
+ Name: "Level2-2",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ var foo *Foo = &Foo{
|
|
|
+ ID: 1,
|
|
|
+ FirstName: "Dark",
|
|
|
+ LastName: "Vador",
|
|
|
+ Age: 30,
|
|
|
+ Bar: bar,
|
|
|
+ Bars: []*Bar{
|
|
|
+ bar,
|
|
|
+ bar,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ funk.Get([]*Foo{foo}, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"}
|
|
|
+ funk.Get(foo, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"}
|
|
|
+ funk.Get(foo, "Bar.Name") // Test
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.GetOrElse 检索指针点 Else 默认的值
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ str := "hello world"
|
|
|
+ GetOrElse(&str, "foobar") // string{"hello world"}
|
|
|
+ GetOrElse(str, "foobar") // string{"hello world"}
|
|
|
+ GetOrElse(nil, "foobar") // string{"foobar"}
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Set 在Struct的路径节点赋值
|
|
|
+
|
|
|
+ - funk.MustSet 如果Struct不包含接口字段类型,则丢弃错误
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ var bar Bar = Bar{
|
|
|
+ Name: "level-0",
|
|
|
+ Bar: &Bar{
|
|
|
+ Name: "level-1",
|
|
|
+ Bars: []*Bar{
|
|
|
+ {Name: "level2-1"},
|
|
|
+ {Name: "level2-2"},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ _ = Set(&bar, "level-0-new", "Name")
|
|
|
+ fmt.Println(bar.Name) // "level-0-new"
|
|
|
+
|
|
|
+ MustSet(&bar, "level-1-new", "Bar.Name")
|
|
|
+ fmt.Println(bar.Bar.Name) // "level-1-new"
|
|
|
+
|
|
|
+ Set(&bar, "level-2-new", "Bar.Bars.Name")
|
|
|
+ fmt.Println(bar.Bar.Bars[0].Name) // "level-2-new"
|
|
|
+ fmt.Println(bar.Bar.Bars[1].Name) // "level-2-new"
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Prune 复制仅包含选定的结构体字段的结构。切片是通过修剪所有元素来处理的。
|
|
|
+
|
|
|
+ - funk.PruneByTag 与 funk 相同的功能。但使用tag而不是结构体字段名称
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ bar := &Bar{
|
|
|
+ Name: "Test",
|
|
|
+ }
|
|
|
+
|
|
|
+ foo1 := &Foo{
|
|
|
+ ID: 1,
|
|
|
+ FirstName: "Dark",
|
|
|
+ LastName: "Vador",
|
|
|
+ Bar: bar,
|
|
|
+ }
|
|
|
+
|
|
|
+ pruned, _ := Prune(foo1, []string{"FirstName", "Bar.Name"})
|
|
|
+ // *Foo{
|
|
|
+ // ID: 0,
|
|
|
+ // FirstName: "Dark",
|
|
|
+ // LastName: "",
|
|
|
+ // Bar: &Bar{Name: "Test},
|
|
|
+ // }
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Keys 创建一个数组来列举Map的Key或者结构体字段名称
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ funk.Keys(map[string]int{"one": 1, "two": 2}) // []string{"one", "two"} (iteration order is not guaranteed)
|
|
|
+
|
|
|
+ foo := &Foo{
|
|
|
+ ID: 1,
|
|
|
+ FirstName: "Dark",
|
|
|
+ LastName: "Vador",
|
|
|
+ Age: 30,
|
|
|
+ }
|
|
|
+
|
|
|
+ funk.Keys(foo) // []string{"ID", "FirstName", "LastName", "Age"} (iteration order is not guaranteed)
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Values 创建一个数组来列举Map的值或者结构体字段的值
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ funk.Values(map[string]int{"one": 1, "two": 2}) // []int{1, 2} (iteration order is not guaranteed)
|
|
|
+
|
|
|
+ foo := &Foo{
|
|
|
+ ID: 1,
|
|
|
+ FirstName: "Dark",
|
|
|
+ LastName: "Vador",
|
|
|
+ Age: 30,
|
|
|
+ }
|
|
|
+
|
|
|
+ funk.Values(foo) // []interface{}{1, "Dark", "Vador", 30} (iteration order is not guaranteed)
|
|
|
+ ```
|
|
|
|
|
|
+ - funk.ForEach 遍历一个Map或者切片
|
|
|
|
|
|
+ - ```go
|
|
|
+ funk.ForEach([]int{1, 2, 3, 4}, func(x int) {
|
|
|
+ fmt.Println(x)
|
|
|
+ })
|
|
|
+ ```
|
|
|
|
|
|
+ - funk.ForEachRight 从右侧遍历一个Map或者切片
|
|
|
|
|
|
- - 态射的种类:
|
|
|
+ - ```go
|
|
|
+ results := []int{}
|
|
|
+
|
|
|
+ funk.ForEachRight([]int{1, 2, 3, 4}, func(x int) {
|
|
|
+ results = append(results, x)
|
|
|
+ })
|
|
|
+
|
|
|
+ fmt.Println(results) // []int{4, 3, 2, 1}
|
|
|
+ ```
|
|
|
|
|
|
- - 单态射:不存在两个a中元素 map 到同一个b中的元素。
|
|
|
- - 满态射:每一个b中的元素,都在a中有至少一个 source mapper。
|
|
|
- - 双态射:即是单态射,又是满态射。
|
|
|
- - 同构:a,b两个对象的元素存在一对一的 map 关系。同构 = 双态射 + 存在逆态射。
|
|
|
- - 自态射:表示一个态射源对象和目标对象是同一个, f:a→a
|
|
|
- - 自同构:如果f既是一种自态射,又是具有同构性。
|
|
|
- - 撤回射:如果存在一个f的右逆,其含义是:g 可以通过f找到 source element。f必定是一个满态射
|
|
|
- - 部分射:如果f的左逆是存在的,也就是说,如果存在 g : b→a, g ∘ f = 1a。 f 是另一个态射g的部分射,其含义是:f 确定了g的同构部分。f必定是一个单态射。
|
|
|
- - 同态:是一个态射,表示一个**数学结构**A到另一个数学结构B的map关系,并且维持了数学结构上的的每一种操作*。 f:A→B,有: f(x∗y)=f(x)∗f(y)
|
|
|
+ - funk.Chunk 创建一个数组,这些元素按大小的长度拆分为组。如果数组不能均匀拆分,则最后一个块将是剩余的元素。
|
|
|
|
|
|
- - 同一种操作在不同的数学结构上定义可以不同。
|
|
|
- - 比如:指数函数是一个同态。e(x+y)=exey→f(x∗y)=f(x)∗f(y)
|
|
|
+ - ```go
|
|
|
+ funk.Chunk([]int{1, 2, 3, 4, 5}, 2) // [][]int{[]int{1, 2}, []int{3, 4}, []int{5}}
|
|
|
+ ```
|
|
|
|
|
|
- - **函子(functor)**
|
|
|
+ - funk.FlattenDeep 递归平展数组
|
|
|
|
|
|
- - 函子是函数式编程里面最重要的,也是基本的运算单位和功能单位。
|
|
|
+ - ```go
|
|
|
+ funk.FlattenDeep([][]int{[]int{1, 2}, []int{3, 4}}) // []int{1, 2, 3, 4}
|
|
|
+ ```
|
|
|
|
|
|
- - 函子是范畴之间的map关系。可以理解为范畴之间的态射。
|
|
|
+ - funk.Uniq 创建具有唯一值的数组
|
|
|
|
|
|
- - 图中,函数`f`完成值的转换(`a`到`b`),将它传入函子,就可以实现范畴的转换(`Fa`到`Fb`)。
|
|
|
+ - ```go
|
|
|
+ funk.Uniq([]int{0, 1, 1, 2, 3, 0, 0, 12}) // []int{0, 1, 2, 3, 12}
|
|
|
+ ```
|
|
|
|
|
|
- - ![bg2017022211](./img_readbook/bg2017022211.jpg)
|
|
|
+ - funk.Drop 创建一个数组/切片,其中删除了 n 个元素
|
|
|
|
|
|
- - 具有`map`方法的数据结构,都可以当作函子的实现。代码中,`Functor`是一个函子,它的`map`方法接受函数`f`作为参数,然后返回一个新的函子,里面包含的值是被`f`处理过的(`f(this.val)`)。
|
|
|
+ - ```go
|
|
|
+ funk.Drop([]int{0, 0, 0, 0}, 3) // []int{0}
|
|
|
+ ```
|
|
|
|
|
|
- - ```java
|
|
|
- class Functor {
|
|
|
- constructor(val) {
|
|
|
- this.val = val;
|
|
|
- }
|
|
|
-
|
|
|
- map(f) {
|
|
|
- return new Functor(f(this.val));
|
|
|
- }
|
|
|
- }
|
|
|
- ```
|
|
|
+ - funk.Initial 获取数组中除最后一个元素之外的所有元素
|
|
|
|
|
|
- - 函数式编程里面的运算,都是通过函子完成,即运算不直接针对值,而是针对这个值的容器----函子。函子本身具有对外接口(`map`方法),各种函数就是运算符,通过接口接入容器,引发容器里面的值的变形。因此,**学习函数式编程,实际上就是学习函子的各种运算。**由于可以把运算方法封装在函子里面,所以又衍生出各种不同类型的函子,有多少种运算,就有多少种函子。函数式编程就变成了运用不同的函子,解决实际问题。
|
|
|
+ - ```go
|
|
|
+ funk.Initial([]int{0, 1, 2, 3, 4}) // []int{0, 1, 2, 3}
|
|
|
+ ```
|
|
|
|
|
|
- - Maybe 函子
|
|
|
+ - funk.Tail 获取数组中除第一个元素之外的所有元素
|
|
|
|
|
|
- - Either 函子(运算`if...else`)
|
|
|
+ - ```go
|
|
|
+ funk.Tail([]int{0, 1, 2, 3, 4}) // []int{1, 2, 3, 4}
|
|
|
+ ```
|
|
|
|
|
|
- - ap函子,函子里面包含的值,完全可能是函数。我们可以想象这样一种情况,一个函子的值是数值,另一个函子的值是函数。
|
|
|
+ - funk.Shuffle 创建随机值的数组
|
|
|
|
|
|
- -
|
|
|
+ - ```go
|
|
|
+ funk.Shuffle([]int{0, 1, 2, 3, 4}) // []int{2, 1, 3, 4, 0}
|
|
|
+ ```
|
|
|
|
|
|
-
|
|
|
+ - funk.Subtract 返回两个集合之间的减法。它保持顺序
|
|
|
|
|
|
- - 协变函子 F:C→D
|
|
|
+ - ```go
|
|
|
+ funk.Subtract([]int{0, 1, 2, 3, 4}, []int{0, 4}) // []int{1, 2, 3}
|
|
|
+ funk.Subtract([]int{0, 3, 2, 3, 4}, []int{0, 4}) // []int{3, 2, 3}
|
|
|
+ ```
|
|
|
|
|
|
- - 对于每一个 C 中的对象x,对应一个 D 中的F(x)。
|
|
|
- - 对于每一个C中的态射f:x→y,对应D的态射F(f):F(x)→F(y)。
|
|
|
+ - funk.Sum 累加
|
|
|
|
|
|
- - 逆变函子
|
|
|
+ - ```go
|
|
|
+ funk.Sum([]int{0, 1, 2, 3, 4}) // 10.0
|
|
|
+ funk.Sum([]interface{}{0.5, 1, 2, 3, 4}) // 10.5
|
|
|
+ ```
|
|
|
|
|
|
- - 和协变函子类似,只不过态射的方向是从D到C。
|
|
|
+ - funk.Reverse 把一个数组反转顺序
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ funk.Reverse([]int{0, 1, 2, 3, 4}) // []int{4, 3, 2, 1, 0}
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.SliceOf 返回基于元素的切片
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ funk.SliceOf(f) // will return a []*Foo{f}
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.RandomInt 基于最小值和最大值生成随机整数
|
|
|
|
|
|
- - 自然转换(Natural transformation)
|
|
|
+ - ```go
|
|
|
+ funk.RandomInt(0, 100) // will be between 0 and 100
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.RandomString 生成具有固定长度的随机字符串
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ funk.RandomString(4) // will be a string of 4 random characters
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Shard 生成具有固定长度和深度的分片字符串
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 1, 2, false) // []string{"e", "8", "e89d66bdfdd4dd26b682cc77e23a86eb"}
|
|
|
+
|
|
|
+ funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 2, false) // []string{"e8", "9d", "e89d66bdfdd4dd26b682cc77e23a86eb"}
|
|
|
+
|
|
|
+ funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 3, true) // []string{"e8", "9d", "66", "bdfdd4dd26b682cc77e23a86eb"}
|
|
|
+
|
|
|
+ ```
|
|
|
+
|
|
|
+ - funk.Subset 如果一个集合是另一个集合的子集,则返回 true
|
|
|
+
|
|
|
+ - ```go
|
|
|
+ funk.Subset([]int{1, 2, 4}, []int{1, 2, 3, 4, 5}) // true
|
|
|
+ funk.Subset([]string{"foo", "bar"},[]string{"foo", "bar", "hello", "bar", "hi"}) //true
|
|
|
+ ```
|
|
|
+
|
|
|
+
|
|
|
|
|
|
- - 自然转换是两个函子之间的关系。函子描述“自然构造(natural constructions)”,自然转换描述两个这样构造的"自然同态(natural homomorphisms)"。
|
|
|
|
|
|
|
|
|
+ - 例子
|
|
|
|
|
|
-- Map-Reduce-Filter 模式(高阶函数)
|
|
|
- - reduce
|
|
|
|
|
|
- - ```go
|
|
|
- //在Haskell中是没有循环的,由于循环能够经过递归实现,在咱们实现的sum函数中,也没有用到任何循环语句,这和咱们原来的编程思惟有所不一样,刚开始写求和函数的时候,都是从for,while开始的,可是函数式给我打开了新世界的大门。
|
|
|
- //递归 尾递归
|
|
|
- func foldr(f func(a,b int) int,z int,list []int)int{
|
|
|
- if len(list) == 0{
|
|
|
- return z
|
|
|
+ - ```java
|
|
|
+ abstract class MessageHandler {
|
|
|
+ void handleMessage(Message msg) {
|
|
|
+ // ...
|
|
|
+ msg.setClientCode(getClientCode());
|
|
|
+ // ...
|
|
|
+
|
|
|
+ sendMessage(msg);
|
|
|
+ }
|
|
|
+
|
|
|
+ abstract String getClientCode();
|
|
|
+ // ...
|
|
|
+ }
|
|
|
+
|
|
|
+ class MessageHandlerOne extends MessageHandler {
|
|
|
+ String getClientCode() {
|
|
|
+ return "ABCD_123";
|
|
|
+ }
|
|
|
}
|
|
|
- return f(list[0],foldr(f,z,list[1:]))
|
|
|
- }
|
|
|
+
|
|
|
+ class MessageHandlerTwo extends MessageHandler {
|
|
|
+ String getClientCode() {
|
|
|
+ return "123_ABCD";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
|
|
|
- func add(a,b int) int{
|
|
|
- return a+b;
|
|
|
- }
|
|
|
+ - ```java
|
|
|
+ class MessageHandler {
|
|
|
+ void handleMessage(Message msg, Function getClientCode) {
|
|
|
+ // ...
|
|
|
+ Message msg1 = msg.setClientCode(getClientCode());
|
|
|
+ // ...
|
|
|
+ sendMessage(msg1);
|
|
|
+ }
|
|
|
+ // ...
|
|
|
+ }
|
|
|
+
|
|
|
+ String getClientCodeOne() {
|
|
|
+ return "ABCD_123";
|
|
|
+ }
|
|
|
+
|
|
|
+ String getClientCodeTwo() {
|
|
|
+ return "123_ABCD";
|
|
|
+ }
|
|
|
+
|
|
|
+ MessageHandler handler = new MessageHandler();
|
|
|
+ handler.handleMessage(someMsg, getClientCodeOne);
|
|
|
+ ```
|
|
|
|
|
|
- func sum(list []int) int{
|
|
|
- return foldr(add,0,list)
|
|
|
- }
|
|
|
+ -
|
|
|
|
|
|
- func main(){
|
|
|
- a := []int{1,2,3}
|
|
|
- sum(a) // 6
|
|
|
- }
|
|
|
- ```
|
|
|
-
|
|
|
- - ```go
|
|
|
- //复用 foldr 高阶函数
|
|
|
- func muti(a,b int) int{
|
|
|
- return a*b;
|
|
|
- }
|
|
|
+ - 函数式选项
|
|
|
|
|
|
- func product(list []int) int{
|
|
|
- return foldr(muti,1,list)
|
|
|
- }
|
|
|
- ```
|
|
|
-
|
|
|
- - ```javascript
|
|
|
- function flip(f, a, b){
|
|
|
- return f(b,a);
|
|
|
- }
|
|
|
- //这个函数须要进行柯里化,不然没法在foldr中做为参数传入
|
|
|
- var flip_ = _.curry(flip);
|
|
|
+ - ```go
|
|
|
+ type User struct {
|
|
|
+ ID string
|
|
|
+ Name string
|
|
|
+ Age int
|
|
|
+ Email string
|
|
|
+ Phone string
|
|
|
+ Gender string
|
|
|
+ }
|
|
|
+
|
|
|
+ type Option func(*User)
|
|
|
+
|
|
|
+ func WithAge(age int) Option {
|
|
|
+ return func(u *User) {
|
|
|
+ u.Age = age
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func WithEmail(email string) Option {
|
|
|
+ return func(u *User) {
|
|
|
+ u.Email = email
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func WithPhone(phone string) Option {
|
|
|
+ return func(u *User) {
|
|
|
+ u.Phone = phone
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func WithGender(gender string) Option {
|
|
|
+ return func(u *User) {
|
|
|
+ u.Gender = gender
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func NewUser(id string, name string, options ...func(*User)) (*User, error) {
|
|
|
+ user := User{
|
|
|
+ ID: id,
|
|
|
+ Name: name,
|
|
|
+ Age: 0,
|
|
|
+ Email: "",
|
|
|
+ Phone: "",
|
|
|
+ Gender: "female",
|
|
|
+ }
|
|
|
+ for _, option := range options {
|
|
|
+ option(&user)
|
|
|
+ }
|
|
|
+ //...
|
|
|
+ return &user, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ func main() {
|
|
|
+ user, err := NewUser("1", "Ada", WithAge(18), WithPhone("123456"))
|
|
|
+ if err != nil {
|
|
|
+ fmt.Printf("NewUser: err:%v", err)
|
|
|
+ }
|
|
|
+ fmt.Printf("NewUser Success")
|
|
|
+ }
|
|
|
+ ```
|
|
|
|
|
|
- function cons(a,b){
|
|
|
- return a.concat(b);
|
|
|
- }
|
|
|
+ - 工厂方法
|
|
|
|
|
|
- function reverse(list){
|
|
|
- return foldr(flip_(cons),[],list);
|
|
|
- }
|
|
|
+ - ```go
|
|
|
+ // 工厂方法模式
|
|
|
+ type Person03 struct {
|
|
|
+ name string
|
|
|
+ age int
|
|
|
+ }
|
|
|
+
|
|
|
+ func NewPersonFactory(age int) func(name string) Person03 {
|
|
|
+ return func(name string) Person03 {
|
|
|
+ return Person03{
|
|
|
+ name: name,
|
|
|
+ age: age,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func use() {
|
|
|
+ newBaby := NewPersonFactory(1)
|
|
|
+ baby := newBaby("john")
|
|
|
+ fmt.Print(baby)
|
|
|
+
|
|
|
+ newTeenager := NewPersonFactory(16)
|
|
|
+ teen := newTeenager("jill")
|
|
|
+ fmt.Print(teen)
|
|
|
+ }
|
|
|
+
|
|
|
+ ```
|
|
|
|
|
|
- console.log(reverse([1,2,3]))
|
|
|
- // [ 3, 2, 1 ]
|
|
|
+ - 装饰器模式
|
|
|
|
|
|
- //curry,柯里化。简单地说,柯里化就是一个函数能够先接受一部分参数,而后返回一个接受剩下参数的函数。用上面的例子来讲,flip函数在被柯里化以后获得的函数flip_,能够先接受第一个参数cons而后返回一个接受两个参数a,b的函数,也就是咱们须要的链接函数。
|
|
|
+ - ```go
|
|
|
+ type Object func(int) int
|
|
|
+
|
|
|
+ func LogDecorate(fn Object) Object {
|
|
|
+ return func(n int) int {
|
|
|
+ log.Println("Starting the execution with the integer", n)
|
|
|
+
|
|
|
+ result := fn(n)
|
|
|
+
|
|
|
+ log.Println("Execution is completed with the result", result)
|
|
|
+
|
|
|
+ return result
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
|
|
|
- //在go语言里面,实现curry,可用用第三方包。
|
|
|
- ```
|
|
|
-
|
|
|
- - ```javascript
|
|
|
- //先定义一个count函数
|
|
|
- function count(a,n){
|
|
|
- return n + 1;
|
|
|
- }
|
|
|
- //再实现length函数
|
|
|
- function length(list){
|
|
|
- return foldr(count,0,list);
|
|
|
- }
|
|
|
- //调用
|
|
|
- console.log(length([1,2,3,4]));
|
|
|
- // 4
|
|
|
- ```
|
|
|
-
|
|
|
- - `reduce`咱们讲完了,而后咱们看看`map`
|
|
|
-
|
|
|
- - ```go
|
|
|
- //go 的尴尬之处在于,须要很是明确的函数定义,因此咱们要从新写一个foldr函数,来接受第二个参数为列表的f
|
|
|
- func foldr2(f func(a int,b []int) []int,z []int,list []int)[]int{
|
|
|
- if len(list) == 0{
|
|
|
- return z
|
|
|
+ - Spring webflux 函数式编程web框架
|
|
|
+
|
|
|
+ - ```java
|
|
|
+ @Component
|
|
|
+ public class HelloWorldHandler {
|
|
|
+
|
|
|
+ public Mono<ServerResponse> helloWorld(ServerRequest request){
|
|
|
+ return ServerResponse.ok()
|
|
|
+ .contentType(MediaType.TEXT_PLAIN)
|
|
|
+ .body(BodyInserters.fromObject("hello flux"));
|
|
|
+ }
|
|
|
}
|
|
|
- return f(list[0],foldr2(f,z,list[1:]))
|
|
|
- }
|
|
|
+
|
|
|
+ ```
|
|
|
|
|
|
- func doubleandcons(n int,list []int) []int{
|
|
|
- return append([]int{n * 2},list...)
|
|
|
- }
|
|
|
+ - ```java
|
|
|
+ @Configuration
|
|
|
+ public class RouterConfig {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private HelloWorldHandler helloWorldHandler;
|
|
|
+
|
|
|
+ @Bean
|
|
|
+ public RouterFunction<?> helloRouter() {
|
|
|
+ return RouterFunctions.route(RequestPredicates.GET("/hello"), helloWorldHandler::helloWorld);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ```
|
|
|
|
|
|
- func doubleall(list []int) []int{
|
|
|
- return foldr2(doubleandcons,make([]int,0),list)
|
|
|
- }
|
|
|
- // doubleall([]int{1,2,3,4})
|
|
|
- //[2 4 6 8]
|
|
|
- ```
|
|
|
-
|
|
|
- - ```javascript
|
|
|
- function fandcons(f, el, list){
|
|
|
- return [f(el)].concat(list);
|
|
|
- }
|
|
|
- //须要柯里化
|
|
|
- var fandcons_ = _.curry(fandcons);
|
|
|
+ - 再来一个例子
|
|
|
|
|
|
- function map(f, list){
|
|
|
- return foldr(fandcons_(f),[],list);
|
|
|
- }
|
|
|
- //调用
|
|
|
- console.log(map(function(x){return 2*x},[1,2,3,4]));
|
|
|
- // 输出[ 2, 4, 6, 8 ]
|
|
|
- ```
|
|
|
-
|
|
|
- - ```haskell
|
|
|
- -- map的一些神奇的操做:矩阵求和
|
|
|
- summatrix :: Num a => [[a]] -> a
|
|
|
- summatrix x = sum (map sum x)
|
|
|
+ - ```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));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ```
|
|
|
|
|
|
- -- 调用
|
|
|
- summatrix [[1,2,3],[4,5,6]]
|
|
|
- -- 21
|
|
|
- ```
|
|
|
-
|
|
|
- - ```javascript
|
|
|
- function sum(list){
|
|
|
- return foldr(add,0,list);
|
|
|
- }
|
|
|
- function summatrix(matrix){
|
|
|
- return sum(map(sum,matrix));
|
|
|
- }
|
|
|
- //调用
|
|
|
- mat = [[1,2,3],[4,5,6]];
|
|
|
- console.log(summatrix(mat));
|
|
|
- //输出 21
|
|
|
- ```
|
|
|
-
|
|
|
- - filter
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+ - ```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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+ -
|
|
|
|
|
|
|
|
|
|