函数柯里化

2018.10.22 星期一

颗粒化

柯里化(英语:Currying),又称为部分求值,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回一个新的函数的技术,新函数接受余下参数并返回运算结果。


1
2
3
4
5
6
7
8
9
10
var currying = function (fn) {
var _args = [];
return function () {
if (arguments.length === 0) {
return fn.apply(this, _args);
}
Array.prototype.push.apply(_args, [].slice.call(arguments));
return arguments.callee;
}
};

1 提高适用性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function square(i) {return i * i;}
function dubble(i) {return i *= 2;}
function map(handeler, list) {
return list.map(handeler);
}
// ## 未currying ,每次都需要 map(handle,list)
map(square, [1, 2, 3, 4, 5]);
map(dubble, [1, 2, 3, 4, 5]);
// ## currying,定义的时候绑定handle,直接传数据就可以了
function currying(fn) {
var slice = Array.prototype.slice,
__args = slice.call(arguments, 1);
return function () {
var __inargs = slice.call(arguments);
return fn.apply(null, __args.concat(__inargs));
};
}
var mapSQ = currying(map, square);
var mapDB = currying(map, dubble);
mapSQ([1, 2, 3, 4, 5]);
mapDB([1, 2, 3, 4, 5]);

$PS: 利用闭包,绑定

2 延迟执行

柯里化的另一个应用场景是延迟执行。不断的柯里化,累积传入的参数,最后执行。

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// # 0 简单:只能两个括号 $PS: 其实和第一条一样,利用闭包
var add = function(x) {
return function(y) {
return x + y;
};
};
console.log(add(1)(1)); // 输出2
var add1 = add(1);
console.log(add1(1)); // 输出2
var add10 = add(10);
console.log(add10(1)); // 输出11
// # 1 每次都是新结果,可以混合传参
// ## 1 直接定义 \#1
var add = function() {
var _args = arguments;
return function() {
if (!arguments.length) {
var sum = 0;
for (var i = 0,c; c = _args[i++];) {
sum += c
}
return sum
} else {
Array.prototype.push.apply(_args, arguments)
return arguments.callee
}
}
}
add(1)(2)(3)(4)();//10
add(5,3,9)(4)() // 21 // 正确
// ## 2 利用toString #3
function add() {
var _args = [].slice.call(arguments);
var adder = function () {
var _adder = function() {
[].push.apply(_args, [].slice.call(arguments));
return _adder;
};
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
return adder.apply(null, [].slice.call(arguments));
}
// 输出结果,可自由组合的参数
console.log(add(1, 2, 3, 4, 5)); //f 15
console.log(add(1, 2, 3, 4)(5)); // f 15
console.log(add(1)(2)(3)(4)(5)); // f 15
console.log(add(1)(2,3)(3,5).toString()) // 14
alert(add(1,2)(3)) // alert: 6 true
/*
1)当使用console.log,或者进行运算时,隐式转换就会发生。
2) 当我们没有重新定义toString与valueOf时,函数的隐式转换会调用默认的toString方法,它会将函数的定义内容作为字符串返回。而当我们主动定义了toString/vauleOf方法时,那么隐式转换的返回结果则由我们自己控制了。
3) 其中valueOf的优先级会toString高一点。 */


// # 2 原来基础上增加
// ## 2.1_加法 不可以混合,只有第一个参数有意义
function add(n){//n
return function(m){
n+=m;
arguments.callee.toString=function(){
return n;
}
return arguments.callee;//引用当前正在调用的函数本身
}
}
//alert(add(1)(2)(3)/*.toString()*/);
//alert(add(1)(2)(3)(4)/*.toString()*/);

// ## 2.1_乘法 不可以混合,只有第一个参数有意义
function fn(n){
var count=n;
var tem=function(m){
count=count*m;
return tem;
}
tem.toString=function(){
return count;
}
return tem;
}
console.log(fn(2)(3)(4)) // f 22*3*4
console.log(fn(2,3)(3,4)(5,3)) // f 30=2*3*5
console.log(fn(2,3)(3,4)(5,3).toString) // 30

// ## 2.2 通用写法 \#1 \#2 可以混合传参
var curring = function(fn){
var _args = [];
return function cb(){
// return function () { // \#4
if(arguments.length === 0) {
return fn.apply(this, _args);
}
Array.prototype.push.apply(_args, [].slice.call(arguments));
return cb; //
// return arguments.callee; // \#4
}
}
var multi = function(){ // \#2
var total = 0;
var argsArray = Array.prototype.slice.call(arguments);
argsArray.forEach(function(item){
total += item;
})
/* \#4
for (var i = 0, c; c = arguments[i++];) {
total += c;
} */
return total
};
var calc = curring(multi);
calc(1,2)(3)(4,5,6);
console.log(calc()); // 21 //空白调用时才真正计算
calc(1,2)(3)()// 27 // 是在原来(21)的基础上增加的

3 固定易变因素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Function.prototype.bind = function(context) {
var _this = this,
_args = Array.prototype.slice.call(arguments, 1);
return function() {
return _this.apply(context, _args.concat(Array.prototype.slice.call(arguments)))
}
}
// 简单点
Object.prototype.bind = function(context) {
var _this = this;
var args = [].prototype.slice.call(arguments, 1);
return function() {
return _this.apply(context, args)
}
}
// ## 0 直观写法 #2
Function.prototype.bind = function(ctx) {
var fn = this;
return function() {
fn.apply(ctx, arguments);
};
};

函数记忆

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
var isPrime1=(function(){
var hash={};
return function(n){
if(n<=3){
return true;
}else if(hash[n]!==undefined){
return hash[n];
}else{
for(var i=2;i<Math.sqrt(n);i++){
if(n%i==0){
return hash[n]=false;
}
}
return hash[n]=true;
}
}
})();
Function.prototype.toMemory=function(){
var hash={};
var fun=this;
return function(n){
if(hash[n]!==undefined){
return hash[n];
}else{
return hash[n]=fun.call(null,n);
}
}
}
function isPrime2(n){
if(n<=3){
return true
}else{
for(var i=2;i<Math.sqrt(n);i++){
if(n%i==0){
return false;
}
}
return true;
}
}
isPrime2=isPrime2.toMemory();

17:00

knowledge is no pay,reward is kindness
0%