编程形而上学

函数式编程

  1. 柯里化的目的有时是为了定制多个相似的函数,例如函数(a) => (b) => (c) => a + b +c。目的是为了得到一个(c) => someValue +c,someValue 取何值要视具体场景而定,这有点像一个函数的constructor. 补充:程墨大佬说函数式编程以柯里化作为传入参数的主要方式。
  2. 柯里化的目的也可以这么理解:分步传参,基础函数只能一次性传参,时常需要用多个变量存储参数,最后一次性调用函数并传参;而柯里化的函数则方便得多。
  3. compose是一种管道机制,设有纯函数a、b、c,const foo = c(b(a(arg)))。运行foo(val)时,先以c,b,a的顺序push入执行栈,然后参数arg以a,b,c的顺序return,就像gulp的pipe一样,abc的顺序至关重要。
  4. 作为高阶函数的参数的函数,自然会在高阶函数中调用,大概率会被传入高阶函数作用域中的参数,被传入的参数有可能还是函数。
  5. 函数式编程鼓励尽量不用this,一般来说也用不上。
  6. immutable的对象,需用到Object.freeze方法
  7. 只要是跟函数外部环境发生的交互就都是副作用
  8. 由于纯函数的特性“同一个输入只对应同一个输出”,可作memoize函数,形成闭包缓存,对应背包问题。
  9. 纯函数对于其依赖必须要诚实,将依赖都写在接受的参数里。
  10. 柯里化和类相似,都是蓝图。
  11. 可以考虑把有副作用的函数放入一个nameSpace严格限制
  12. 函数签名example: foo :: string -> [string]
  13. functor的威力在于: 1 约定了统一的类型,防止了链式调用中意外情况的发生 2 这使得它可以安全地链式调用,如同compose 3 更好地定制map函数的行为,可增加截流之类的功能 4 container如同vm一样,成为data和behavior的中间人,映射从行为中被抽离了出来! 5 这意味着:我们只需要关注映射——函数,而这正是声明式编程。6 进一步地演绎它,可以在map的参数中做各种函数式操作,比如compose,局部传参等等,你只需确保最后map的参数是一个纯函数即可。
  14. functor 是实现了 map 函数并遵守一些特定规则的容器类型,it’s mappable.这和Promise是thenable的有异曲同工之妙。
  15. js数组如今已经mappable了,可以作为functor使用,array在react和vue中的表现很好地印证了functor的安全性。
  16. 柯里化的局部传参或许可以很好地用于连续异步,一次异步传一次参数。
  17. 我认为函数式应该这样实践:手动声明的基础函数应完成最基础的一件事,比如obj => obj.prop,复杂的功能用compose组合基础函数,这样的代码有很好的可读性,且从具体到抽象过渡流畅,可抽象可具体。
  18. Promise是一种monad,resovle是将值置入容器中,然后触发map(f)/then(f),如果resolve的值是一个Promise,会递归resolve,一直到取到非Promise的值为止。
  19. map(map(func, functor)),这样嵌套的原因是有时functor中的值还是一个functor
  20. monad要素:of方法,join方法(flaten多层functor)
  21. 我觉得react的组件就很像一个functor:state就是_value, render就是map的参数;高阶组件就如同柯里化的高阶函数
  22. functor很像vm,是一个中间层-数据和映射的中间层,他的主要意义在于我们能把映射从操作中剥离出来并专注于它,这比起命令式编程更接近人类的思维。

面向对象编程

  1. 对象 封装状态和行为:state&behavior,行为因状态而不同,状态因行为而变化。
  2. prototype中放的基本是行为,为何?结合日常实际,一个生物之所以归类为人,是因为他的行为,还是因为他的长相和身材呢?主要还是行为吧。这种思想就是“多态”(大概)。
  3. 當一組數據和方法强烈耦合時,就要考慮封裝為對象了。
  4. 面向对象有不同的实现方式:基于类 & 原型链
  5. 原型链OOP契合"polyMorphism",关注behavior。而class侧重于类型,严谨,可避免大量错误。

抽象/软件工程/设计模式

  1. 函数封装本质是对一系列过程/行为的抽象,封装一个代码块的原因:1它将被大量重复使用 2 将一系列行为抽象为一个行为,抽象程度越高的行为使用的越多,每次复用它产生的效率就越高,系统就越有序。
  2. 函数的封装参考“单一功能”原则,一个函数最好只做一件事。“一件事”是个抽象的概念,实际操作起来一件事可以包含几件小事,几件小小事,多半还是要随机应变。个人觉得“一件事”应是你的程序中一个“尽可能大的&&可预见的未来不需要拆分”的功能。——“尽可能大”是为了高效,如果复用的函数粒度很小,则要引用更多函数完成一个功能,这样复用的效率就大打折扣。“可预见的未来不需要拆分”是为了增加可复用的次数,因为比函数更小的功能是不能用该函数实现的。
  3. 这就好像盖房子用砖头一样,砖头越大,砌砖、搬砖的次数越少,但是相应的,砖头不能填上的边角空隙就会越多,这些空隙最终还要用砂浆之类的东西来填上。
  4. 代码复用的效果:1 成倍地减少工作量-所有需要同样代码块的位置引用一个代码块 2 扩展/变换方便,修改一处即可,快且正确,不易失误。如何确保改变复用代码(util)不出问题呢?我认为应该对复用的代码在开始时就应 well-defined,定义好它的作用和行为,约定好接口。
  5. 故,复用需要提炼出代码中相同/相似的、重复的、不变的部分。完全相同的部分不必多言,相似的部分要复用,往往是柯里化函数、面向对象、compose等,换句话说就是要根据具体的使用场景一次或分多次传入参数。值得注意的是,如果传参的过程过于复杂,可能还不如不引用代码块。
  6. 前端路由就像一棵决策树,每匹配一层路由就进行一次决策,确定一块视图,所以在vue中,每多一层路由便多一个router-view,它就像一个promise,是未来决策出的视图
  7. for(){array.push()}时,这一过程是时间映射到空间;递归(树形结构)时,是空间映射到时间
  8. 一层if-elseif-else语句等于按顺序划分一个集合(所有变量可能的状态组成的集合,布尔运算一般都能对应到集合操作),if是第一刀,切走第一块,后面的elseif只能切剩下的,然后最后剩下的那块归else
  9. 函数中,每个带有return的if-else语句都是一层筛,过了筛子则继续计算,被筛掉了则中止于该return
  10. 后端数据库的复制,与内存中的复制相比,多了一层connect也就是根据父节点的外键取出子一层数据。复制完成后写回数据库是最后一步。
  11. 一个代码块(或者函数)的行为b依托于"参数及其他状态"s,从时间轴的角度看,多次调用该函数可抽象为一个数组,函数中的if类似于离散数学中的∀x(P(x)->),对于多个s组成的数组ss,代码块中的if相当于ss.filter().forEach(),多层嵌套的if-else相当于ss.filter().filter().foreEach()
  12. 关联的数据,如果为了更快/渐进的加载,由一个接口分为两个接口,可能会面临这样的风险:第一个接口数据到了,如果第二个接口慢了很多才到,就可能导致界面上的数据在一段时间内自相矛盾。而数据全部由一个接口返回就没有这样的问题。
  13. 观察者模式: 被观察者维护这样的一个数组: Array, 被观察者需要与观察者约定好-观察者上的一个钩子函数,比如hook,被观察者实现notify函数:调用所有 Array 中的 hook,如此一来双方是松散耦合,他们只需约定hook函数名。
  14. 发布订阅模式是观察者模式基础上发展而来,发布者和订阅者完全解耦,二者靠中间层通信,比较类似于消息中间件

数据类型

  1. 为何需要数据类型: 计算机底层对事物的编码是通过0101这样的电位逻辑,以number和string为例,number最重要的特性就是可以加减乘除,为此,需要把数字以二进制的形式存储于多位晶体管,这个过程中,一个number只对应一个编码/状态,也只有这样number才能参与计算,比如number++,只要把第一位晶体管异或门判断,记录下进位并和下一位重复这一过程。
  2. 如果是string,数据的主要行为就是拼接,匹配之类的操作,字符对应的编码完全是人为约定的,只要不同的字符不会对应同一个编码即可,因此才会有字符编码的多个标准,比如ASCII和UTF-8。
  3. 所以如果没有数据类型,string就可以被用来加减乘除,把ASCII的a(编码65)加一,编码变为66,就变成了b,‘a’+1=‘b’?,that make no sense。
  4. http RESTful api:用uri(url)来指代网络上的资源, [get,post,put,delete]作为方法,相当于: class Resource { Data data, Data get(), void post(Data d), void put(param), void delete(param) }

离散数学

  1. 蕴含等值 P∨Q <=> ┐P→Q ,可用集合理解:已知某元素在集合P或集合Q中,可得出结论,若该元素不在集合P中则必然在Q中。
  2. “所有”意味着一堆∧,“存在”意味着一堆∨
  3. 大部分情况下,一个值可以对应无限多个表达式,例如:1这个值可以表达为1 或 2-1 或 3/3等等,计算一个表达式,实际上是一个表达式转化为等价的另一个表达式的过程,那么转化为怎样的表达式才是终点呢?应该是最一目了然的那个。比如单个数字就非常一目了然。
  4. 集合与布尔值,相伴而生。布尔值可以表达为在集合内或集合外,而集合由来自是否(布尔值)满足特性。
  5. false意味着在集合外,又意味着在集合的补集内,从补集的角度看,又成了true
  6. 由真值表得出基本的proposition equilavences, 再由它们得出复杂命题的equilavence
  7. de morgan’s law: 非(p1或p2或p3…) equilavent 非p1与非p2与非p3…。一种解释是:非(所有的x都P) <=> 存在x非P
  8. 一个真假未定的命题(一般含有个体变量)对应着一个集合。它的值为真的情况代表一个状态落在这个集合内。
  9. 个人觉得 ∀x(P->Q) 有筛选的意味: array.filter(x => P)辖域内∀xQ
  10. 命题中的前置量词:对于多个量词quatifier ∀ ∃的顺序,如果都是同性则顺序无关紧要,如果不同则顺序不可随意调换。最外层的量词有决定性的影响。
  11. 大多表达式中的operator都基于二元关系(两个变量),因此复杂表达式往往可以通过分治,层层二元拆解,最终底层都是二元表达式。
  12. 状态机中状态的迁移可以构成一张图(图论的图),每个状态机是一个函数,每个函数对应着图中的一个node和这个node为起点的edge,函数的参数便是选择edge从而到达下一个状态,所以一个状态机往往返回另一个状态机或者终点状态,也就是从一个状态到另一个状态(有点像向量的概念)。

图论

  1. 图论中的一些概念:无序积:用来形容无向图的边 n阶图,n为顶点个数 顶点的邻域:与该顶点相连的点组成的集合 握手定理:无向图中度(单个顶点的边数)的总和等于边的总和的两倍,推论:度为奇数的顶点,个数必定是偶数 对于数列d,如果它每项能映射到一张图的一个顶点的度,则说这个数列可图化,该数列是该图的度序列。 同构:两个图,可以通过移动顶点最终达到完全重合 r部图,r为int,当r=2时称为2部图或对偶图 补图、自补图:一个图的补图与自身同构 超立方体图可用于并行计算连接cpu 子图 不含平行边也不包含自环的图称为简单图 操作(都是以边为准):删除、收缩(两个相连的点合成一个点)、并图、交图、差图、联图(可产生完全2分图)、积图(把一个图中每个顶点换成另一个图)
  2. 如果一个图中全是偶数长度的回路而不存在基数长度的回路,则此图可成为二部图。分法为选定一个基点,同基点距离偶数的同基点分为一部,距离偶数的分为另一部(当回路长度为奇数时,从一侧出发距离为偶数,而另一侧的距离为奇数,一致性被破坏)

编译原理

  1. 编译原理大概分为两部分: 词法分析和语法分析
  2. 语法分析中有一个概念/语言为BNF,目前我对于它的理解(不一定正确)为:consider this example:
<a> ::= 
	<b>
	|
	<a><+><b>
	|
	<a><-><b>

这里的|链接部分基本等同于if-else,也就是说它是有先后顺序的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值