Browse Source

book add and read FP

xuqiang 2 years ago
parent
commit
6a38c4dd5c

BIN
Go 入门指南.pdf


BIN
Go语言从入门到进阶实战_徐波.pdf


BIN
Vim 从入门到精通.pdf


+ 0 - 177
xq_study/go语言代码规范.md

@@ -1,177 +0,0 @@
-2021
-
-# go语言编码规范
-
-建信金科 智能云事业部  产品创新和云技术团队
-
-2021-01-25
-
-
-
-- 格式化
-
-  - 使用gofmt格式化
-  - 一行最好不要超过120个字符
-
-- 使用`golint`进行go代码的检测
-
-  - https://github.com/golang/lint
-
-- 使用go vet静态分析代码问题
-
-- log
-
-  - 使用第三方库 https://github.com/sirupsen/logrus
-
-  - log定向到标准输出 SetOutput(os.Stdout)
-
-  - 
-    ```
-    
-    import (
-    	log "github.com/sirupsen/logrus"
-    	"os"
-    )
-    
-    func init() {
-    	log.SetFormatter(&log.JSONFormatter{})//设置日志的输出格式为json格式,还可以设置为text格式
-    	log.SetOutput(os.Stdout)//设置日志的输出为标准输出
-    	log.SetLevel(log.InfoLevel)//设置日志的显示级别,这一级别以及更高级别的日志信息将会输出
-    }
-    ```
-
-
-- GOPATH
-
-  - 单一的 GOPATH原则,除非你的项目很大并且极为重要,否则不要为每个项目使用不同的 GOPAHT
-
-- 文件命名,目录名(包名)
-
-  - 使用小写
-  - 不同单词之间用下划线分词,不要使用驼峰式命名
-  - 如果是测试文件,以 `_test.go`结尾
-  - 保持package的名字和目录保持一致
-  - 不要和标准库冲突
-  - 一个目录只实现一个包
-
-- 常量命名
-
-  - 使用全大写且用下划线分词,比如 APP_VERSION
-
-  - 如果要定义多个变量,请使用 括号 来组织
-
-  - 
-    ```
-   
-    const (
-    	APP_VERSION = "0.1.0"
-    	CONF_PATH = "/etc/xx.conf"
-    )
-    ```
-
-- 变量命名
-
-  - 统一使用 驼峰命名法
-  - 一般情况下首单词全小写,其后各单词首字母大写
-  - 首单词小写(变量为私有)
-  - (变量不是私有),那首单词就要变成全大写
-  - 局部变量可以使用简单变量名
-
-- 函数命名
-
-  - 驼峰命名法
-  - 不需要在包外访问,请使用小写字母开头
-  - 需要在包外访问,请使用 大写字母开头
-
-- 接口命名
-
-  - 使用驼峰命名法
-  - 大写开头的 type 给包外访问
-
-- 注释
-
-  - 包注释,位于 package 之前,如果一个包有多个文件,只需要在一个文件中编写即可
-  - 如果是特别复杂的包,可单独创建 doc.go 文件说明
-  - 包、函数、方法和类型的注释说明都是一个完整的句子
-  - 特别注释
-    - TODO:提醒维护人员此部分代码待完成
-    - FIXME:提醒维护人员此处有BUG待修复
-    - NOTE:维护人员要关注的一些问题说明
-
-- context
-
-  - 不要把context 放入结构体中
-  - 函数中使用context 类型做为参数时 应该做为第一个参数
-  - context 在函数中传递的过程中是不可以改变的
-
-- goroutine
-
-  - 完成任务的goroutine 确保退出
-  - 尽可能少使用channel
-
-- 函数返回值
-
-  - 直接使用类型返回值,避免使用返回命名参数
-
-- copy
-
-  - 使用时要小心指针
-
-- 传值
-
-  - 对于大型的数据结构,或者小型的可能增长的结构,考虑使用指针
-
-- 包导入
-
-  - 多个包导入,请使用 `{}` 来组织
-
-  - 
-    ```
-   
-    import {
-      "fmt"
-      "os"
-    }
-    ```
-
-  - 标准库排最前面,第三方包次之、项目内的其它包和当前包的子包排最后,每种分类以一空行分隔
-
-  - 不要使用相对路径来导入包
-
-- 错误处理
-
-  - 不能丢弃任何有返回error的调用,不要采用`_`丢弃,必须全部处理
-
-- if
-
-  - 尽量不要在if 语句中初始化 语句,而应该另起一行
-
-- 闭包
-
-  - 在循环中调用函数或者goroutine方法,一定要采用显示的变量调用,不要再闭包函数里面调用循环的参数
-
-  - 
-    ```
-   
-    for i:=0;i<limit;i++{
-        go func(){ DoSomething(i) }() //错误的做法
-        go func(i int){ DoSomething(i) }(i)//正确的做法
-    }
-    ```
-
-- recieved是值类型还是指针类型
-
-  - 
-    ```
-      
-        func(w Win) Tally(playerPlayer)int    //w不会有任何改变 
-        func(w *Win) Tally(playerPlayer)int    //w会改变数据
-    ```
-
-  - 如果定义的struct中带有mutex,那么你的receivers必须是指针
-
-    
-
-
-
-

BIN
xq_study/img_readbook/13404785-d43aafa38339287e.webp


+ 0 - 0
xq_study/img_readbook/650745-20151010032215503-1297138358.png


BIN
xq_study/img_readbook/bg2017022204.png


BIN
xq_study/img_readbook/bg2017022209.png


BIN
xq_study/img_readbook/bg2017022210.jpg


BIN
xq_study/img_readbook/bg2017022211.jpg


BIN
xq_study/img_readbook/funclang.webp


+ 0 - 0
xq_study/img_readbook/p55783884.jpg


+ 329 - 16
xq_study/readbook.md

@@ -1,21 +1,33 @@
 
+2021/12
 
+### 函数式编程笔记
 
+- 函数式编程主要解决两个问题
 
----
+  - 分布式计算颗粒度
+  - 代码复杂度
 
-2021/12
+- 编程语言是工具(简化程序复杂度)
+
+  - 软件开发的工具
+  - 团队协作软件开发的工具
+  - 使用工具到顶了,功能复杂度hold人脑不住了,就要换工具(人脑几十年的时间不会有什么进化)
+  - 结构化编程 -> 面向对象 -> 函数式编程;可以看到这个路线就是对“状态”的限制使用之路
+
+  
 
 - **莱布尼兹**(与**牛顿**)曾经有两个梦想:
   - 创建一种“普遍语言”(universal language)使得任何问题都可以用这种语言表述;
     - 对前一个问题的回答就是自弗雷格、罗素开始,经公理集合论运动的最终结果:以一阶谓词逻辑为语言所形式化阐述的集合论,现在已经成为数学的普遍语言,现代逻辑学、特别是将符号逻辑应用于数学领域所产生的数理逻辑,其最重要的目标就是为整个数学提供一个严格精确的语言。这是我们在学习**数理逻辑**时应当把握的方向。
   - 找到一种"判定方法"(decision method)以解决所有可以在“普遍语言”中所表述的问题
     - 第二个问题则是现代哲学和**计算机科学**最关注的问题
-    - 1928年提出的一个重要的数学问题:“判定性问题” (decision problem)
-    - 1936年,阿隆佐邱奇**Alonzo Church** 和 阿兰图灵**Alan Turing** 证明了:为“判定性问题”设计一个通用算法这件事是不可能的,“可计算问题”,“不可计算问题”。
-    - 与此同时,对于“可计算问题”,他们各自详细阐述了两个计算模型:Alonzo Church的**Lambda演算(λ演算)**,Alan Turing的理论机器(之后被称作**图灵机**)
-
-![](./img_readbook/p55783884.jpg)
+    - 1928年提出的一个重要的数学问题:“判定性问题” (decision problem),所有计算问题都可以规约到相应的一个判定性问题。
+    - “任意正整数是不是素数”这个问题就是可判定的。不可判定问题:“停机问题”、“理发师悖论”-理发师不帮自己理发的人理发,那他该不该帮自己理发?
+    - 可计算理论的研究对象有三个 : ( 1) 判定问题; ( 2) 可计算函数;( 3) 计算复杂性(P=?NP)。
+    - 1936年,阿隆佐邱奇**Alonzo Church** 和 阿兰图灵**Alan Turing** 证明了:为“判定性问题”设计一个通用算法这件事是不可能的,“可判定”,“不可判定”。
+    - 与此同时,对于“可判定”,他们各自详细阐述了两个计算模型:Alonzo Church的**Lambda演算(λ演算)**,Alan Turing的理论机器(之后被称作**图灵机**)
+  - ![](./img_readbook/p55783884.jpg)
 
 - 图灵机
 
@@ -46,8 +58,8 @@
     - **变量(Variable)**:形式:**x**  变量名可能是一个字符或字符串,它**表示一个参数**(形参)或者**一个值**(实参)
     - **抽象体(Abstraction)**:    形式:**λx.M**  它表示获取一个参数x并返回M的lambda函数,M是一个合法lambda表达式,且符号λ和.表示绑定变量x于该函数抽象的函数体M。简单来说就是**表示一个形参为x的函数M**
     - **应用(Application)**    形式:**M N**   它表示将函数M应用于参数N,其中M、N均为合法lambda表达式。简单来说就是**给函数M输入实参N**
-  - Alpha「转换」: 
-  - Beta「规约」:是把标识符用参数值替换;执行任何可以由机器来完成的计算。
+  - Alpha「转换」(α 转换): 一个lambda函数抽象在更名绑定变量前后是等价的。**α: λx.x ≡ λy.y**
+  - Beta「规约」(β-Reduction):是把标识符用参数值替换;执行任何可以由机器来完成的计算。
 
   ```lambda
   (lambda x . x + 1) 3 == 3 + 1
@@ -89,11 +101,6 @@
     let BoolNot = lambda x . x FALSE TRUE 
     ```
 
-  - 
-
-  - “柯里化”(Currying)
-
-
 
 
 - 图灵机引出了命令式编程
@@ -116,6 +123,311 @@
   - golang(2009 谷歌)
   - F#(2005)
 
+  
+
+- 函数式编程的语言分类:
+  - 只支持函数式范式(如Haskell)
+  - 多范式+一流支持(如Scala)
+  - 多范式+部分支持(如Javascript、Go)。这类语言中,函数式编程一般通过库来支持,这些库复制前两种语言的标准库中的部分或全部功能。
+- 函数式编程的要求:
+  - 一等函数/高阶函数
+  - 闭包
+  - 泛型
+  - 尾递归优化
+  - 可变个数参数的函数+可变类型参数
+  - “柯里化”(Currying)
+
+
+
+- 范畴论简介,函数式编程的理论基础;
+
+  - 伴随着范畴论的发展,就发展出一整套函数的运算方法。这套方法起初只用于数学运算,后来有人将它在计算机上实现了,就变成了今天的"函数式编程"。
+
+  - **函数式编程只是范畴论的运算方法**
+
+  - 为什么函数式编程要求函数必须是纯的,不能有副作用?因为它是一种数学运算
+
+    
+
+
+  - 范畴(category) ;随便什么东西,只要能找出它们之间的关系,就能定义一个"范畴"
+
+    - ![bg2017022210](img_readbook/bg2017022210.jpg)
+
+    - 对象/点:object
+
+    - 态射/关系/箭头: morphism, f:a→b
+
+    - 满足结合律:f:a→b,g:b→c 的组合是 g∘f  ; 结合律:(h∘g)∘f  = h∘(g∘f)
+
+      ![bg2017022204](/work/book/xq_study/img_readbook/bg2017022204.png)
+
+      ![bg2017022209](/work/book/xq_study/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)=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
+      ```
+
+      
+
+    - 使用代码,定义一个简单的范畴
+
+      ```javascript
+      class Category {
+        constructor(val) { 
+          this.val = val; 
+        }
+      
+        addOne(x) {
+          return x + 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) 
+
+      - 同一种操作在不同的数学结构上定义可以不同。 
+      - 比如:指数函数是一个同态。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)`)。
+
+    - ```java
+      class Functor {
+        constructor(val) { 
+          this.val = val; 
+        }
+      
+        map(f) {
+          return new Functor(f(this.val));
+        }
+      }
+      ```
+
+    - 例子说明,函数式编程里面的运算,都是通过函子完成,即运算不直接针对值,而是针对这个值的容器----函子。函子本身具有对外接口(`map`方法),各种函数就是运算符,通过接口接入容器,引发容器里面的值的变形。因此,**学习函数式编程,实际上就是学习函子的各种运算。**由于可以把运算方法封装在函子里面,所以又衍生出各种不同类型的函子,有多少种运算,就有多少种函子。函数式编程就变成了运用不同的函子,解决实际问题。
+
+    - 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 模式(高阶函数)
+  - 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
+        }
+        return f(list[0],foldr(f,z,list[1:]))
+    }
+    
+    func add(a,b int) int{
+        return a+b;
+    }
+    
+    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);
+    
+    function cons(a,b){
+         return a.concat(b);
+     }
+    
+    function reverse(list){
+      return foldr(flip_(cons),[],list);
+    }
+    
+    console.log(reverse([1,2,3]))
+    // [ 3, 2, 1 ]
+    
+    //curry,柯里化。简单地说,柯里化就是一个函数能够先接受一部分参数,而后返回一个接受剩下参数的函数。用上面的例子来讲,flip函数在被柯里化以后获得的函数flip_,能够先接受第一个参数cons而后返回一个接受两个参数a,b的函数,也就是咱们须要的链接函数。
+    
+    //在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
+        }
+        return f(list[0],foldr2(f,z,list[1:]))
+    }
+    
+    func doubleandcons(n int,list []int) []int{
+        return append([]int{n * 2},list...)
+    }
+    
+    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)
+    
+    -- 调用
+    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
+
+
+
+
+
+
+
 
 
 
@@ -124,7 +436,9 @@
 
 2021/5
 
-- 计算机历史
+### 笔记
+
+- 历史
 
   - 实体机以前,先有理论(指令集有了,可以编程了)
     - 可计算问题,整数可以表示所有可计算问题
@@ -352,4 +666,3 @@
   - 程序设计语言实践之路,
   - Unix编程艺术,
   - 深入理解计算机系统
-

+ 0 - 698
xq_study/runtime.md

@@ -1,698 +0,0 @@
-2021
-
-### 系统监控 sysmon
-
-在系统栈中创建新的线程,它会每隔一段时间检查 Go 语言运行时,它在内部启动了一个不会中止的循环。
-
-类似于守护进程
-
-usleep 微秒(1/1000毫秒)   nanosleep
-
-```
-func sysmon() {
-	sched.nmsys++
-	checkdead()
-
-	lasttrace := int64(0)
-	idle := 0
-	delay := uint32(0)
-	for {
-		if idle == 0 {
-			delay = 20
-		} else if idle > 50 {
-			delay *= 2
-		}
-		if delay > 10*1000 {
-			delay = 10 * 1000
-		}
-		usleep(delay)
-		...
-	}
-}
-//初始的休眠时间是 20μs;
-//最长的休眠时间是 10ms;
-//当系统监控在 50 个循环中都没有唤醒 Goroutine 时,休眠时间在每个循环都会倍增;
-```
-
-
-
-- 运行计时器 — 获取下一个需要被触发的计时器;
-- 轮询网络 — 获取需要处理的到期文件描述符;
-- 抢占处理器 — 抢占运行时间较长的或者处于系统调用的 Goroutine;
-- 垃圾回收 — 在满足条件时触发垃圾收集回收内存;
-
-
-
-### 计时器
-
-计时器的实现分别经历了以下几个过程:
-
-- Go 1.9 版本之前,所有的计时器由全局唯一的四叉堆维护;
-- Go 1.10 ~ 1.13,全局使用 64 个四叉堆维护全部的计时器,每个处理器(P)创建的计时器会由对应的四叉堆维护;
-- Go 1.14 版本之后,每个处理器单独管理计时器并通过网络轮询器触发;
-
-```
-type timer struct {
-	pp puintptr
-
-	when     int64
-	period   int64
-	f        func(interface{}, uintptr)
-	arg      interface{}
-	seq      uintptr
-	nextwhen int64
-	status   uint32
-}
-
-// runtime.timer 只是计时器运行时的私有结构体,对外暴露的计时器使用 time.Timer 
-
-type Timer struct {
-	C <-chan Time
-	r runtimeTimer
-}
-
-func NewTimer(d Duration) *Timer {
-	c := make(chan Time, 1)
-	t := &Timer{
-		C: c,
-		r: runtimeTimer{
-			when: when(d),
-			f:    sendTime,
-			arg:  c,
-		},
-	}
-	startTimer(&t.r)
-	return t
-}
-
-func sendTime(c interface{}, seq uintptr) {
-	select {
-	case c.(chan Time) <- Now():
-	default:
-	}
-}
-```
-
-**time.Ticker**
-
-**time.Timer**
-
-**time.Tick**
-
-**time.After**
-
-```
-func After(d Duration) <-chan Time {
-	return NewTimer(d).C
-}
-
-func Tick(d Duration) <-chan Time {
-	if d <= 0 {
-		return nil
-	}
-	return NewTicker(d).C
-}
-```
-
-**time.AfterFunc**
-
- AfterFunc 返回的 Timer 根本不会使用到通道 C,返回的计时器不会被触发,只能用于调用 Stop 和 Reset 方法。
-
-```
-func AfterFunc(d Duration, f func()) *Timer {
-	t := &Timer{
-		r: runtimeTimer{
-			when: when(d),
-			f:    goFunc,
-			arg:  f,
-		},
-	}
-	startTimer(&t.r)
-	return t
-}
-```
-
-
-
-**(\*time.Timer).Reset**
-
-需要注意的地方是使用 `Reset` 时需要确保 `t.C` 通道被释放时才能调用,以防止发生资源竞争的问题,因为在清空 channel 和计数器到期之间存在竞争,我们无法正确使用 Reset 返回值。Reset 方法必须作用于已停止或已过期的 channel 上。
-
-假如在 Stop 调用期间触发了定时器,且 channel 存在未消费的数据, 则 C 会存在一个值。将导致后续读取是错误的。
-
- ```
-    for {
-        select {
-        case <-other:
-        case <-time.After(period):
-        }
-    }
-
-    for {
-        timer := time.NewTimer(period)
-        select {
-        case <-other:
-        case <-timer.C:
-        }
-        timer.Stop()
-    }
-
-//为了使 C 上传递的消息有效,C 应该在每次 重置 之前被消费完。
-    timer := time.NewTimer(period)
-    for {
-        if !timer.Stop() {
-            <-timer.C
-        }
-        timer.Reset(period)
-        select {
-        case <-other:
-        case <-timer.C:
-        }
-    }
-
-	timer := time.NewTimer(period)
-	for {
-		if !timer.Stop() {
-			select {
-			case <-timer.C:
-			default:
-			}
-		}
-
-		timer.Reset(period)
-		select {
-		case <-other:
-		case <-timer.C:
-		}
-	}
- ```
-
-**time.Ticker.Reset**
-
-
-
-### 内核态、用户态
-
-用户态,内核态切换耗费资源和时间,所以应该减少系统调用的操作
-
-- 基于数据收发的基本原理,系统利用阻塞提高了 CPU 利用率
-- 为了优化上线文切换,设计了“IO 多路复用”,等着收到一批数据,再一次批量的处理数据
-- 为了优化“内核与监视进程的交互”,设计了三个版本的 API(select,poll,epoll)
-
-
-
-一个进程的上下文可以分为三个部分:用户级上下文、寄存器上下文以及系统级上下文。
-
-​    (1)用户级上下文: 正文、数据、用户堆栈以及共享存储区;
-    (2)寄存器上下文: 通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP);
-    (3)系统级上下文: 进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈。
-
-进行进程切换就是上下文切换(context switch).操作系统必须对上面提到的全部信息进行切换
-
-模式切换最主要的任务只是切换进程寄存器上下文的切换
-
-一个中断上下文,通常都会始终占有CPU
-
-
-
-### 调度器
-
-- 单线程调度器 · 0.x
-
-只包含 40 多行代码;
-程序中只能存在一个活跃线程,由 G-M 模型组成;
-
-- 多线程调度器 · 1.0
-
-允许运行多线程的程序;
-全局锁导致竞争严重;线程需要经常互相传递可运行的 Goroutine,引入了大量的延迟;
-
-- 任务窃取调度器 · 1.1
-
-引入了处理器 P,构成了目前的 G-M-P 模型;
-在处理器 P 的基础上实现了基于工作窃取的调度器;
-在某些情况下,Goroutine 不会让出线程,进而造成饥饿问题;
-时间过长的垃圾回收(Stop-the-world,STW)会导致程序长时间无法工作;
-
-- 抢占式调度器 · 1.2 ~ 至今
-
-基于协作的抢占式调度器 - 1.2 ~ 1.13
-通过编译器在函数调用时插入抢占检查指令,在函数调用时检查当前 Goroutine 是否发起了抢占请求,实现基于协作的抢占式调度;
-Goroutine 可能会因为垃圾回收和循环长时间占用资源导致程序暂停;
-
-- 基于信号的抢占式调度器 - 1.14 ~ 至今
-
-实现基于信号的真抢占式调度;
-垃圾回收在扫描栈时会触发抢占调度;
-抢占的时间点不够多,还不能覆盖全部的边缘情况;
-
-- 非均匀存储访问调度器 · 提案
-
-对运行时的各种资源进行分区;
-实现非常复杂,到今天还没有提上日程;
-
-
-
-### goroutine的栈管理
-
-- Go语言栈空间来运行被调用的函数。如果空间不足,Go的运行环境就会分配更多的栈空间。因为有了这个检查机制,一个goroutine的初始栈可以很小。这样Go程序员就可以把goroutine作为相对廉价的资源来使用。 
-
-- Go 1.2 :协程的堆栈大小从 4Kb 增加到 8Kb。
-- Go 1.4 :协程的堆栈大小从 8Kb 减小到 2Kb。
-- 堆栈的大小之所以改变是因为堆栈分配的策略改变了
-
-
-
-### goroutine的阻塞
-
-长时间等待,不需要cpu的状态
-
-- 使用channel
-- 使用waitGroup
-- sync.Mutex锁
-- 阻塞的系统调用 (读写文件)
-- time.sleep 
-
-
-
-
-
-### 网络轮询器
-
-操作系统中的 I/O 多路复用机制和 Go 语言的运行时,在两个不同体系之间构建了桥梁
-
-在不同操作系统上的 I/O 操作,使用平台特定的函数实现了多个版本的网络轮询模块:
-
-- `src/runtime/netpoll_epoll.go`
-- `src/runtime/netpoll_kqueue.go`
-- `src/runtime/netpoll_solaris.go`
-- `src/runtime/netpoll_windows.go`
-- `src/runtime/netpoll_aix.go`
-- `src/runtime/netpoll_fake.go`
-
-```
-func netpoll(delay int64) gList {
-	var waitms int32
-	if delay < 0 {
-		waitms = -1
-	} else if delay == 0 {
-		waitms = 0
-	} else if delay < 1e6 {
-		waitms = 1
-	} else if delay < 1e15 {
-		waitms = int32(delay / 1e6)
-	} else {
-		waitms = 1e9
-	}
-	
-	var events [128]epollevent
-retry:
-	n := epollwait(epfd, &events[0], int32(len(events)), waitms)
-	if n < 0 {
-		if waitms > 0 {
-			return gList{}
-		}
-		goto retry
-	}
-	
-	var toRun gList
-	for i := int32(0); i < n; i++ {
-		ev := &events[i]
-		if *(**uintptr)(unsafe.Pointer(&ev.data)) == &netpollBreakRd {
-			...
-			continue
-		}
-
-		var mode int32
-		if ev.events&(_EPOLLIN|_EPOLLRDHUP|_EPOLLHUP|_EPOLLERR) != 0 {
-			mode += 'r'
-		}
-		...
-		if mode != 0 {
-			pd := *(**pollDesc)(unsafe.Pointer(&ev.data))
-			pd.everr = false
-			netpollready(&toRun, pd, mode)
-		}
-	}
-	return toRun
-}
-
-```
-
-
-
-
-
-## channel
-
-我们能对channel进行的操作只有4种: 
-
-- 创建channel (通过make()函数) 
-- 放入数据 (通过 channel <- data 操作) 
-- 取出数据 (通过 <-channel 操作) 
-- 关闭channel (通过close()函数)
-
-
-
-- 先入先出
-
-先从 Channel 读取数据的 Goroutine 会先接收到数据;
-
-先向 Channel 发送数据的 Goroutine 会得到先发送数据的权利;
-
-
-
-- 无锁管道
-
-Channel 是一个用于同步和通信的有锁队列,使用**互斥锁**解决程序中可能存在的线程竞争问题
-
-Go 语言社区也在 2014 年提出了无锁 Channel 的实现方案,目前通过 CAS 实现的无锁 Channel 没有提供先进先出的特性,所以该提案暂时也被搁浅了。
-
-Channel 在运行时使用 `runtime.hchan` 结构体表示
-
-```
-type hchan struct {
-	qcount   uint
-	dataqsiz uint
-	buf      unsafe.Pointer
-	elemsize uint16
-	closed   uint32
-	elemtype *_type
-	sendx    uint
-	recvx    uint
-	recvq    waitq
-	sendq    waitq
-
-	lock mutex
-}
-```
-
-
-
-
-
-### 同步原语与锁
-
-
-
-- sync.Mutex  互斥锁
-
-```
-type Mutex struct {
-	state int32
-	sema  uint32
-}
-
-//1s=10^3ms(毫秒)=10^6μs(微秒)=10^9ns(纳秒)=10^12ps(皮秒)=10^15fs(飞秒)=10^18as(阿秒)=10^21zm(仄秒)=10^24ym(幺秒) 
-```
-
-- 正常模式和饥饿模式
-
-```
-package main
-
-import (
-	"sync"
-	"time"
-)
-
-func main() {
-	done1 := make(chan bool, 1)
-	done2 := make(chan bool, 1)
-	var mu sync.Mutex
-
-	// goroutine 1
-	go func() {
-		for {
-			select {
-			case <-done1:
-				return
-			default:
-				mu.Lock()
-				time.Sleep(100 * time.Millisecond)
-				mu.Unlock()
-			}
-		}
-	}()
-
-	// goroutine 2
-	go func() {
-		for {
-			select {
-			case <-done2:
-				return
-			default:
-				time.Sleep(100 * time.Millisecond)
-				mu.Lock()
-				mu.Unlock()
-			}
-		}
-	}()
-
-	time.Sleep(1000 * time.Millisecond)
-	done1 <- true
-	done2 <- true
-}
-```
-
-
-
-互斥锁的加锁过程,它涉及自旋、信号量以及调度等概念:
-
-- 如果互斥锁处于初始化状态,会通过置位 `mutexLocked` 加锁;
-- 如果互斥锁处于 `mutexLocked` 状态并且在普通模式下工作,会进入自旋,执行 30 次 `PAUSE` 指令消耗 CPU 时间等待锁的释放;
-- 如果当前 Goroutine 等待锁的时间超过了 1ms,互斥锁就会切换到饥饿模式;
-- 互斥锁在正常情况下会通过 `runtime.sync_runtime_SemacquireMutex` 将尝试获取锁的 Goroutine 切换至休眠状态,等待锁的持有者唤醒;
-- 如果当前 Goroutine 是互斥锁上的最后一个等待的协程或者等待的时间小于 1ms,那么它会将互斥锁切换回正常模式;
-
-互斥锁的解锁过程:
-
-- 当互斥锁已经被解锁时,调用 `sync.Mutex.Unlock` 会直接抛出异常;
-- 当互斥锁处于饥饿模式时,将锁的所有权交给队列中的下一个等待者,等待者会负责设置 `mutexLocked` 标志位;
-- 当互斥锁处于普通模式时,如果没有 Goroutine 等待锁的释放或者已经有被唤醒的 Goroutine 获得了锁,会直接返回;在其他情况下会通过 `sync.runtime_Semrelease` 唤醒对应的 Goroutine;
-
-
-
-- 读写互斥锁 `sync.RWMutex` 是细粒度的互斥锁
-
-```
-type RWMutex struct {
-	w           Mutex
-	writerSem   uint32
-	readerSem   uint32
-	readerCount int32
-	readerWait  int32
-}
-//w — 复用互斥锁提供的能力;
-//writerSem 和 readerSem — 分别用于写等待读和读等待写:
-//readerCount 存储了当前正在执行的读操作数量;
-//readerWait 表示当写操作被阻塞时等待的读操作个数;
-```
-
-- 写操作使用 `sync.RWMutex.Lock` 和 `sync.RWMutex.Unlock` 方法;
-- 读操作使用 `sync.RWMutex.RLock` 和 `sync.RWMutex.RUnlock` 方法;
-- 读锁和写锁的关系:
-  - 调用 sync.RWMutex.Lock 尝试获取写锁时;每次 sync.RWMutex.RUnlock 都会将 readerCount 其减一,当它归零时该 Goroutine 会获得写锁;将 readerCount 减少 rwmutexMaxReaders 个数以阻塞后续的读操作;
-  - 调用 sync.RWMutex.Unlock 释放写锁时,会先通知所有的读操作,然后才会释放持有的互斥锁;
-
-
-
-- 并发竞争
-
-```golang
-package main
-
-import (
-	"fmt"
-	"sync"
-	"sync/atomic"
-)
-
-func main() {
-	var count int64
-	wg := sync.WaitGroup{}
-
-	for i := 0; i < 10; i++ {
-		wg.Add(1)
-		go func() {
-			defer wg.Done()
-			for j := 0; j < 10; j++ {
-				atomic.AddInt64(&count, int64(j))
-				//count++
-			}
-		}()
-	}
-	wg.Wait()
-	fmt.Println(count)
-}
-
-//抢占,go1.8 自旋几次再阻塞(占用资源),go1.9 饥饿模式(性能不会明显下降)
-```
-
-
-
-- goroutine 同步
-
-```
-package main
-
-import (
-	"fmt"
-	"sync"
-)
-
-func main() {
-	wg := sync.WaitGroup{}
-	wg.Add(5)
-	for i := 0; i < 5; i++ {
-		go func(i int) {
-			defer wg.Done()
-			fmt.Println(i)
-		}(i)
-	}
-	wg.Wait()
-}
-
-```
-
-```
-package main
-
-import (
-	"fmt"
-	"sync"
-)
-
-func main() {
-	wg := sync.WaitGroup{}
-	wg.Add(5)
-	for i := 0; i < 5; i++ {
-		go func() {
-			defer wg.Done()
-			fmt.Println(i)
-		}()
-	}
-	wg.Wait()
-}
-
-```
-
-```
-package main
-
-import (
-	"fmt"
-	"time"
-)
-
-func main() {
-	stop := make(chan bool)
-	go subProcess(stop)
-
-	time.Sleep(5 * time.Second)
-	fmt.Println("可以了,通知监控停止")
-	stop <- true
-	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
-	time.Sleep(3 * time.Second)
-}
-
-func subProcess(stop chan bool) {
-	for {
-		select {
-		case <-stop:
-			fmt.Println("监控退出,停止了...")
-			return
-		default:
-			time.Sleep(time.Second)
-		}
-	}
-}
-//如有很多goroutine,并且这些goroutine还衍生了其他goroutine,此时chan就比较困难解决这样的问题了
-```
-
-
-
- `sync.Cond`
-
-Go 语言标准库中还包含条件变量 `sync.Cond`,它可以让一组的 Goroutine 都在满足特定条件时被唤醒。
-
-`sync.Once`
-
-Go 语言标准库中 `sync.Once` 可以保证在 Go 程序运行期间的某段代码只会执行一次。
-
-
-
-### context
-
-```
-package main
-
-import (
-	"context"
-	"fmt"
-	"time"
-)
-
-func main() {
-	ctx, cancel := context.WithCancel(context.Background())
-	go watch(ctx, "【监控1】")
-	go watch(ctx, "【监控2】")
-	go watch(ctx, "【监控3】")
-	time.Sleep(5 * time.Second)
-	fmt.Println("可以了,通知监控停止")
-	cancel()
-	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
-	time.Sleep(3 * time.Second)
-}
-func watch(ctx context.Context, name string) {
-	for {
-		select {
-		case <-ctx.Done():
-			fmt.Println(name, "监控退出,停止了...")
-			return
-		default:
-			fmt.Println(name, "goroutine监控中...")
-			time.Sleep(time.Second)
-		}
-	}
-}
-//跟踪goroutine的方案才可以达到控制的目的,go为我们提供了Context
-```
-
-```
-
-func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
-func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
-func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
-func WithValue(parent Context, key, val interface{}) Context
-```
-
-```
-type Context interface {
-	Deadline() (deadline time.Time, ok bool)
-	Done() <-chan struct{}
-	Err() error
-	Value(key interface{}) interface{}
-}
-
-type cancelCtx struct {
-	Context
-
-	mu       sync.Mutex            // protects following fields
-	done     chan struct{}         // created lazily, closed by first cancel call
-	children map[canceler]struct{} // set to nil by the first cancel call
-	err      error                 // set to non-nil by the first cancel call
-}
-```
-
-
-
-- Context使用原则
-
-  - 不要把Context放在结构体中,要以参数的方式进行传递
-  - 以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位
-  - 给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用context.TODO
-  - Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递
-
- 
-

+ 101 - 0
xq_study/新建文本.txt

@@ -0,0 +1,101 @@
+每年半年发布一个二级版本
+
+2007年9月开始设计
+2009年11月推出
+
+Go 1.0 — 2012 年 3 月
+Go 的第一个版本,带着一份兼容性说明文档来保证与未来发布版本的兼容性,进而不会破坏已有的程序
+
+Go 1.1 — 2013 年 5 月
+这个 Go 版本专注于优化语言(编译器,gc,map,go 调度器)和提升它的性能
+
+Go 1.2 — 2013 年 12 月
+本版本中 test 命令支持测试代码覆盖范围并提供了一个新命令 go tool cover ,此命令能测试代码覆盖率
+
+Go 1.3 — 2014 年 6 月:
+这个版本对栈管理做了重要的改进。栈可以申请连续的内存片段,提高了分配的效率,使下一个版本的栈空间降到 2KB
+
+Go 1.4 — 2014 年 12 月:
+此版本带来了官方对 Android 的支持,golang.org/x/mobile ) 让我们可以只用 Go 代码就能写出简单的 Android 程序
+Go 也提供了 go generate 命令通过扫描用 //go:generate 指示的代码来简化代码生成过程
+
+Go 1.5 — 2015 年 8 月:
+实现了自举
+这个新版本,发布时间推迟了两个月,目的是在以后每年八月和二月发布新版本
+这个版本对 gc 进行了重新设计
+这个版本也发布了运行时追踪,用命令 go tool trace 可以查看
+
+Go 1.6 — 2016 年 2 月:
+这个版本最重大的变化是使用 HTTPS 时默认支持 HTTP/2
+在这个版本中 gc 等待时间也降低了
+
+Go 1.7 — 2016 年 8 月:
+这个版本发布了 context 包,为用户提供了处理超时和任务取消的方法
+对编译工具链也作了优化,编译速度更快,生成的二进制文件更小,有时甚至可以减小 20% 到 30%
+
+Go 1.8 — 2017 年 2 月:
+把 gc 的停顿时间减少到了 1 毫秒以下:其他的停顿时间已知,并会在下一个版本中降到 100 微秒以内
+这个版本也改进了 defer 函数
+
+Go 1.9 — 2017 年 8 月:
+这个版本支持下面的别名声明:type byte = uint8
+sync 包新增了一个 Map 类型,是并发写安全的
+
+Go 1.10 — 2018 年 2 月:
+test 包引进了一个新的智能 cache,运行会测试后会缓存测试结果。如果运行完一次后没有做任何修改,那么开发者就不需要重复运行测试,节省时间。
+为了加快构建速度,go build 命令现在也维持了一份最近构建包的缓存。
+
+Go 1.11 — 2018 年 8 月:
+带来了一个重要的新功能:Go modules
+第二个特性是实验性的 WebAssembly,为开发者提供了把 Go 程序编译成一个可兼容四大主流 Web 浏览器的二进制格式的能力
+
+Go 1.12 — 2019 年 2 月:
+基于 analysis 包重写了 go vet 命令,为开发者写自己的检查器提供了更大的灵活性
+
+Go 1.13 — 2019 年 9 月:
+改进了 sync 包中的 Pool,在 gc 运行时不会清除 pool。它引进了一个缓存来清理两次 gc 运行时都没有被引用的 pool 中的实例。
+重写了逃逸分析,减少了 Go 程序中堆上的内存申请的空间。
+
+Go1.14 - 2020 年 2 月:
+现在 Go Module 已经可以用于生产环境,鼓励所有用户迁移到 Module。
+该版本支持嵌入具有重叠方法集的接口。
+性能方面做了较大的改进,包括:进一步提升 defer 性能、页分配器更高效,同时 timer 也更高效。
+Goroutine 支持异步抢占。
+
+Go1.15 - 2020 年 8 月:
+Go 链接器现在具有更低的资源使用量,更快的速度以及更高的代码质量。通常,对于大型Go应用程序,链接过程比之前快 20% 左右,而内存使用消耗减少 30% 左右.
+Go 1.15 二进制文件比Go 1.14 小约5%.
+Go 对 ARM / ARM64 有了更好的 OpenBSD 支持
+现在使用更高内核数的系统,分配的小对象要比之前版本快很多.
+
+Go 1.16 — 2021 年 2 月:
+embed 包和 //go:embed 指令
+增加对 macOS ARM64 的支持
+默认启用 Module
+io/fs 包,不建议使用 io/ioutil 了,因为其中的内容移到 os 和 io 包了
+
+Go 1.17 — 2021 年 8 月:
+基于寄存器的调用惯例替代基于堆栈的调用惯例
+增加对 Windows ARM64 的支持
+新的 //go:build 新版构建约束格式化支持
+go.mod 优化
+标准库的一些改进
+
+
+Go1.18 - 2022 年初:
+如果您正在更新您的软件包以使用泛型,请考虑将新的泛型API隔离到自己的文件中,并为其使用Go 1.18的构建标签(//go:build go1.18),以便Go 1.17用户可以继续构建和使用非泛型部分。
+
+
+
+函数式编程的语言:
+只支持函数式范式(如Haskell)
+多范式+一流支持(如Scala、Elixir)
+多范式+部分支持(如Javascript、Go)。在后一类语言中,函数式编程一般通过使用社区创建的库来支持,这些库复制前两种语言的标准库中的部分或全部功能。
+
+
+Go的语法接近C语言,不包括如枚举、异常处理、继承、泛型、断言、虚函数等功能,但增加了 切片(Slice) 型、并发、管道、垃圾回收、接口(Interface)等特性
+Go内嵌了关联数组map和字符串string类型
+代码风格:内置gofmt工具
+
+
+

BIN
函数式编程思维.pdf


BIN
可计算性理论.pdf


BIN
囚徒健身 中文版.pdf


BIN
我的世界观-阿尔伯特·爱因斯坦.pdf


BIN
活出最乐观的自己(马丁·塞利格曼).pdf


BIN
程序员的职业素养.pdf


BIN
计算机程序设计艺术 第1卷:基本算法(第三版)高清中文版.pdf


BIN
计算机程序设计艺术 第2卷:半数值算法(第三版)高清中文版.pdf