纯函数 pure function
- 引用透明(Referential transparency),对于同样的传入参数永远返回同样的输出值,意味着不能依赖可变状态
- 无副作用(side-effect free),副作用可以是I/O操作,修改可变对象,重新赋值对象等等。
Advantages of functional programming
- 易于编写和调试,因为不依赖可变状态
- 返回值可以被缓存或memoized,来避免重复计算
- 易于测试,因为没有依赖,不用像logging,ajax,database之类需要模拟数据测试
- 把数据从逻辑中分离
higher-order functions : 要么把函数当做参数,要么返回一个函数的函数
利用Object.freeze阻止对象值修改
没有返回值的函数往往是空函数或者是有副作用的函数
// identity 同一律
map(id) === id;
// composition 组合律
compose(map(f), map(g)) === map(compose(f, g));
柯里化 Currying
currying is the process of taking a function that accepts n arguments and turning it into n functions that each accepts a single argument.
在计算机科学中,柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
不可变数据结构
惰性求值
函数组合
尾递归优化
向量
向量是带有索引的一组数据,是不可变数据结构immutable,是持久性数据结构persistent
持久性是指数据结构在被操作的时候永远保持着前一版本
不可变性是指在被创建之后再也不能改变
所以持久性约束的是操作,不可变性约束的是数据
线程不安全是指一个值会被多个线程中的操作同时修改,带来的问题是你很难预测以及重现这值在某个时间到底是什么,解决线程安全通常会用到互斥锁,原子操作等,这些方式大大的增加了编程和测试的难度。
不可变性保证了不管是主线程代码还是回调函数,拿到的值都能一直保持不变,所以不再需要关心会出现线程安全问题。
mori
方括号代表向量
圆括号代表惰性序列(Lazy Sequence)
threshold
在不可变的前提下,如果真的需要循环,那么只能使用递归
柯里悖论的求解?
非尾递归例子:
function fact(n) {
if(n==0) return 1;
return n * fact(n-1);
}
尾递归例子:
function fact(n, acc=1) {
if(n==0) return acc;
return fact(n-1, acc * n);
}
尾递归优化成循环:
function fact(n) {
let acc=1, i=n;
while(i!=0) {
acc = acc * i;
i--;
}
return acc;
}
普通递归可以优化成尾递归再优化成循环
相互递归 Mutual Recursion
有穷状态机 DFA
Trampoline 是一个函数:
- 接收一个函数,一个或多个该函数的参数
- 调用该函数
- 如果返回值是个函数,调用返回的函数
- 如果返回值不是个函数,停止,返回
相互递归最终的优化依然靠循环
mori.trampoline(eat_money, input_seq)
function eat_money() {
if(input_seq.length == 0) return;
let input = input_seq.shift();
if(input == "five") {
return function() {return choose(input_seq)}
} else {
return function() {return eat_money(input_seq)}
}
}
// trampoline实现的大致过程
let res = eat_money(input_seq);
while(true) {
if(typeof res == "function") res = res();
else break;
}
自由变量 Free 与 约束变量 Bound , 自由变量指的是函数中既不是参数,也不是局部变量的那个变量,约束变量就是参数或局部变量
函数(约束1)(约束2)(约束3)... => 值
mori.reduce(fn,0,[1,2,3]);
mori.reduce(mori.sum,0,mori.map(mori.inc, [1,2,3])); // 9
Reducer
- 接收一个xf函数和一个collection
- 用xf转换reducing函数,并应用到collection
Transducer就是那个xf
Reducer的出现只是因为想保持原始reduce的API不变,而Transducer的引入则是提供了另外一种reduce API:
transduce(transducer, reducing, initValue, collection);
REPL Read-Eval-Print-Loop
core.async
goroutine
Sweet.js
clojure
参考库:
- Ramda