-
Notifications
You must be signed in to change notification settings - Fork 0
/
curryingAndUncurrying.html
231 lines (206 loc) · 9.64 KB
/
curryingAndUncurrying.html
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
<title>函数的柯里化(currying)与反柯里化(uncurrying)</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
</head>
<body>
<p>首先我们讨论的是函数柯里化(function
currying)。currying的概念最早由俄国数学家
Moses Schönfinkel发明,而后由著名的数理逻辑
学家Haskell Curry将其丰富和发展,currying由
此得名。
currying又称部分求值。一个currying的函
数首先会接受一些参数,接受了这些参数之后,
该函数并不会立即求值,而是继续返回另外一个
函数,刚才传入的参数在函数形成的闭包中被保
存起来。待到函数被真正需要求值的时候,之前
传入的所有参数都会被一次性用于求值。</p>
<p>接下来我们编写一个通用的function
currying(){},function currying(){}
接受一个参数,即将要被currying的函数。在
这个例子里,这个函数的作用遍历本月每天的开
销并求出它们的总和。代码如下:
</p>
<pre>
var currying = function (fn) {
var args = [];
console.log(this);//调用currying的是window
return function () {
console.log(this);//调用fn的是xiaoMing
if (arguments.length === 0) {
// 当没有参数传入时,计算之前保存的所有参数
// switch (args.length) {
// case 0:
// return fn()
// break;
// case 1:
// return fn(args[0])
// break;
// case 2:
// return fn(args[0], args[1])
// break;
// case 3:
// return fn(args[0], args[1], args[2])
// break;
// case 4:
// return fn(args[0], args[1], args[2], args[3])
// break;
// // ……
// }
//因为参数是保存在数组里,且不确定是几个参数,所以用apply将整个数组传入
return fn.apply(this, args);//修改fn的this指向为xiaoMing,并运行fn(即xiaoMing.cost)
} else {
//将类数组arguments里的参数传入args保存,用apply调用数组的push方法
[].push.apply(args, arguments);
//返回本函数
return arguments.callee;
}
}
};
var xiaoMing = {
name: "小明",
cost: (function () {
var money = 0;
return function () {
for (var i = 0, l = arguments.length;
i < l; i++) {
money += arguments[i];
}
return money;
}
})()
};
xiaoMing.cost = currying(xiaoMing.cost);// 转化成currying函数
xiaoMing.cost(2); // 未真正求值
xiaoMing.cost(4); // 未真正求值
xiaoMing.cost(6); // 未真正求值
console.log(xiaoMing.cost()); // 求值并输出:12
//`方法中用到this的地方就不再局限于原来规定的对象,而是加以泛化并得到更广的适用性。`
// `把泛化this的过程提取出来,就是uncurrying`
console.log("--------- 以下是uncurrying的实现 -------------");
Function.prototype.uncurrying = function () {
var self = this; //保留一下原生的方法(数组的push/shift foreach)
return function () {
//获取第一个参数(这应该是一个对象)
var obj = Array.prototype.shift.call(arguments);//返回值数组原来的第一个元素的值。
return self.apply(obj, arguments);
};
};
//赋值语句会返回那个值,当值不存在时返回undefined,转成boolean就是false,跳出循环。
for (var i = 0, fn, ary = ['push', 'shift', 'forEach']; fn = ary[i]; i++) {
Array[fn] = Array.prototype[fn].uncurrying();
};
var obj = {
"length": 3,
"0": 1,
"1": 2,
"2": 3
};
Array.push(obj, 4); // 向对象中添加一个元素
console.log(obj.length); // 输出:4
var first = Array.shift(obj); // 截取第一个元素
console.log(first); // 输出:1
console.log(obj); // 输出:{0: 2, 1: 3, 2:4, length: 3}
Array.forEach(obj, function (i, n) {
console.log(n); // 分别输出:0, 1, 2
});
</pre>
<script>
var currying = function (fn) {
var args = [];
console.log(this);//调用currying的是window
return function () {
console.log(this);//调用fn的是xiaoMing
if (arguments.length === 0) {
// 当没有参数传入时,计算之前保存的所有参数
// switch (args.length) {
// case 0:
// return fn()
// break;
// case 1:
// return fn(args[0])
// break;
// case 2:
// return fn(args[0], args[1])
// break;
// case 3:
// return fn(args[0], args[1], args[2])
// break;
// case 4:
// return fn(args[0], args[1], args[2], args[3])
// break;
// // ……
// }
//因为参数是保存在数组里,且不确定是几个参数,所以用apply将整个数组传入
return fn.apply(this, args);//修改fn的this指向为xiaoMing,并运行fn(即xiaoMing.cost)
} else {
//将类数组arguments里的参数传入args保存,用apply调用数组的push方法
[].push.apply(args, arguments);
//返回本函数
return arguments.callee;
}
}
};
var xiaoMing = {
name: "小明",
cost: (function () {
var money = 0;
return function () {
for (var i = 0, l = arguments.length;
i < l; i++) {
money += arguments[i];
}
return money;
}
})()
};
xiaoMing.cost = currying(xiaoMing.cost);// 转化成currying函数
xiaoMing.cost(2); // 未真正求值
xiaoMing.cost(4); // 未真正求值
xiaoMing.cost(6); // 未真正求值
console.log(xiaoMing.cost()); // 求值并输出:12
console.log("--------- 以下是uncurrying的实现 -------------");
Function.prototype.uncurrying = function () {
var self = this; //保留一下原生的方法(数组的push/shift foreach)
return function () {
//获取第一个参数(这应该是一个对象)
var obj = Array.prototype.shift.call(arguments);//返回值数组原来的第一个元素的值。
return self.apply(obj, arguments);
};
};
//另一种实现
Function.prototype.uncurrying2 = function () {
var self = this; console.log(self);
return function () {
return Function.prototype.call.apply(self, arguments);
//0、将参数代进来理解
//Function.prototype.call.apply(Array.prototype.push, [obj, 2] );
//1、apply替换的执行函数的对象并扁平化了数组内容,
//Array.prototype.push.call(obj, 2);
//2、call函数将数组内容的第一个参数替换执行函数对象
//obj.push(2);
}
};
//赋值语句会返回那个值,当值不存在时返回undefined,转成boolean就是false,跳出循环。
for (var i = 0, fn, ary = ['push', 'shift', 'forEach']; fn = ary[i]; i++) {
Array[fn] = Array.prototype[fn].uncurrying();
};
var obj = {
"length": 3,
"0": 1,
"1": 2,
"2": 3
};
Array.push(obj, 4); // 向对象中添加一个元素
console.log(obj.length); // 输出:4
var first = Array.shift(obj); // 截取第一个元素
console.log(first); // 输出:1
console.log(obj); // 输出:{0: 2, 1: 3, 2:4, length: 3}
Array.forEach(obj, function (i, n) {
console.log(n); // 分别输出:0, 1, 2
});
</script>
</body>
</html>