函数式编程(一些简单记录)

函数式编程

一等公民

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 太傻了
const getServerStuff = callback => ajaxCall(json => callback(json));
// 这才像样
const getServerStuff = ajaxCall;

// 这行
ajaxCall(json => callback(json));

// 等价于这行
ajaxCall(callback);

// 那么,重构下 getServerStuff
const getServerStuff = callback => ajaxCall(callback);

// ...就等于
const getServerStuff = ajaxCall // <-- 看,没有括号哦
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 可笑的代码
const BlogController = {
index(posts) { return Views.index(posts); },
show(post) { return Views.show(post); },
create(attrs) { return Db.create(attrs); },
update(post, attrs) { return Db.update(post, attrs); },
destroy(post) { return Db.destroy(post); },
};

// 这才对
const BlogController = {
index: Views.index,
show: Views.show,
create: Db.create,
update: Db.update,
destroy: Db.destroy,
};

纯函数

纯函数 没有副作用,只要输入不变 输出就不会变

​ 纯函数的理由

#####可缓存性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var squareNumber  = memoize(function(x){ return x*x; });

squareNumber(4);
//=> 16

squareNumber(4); // 从缓存中读取输入值为 4 的结果
//=> 16

squareNumber(5);
//=> 25

squareNumber(5); // 从缓存中读取输入值为 5 的结果
//=> 25

//memoize
var memoize = function(f) {
var cache = {};

return function() {
var arg_str = JSON.stringify(arguments);
cache[arg_str] = cache[arg_str] || f.apply(f, arguments);
return cache[arg_str];
};
};

柯里化

curry 的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

「我父亲以前跟我说过,有些事物在你得到之前是无足轻重的,得到之后就不可或缺了。微波炉是这样,智能手机是这样,互联网也是这样——老人们在没有互联网的时候过得也很充实。对我来说,函数的柯里化(curry)也是这样。」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
var curry = require('lodash').curry;

var match = curry(function(what, str) {
return str.match(what);
});

var replace = curry(function(what, replacement, str) {
return str.replace(what, replacement);
});

var filter = curry(function(f, ary) {
return ary.filter(f);
});

var map = curry(function(f, ary) {
return ary.map(f);
});


match(/\s+/g, "hello world");
// [ ' ' ]

match(/\s+/g)("hello world");
// [ ' ' ]

var hasSpaces = match(/\s+/g);
// function(x) { return x.match(/\s+/g) }

hasSpaces("hello world");
// [ ' ' ]

hasSpaces("spaceless");
// null

filter(hasSpaces, ["tori_spelling", "tori amos"]);
// ["tori amos"]

var findSpaces = filter(hasSpaces);
// function(xs) { return xs.filter(function(x) { return x.match(/\s+/g) }) }

findSpaces(["tori_spelling", "tori amos"]);
// ["tori amos"]

var noVowels = replace(/[aeiou]/ig);
// function(replacement, x) { return x.replace(/[aeiou]/ig, replacement) }

var censored = noVowels("*");
// function(x) { return x.replace(/[aeiou]/ig, "*") }

censored("Chocolate Rain");
// 'Ch*c*l*t* R**n'

组合

1
2
3
4
5
var compose = function(f,g) {
return function(x) {
return f(g(x));
};
};

组合让代码从右向左运行,而不是由内而外运行。

声明式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 命令式
// 把各种零零散散的东西都展示出来...实在是直白得有些露骨。
var makes = [];
for (i = 0; i < cars.length; i++) {
makes.push(cars[i].make);
}
// 声明式
var makes = cars.map(function(car){ return car.make; });


// 命令式
var authenticate = function(form) {
var user = toUser(form);
return logIn(user);
};

// 声明式
var authenticate = compose(logIn, toUser);