Skip to content

Commit 3d02e86

Browse files
allenlongbaobaoleviding
authored andcommitted
1-js/06-advanced-functions/11-currying-partials (#113)
* Update article.md * Update task.md * Update solution.md * Update task.md * Update task.md * 按第一位校对者意见修改完毕 * Update article.md * Update article.md
1 parent 51ceaf2 commit 3d02e86

File tree

3 files changed

+91
-91
lines changed

3 files changed

+91
-91
lines changed

1-js/06-advanced-functions/11-currying-partials/1-ask-currying/solution.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11

22

3-
1. Either use a wrapper function, an arrow to be concise:
3+
1. 使用封装函数,箭头函数很简洁
44

55
```js
66
askPassword(() => user.login(true), () => user.login(false));
77
```
88

9-
Now it gets `user` from outer variables and runs it the normal way.
9+
现在它从外部变量中获得 `user`,正常运行。
1010

11-
2. Or create a partial function from `user.login` that uses `user` as the context and has the correct first argument:
11+
2. `user.login` 中创建偏函数,使用 `user` 作为上下文,并确定第一个参数:
1212

1313

1414
```js

1-js/06-advanced-functions/11-currying-partials/1-ask-currying/task.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ importance: 5
22

33
---
44

5-
# Partial application for login
5+
# 偏函数在登录中的应用
66

7-
The task is a little more complex variant of <info:task/question-use-bind>.
7+
这个任务是比 <info:task/question-use-bind> 略微复杂的变体。
88

9-
The `user` object was modified. Now instead of two functions `loginOk/loginFail`, it has a single function `user.login(true/false)`.
9+
`user` 对象被修改了。现在不是两个函数 `loginOk/loginFail`,现在只有一个函数 `user.login(true/false)`
1010

11-
What to pass `askPassword` in the code below, so that it calls `user.login(true)` as `ok` and `user.login(false)` as `fail`?
11+
以下代码中,向 `askPassword` 传入什么参数,使得 `user.login(true)` 结果是 `ok``user.login(fasle)` 结果是 `fail`
1212

1313
```js
1414
function askPassword(ok, fail) {
@@ -30,5 +30,5 @@ askPassword(?, ?); // ?
3030
*/!*
3131
```
3232
33-
Your changes should only modify the highlighted fragment.
33+
你只能更改高亮部分代码。
3434

1-js/06-advanced-functions/11-currying-partials/article.md

Lines changed: 83 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,29 @@ libs:
33

44
---
55

6-
# Currying and partials
6+
# 柯里化和偏函数
77

8-
Until now we have only been talking about binding `this`. Let's take it a step further.
8+
到目前为止,对于 bind 我们只讨论过 bind `this`。让我们深入探讨一下 bind。
99

10-
We can bind not only `this`, but also arguments. That's rarely done, but sometimes can be handy.
10+
我们能够绑定的不只是 `this`,还有参数。尽管很少这样做,但是有时候却很方便。
1111

12-
The full syntax of `bind`:
12+
`bind` 的完整语法:
1313

1414
```js
1515
let bound = func.bind(context, arg1, arg2, ...);
1616
```
1717

18-
It allows to bind context as `this` and starting arguments of the function.
18+
可以看出,它允许将上下文绑定到 `this`,以及函数的前几个参数。
1919

20-
For instance, we have a multiplication function `mul(a, b)`:
20+
举个例子,我们有一个做乘法运算的函数 `mul(a,b)`
2121

2222
```js
2323
function mul(a, b) {
2424
return a * b;
2525
}
2626
```
2727

28-
Let's use `bind` to create a function `double` on its base:
28+
基于它,我们利用 `bind` 创造一个新函数 `double`
2929

3030
```js run
3131
*!*
@@ -37,13 +37,13 @@ alert( double(4) ); // = mul(2, 4) = 8
3737
alert( double(5) ); // = mul(2, 5) = 10
3838
```
3939

40-
The call to `mul.bind(null, 2)` creates a new function `double` that passes calls to `mul`, fixing `null` as the context and `2` as the first argument. Further arguments are passed "as is".
40+
`mul.bind(null, 2)` 创造了一个新函数 `double`,传递调用到 `mul` 函数,以 `null` 为上下文,`2` 为第一个参数。之后的参数等待传入。
4141

42-
That's called [partial function application](https://en.wikipedia.org/wiki/Partial_application) -- we create a new function by fixing some parameters of the existing one.
42+
这就是 [偏函数应用](https://en.wikipedia.org/wiki/Partial_application) —— 我们创造了一个新函数,同时将部分参数替换成特定值。
4343

44-
Please note that here we actually don't use `this` here. But `bind` requires it, so we must put in something like `null`.
44+
请注意通常我们不在这里使用 `this`,但是 `bind` 需要一个值,我们必须传入,那么可以是 `null` 这样的值。
4545

46-
The function `triple` in the code below triples the value:
46+
以下代码中的 `triple` 函数对一个值做三倍运算:
4747

4848
```js run
4949
*!*
@@ -55,23 +55,23 @@ alert( triple(4) ); // = mul(3, 4) = 12
5555
alert( triple(5) ); // = mul(3, 5) = 15
5656
```
5757

58-
Why do we usually make a partial function?
58+
为什么我们经常会创建一个偏函数?
5959

60-
Here our benefit is that we created an independent function with a readable name (`double`, `triple`). We can use it and don't write the first argument of every time, cause it's fixed with `bind`.
60+
这里,我们从中受益的是我们创建了一个独立的非匿名函数(`double``triple`)。我们可以使用它,而不需要每次都传入第一个参数,因为 `bind` 帮我们搞定了。
6161

62-
In other cases, partial application is useful when we have a very generic function, and want a less universal variant of it for convenience.
62+
在其他的场景中,当我们有一个非常通用的函数,并且想要方便地获取它的特定变体,偏函数也是非常有用。
6363

64-
For instance, we have a function `send(from, to, text)`. Then, inside a `user` object we may want to use a partial variant of it: `sendTo(to, text)` that sends from the current user.
64+
举个例子,我们拥有函数 `send(from, to, text)`。然后,在 `user` 对象中,我们想要使用它的偏函数变体:`sendTo(to, text)`,该函数表明发送自一个当前的用户。
6565

66-
## Going partial without context
66+
## 无上下文使用偏函数
6767

68-
What if we'd like to fix some arguments, but not bind `this`?
68+
如果我们想要输入一些参数,但是不想绑定 `this`,该怎么做?
6969

70-
The native `bind` does not allow that. We can't just omit the context and jump to arguments.
70+
原生的 `bind` 不允许这样。我们不能忽略上下文,直接跳到参数。
7171

72-
Fortunately, a `partial` function for binding only arguments can be easily implemented.
72+
幸运的是,一个只绑定参数的 `偏函数` 很容易实现。
7373

74-
Like this:
74+
就像这样:
7575

7676
```js run
7777
*!*
@@ -82,38 +82,38 @@ function partial(func, ...argsBound) {
8282
}
8383
*/!*
8484

85-
// Usage:
85+
// 用法:
8686
let user = {
8787
firstName: "John",
8888
say(time, phrase) {
8989
alert(`[${time}] ${this.firstName}: ${phrase}!`);
9090
}
9191
};
9292

93-
// add a partial method that says something now by fixing the first argument
93+
// 添加一个偏函数方法,现在 say 这个函数可以作为第一个函数
9494
user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());
9595

9696
user.sayNow("Hello");
97-
// Something like:
97+
// 结果就像这样:
9898
// [10:00] John: Hello!
9999
```
100100

101-
The result of `partial(func[, arg1, arg2...])` call is a wrapper `(*)` that calls `func` with:
102-
- Same `this` as it gets (for `user.sayNow` call it's `user`)
103-
- Then gives it `...argsBound` -- arguments from the `partial` call (`"10:00"`)
104-
- Then gives it `...args` -- arguments given to the wrapper (`"Hello"`)
101+
`partial(func[, arg1, arg2...])` 调用的结果是一个基于 `func` 的封装函数,以及:
102+
- 和它传入的函数一致的 `this` (对于 `user.sayNow` 调用是 `user`)
103+
- 然后传入 `...argsBound` —— 来自偏函数调用传入的参数(`"10:00"`
104+
- 然后传入 `...args` —— 传入封装函数的参数(`Hello`
105105

106-
So easy to do it with the spread operator, right?
106+
利用扩展操作符,一切都是那么简单,不是吗?
107107

108-
Also there's a ready [_.partial](https://lodash.com/docs#partial) implementation from lodash library.
108+
同样这里有一个 lodash 库实现的偏函数[_.partial](https://lodash.com/docs#partial)
109109

110-
## Currying
110+
## 柯里化
111111

112-
Sometimes people mix up partial function application mentioned above with another thing named "currying". That's another interesting technique of working with functions that we just have to mention here.
112+
有时候人们会把偏函数应用和另一个名为「柯里化」的东西混淆。那是另一个和函数有关的有趣的技术,我们在这里不得不提。
113113

114-
[Currying](https://en.wikipedia.org/wiki/Currying) is translating a function from callable as `f(a, b, c)` into callable as `f(a)(b)(c)`.
114+
[Currying](https://en.wikipedia.org/wiki/Currying) 是一项将一个调用形式为 `f(a, b, c)` 的函数转化为调用形式为 `f(a)(b)(c)` 的技术。
115115

116-
Let's make `curry` function that performs currying for binary functions. In other words, it translates `f(a, b)` into `f(a)(b)`:
116+
接下来让我们创建将两个函数连接起来的「柯里」函数。换句话说,它将 `f(a, b)` 转化为 `f(a)(b)`
117117

118118
```js run
119119
*!*
@@ -126,7 +126,7 @@ function curry(func) {
126126
}
127127
*/!*
128128

129-
// usage
129+
// 用法
130130
function sum(a, b) {
131131
return a + b;
132132
}
@@ -136,79 +136,79 @@ let carriedSum = curry(sum);
136136
alert( carriedSum(1)(2) ); // 3
137137
```
138138

139-
As you can see, the implementation is a series of wrappers.
139+
你可以看到,它的实现就是一系列的封装。
140140

141-
- The result of `curry(func)` is a wrapper `function(a)`.
142-
- When it is called like `sum(1)`, the argument is saved in the Lexical Environment, and a new wrapper is returned `function(b)`.
143-
- Then `sum(1)(2)` finally calls `function(b)` providing `2`, and it passes the call to the original multi-argument `sum`.
141+
- `curry(func)` 的结果就是一层封装 `function(a)`
142+
- 当它被调用,就像 `sum(1)` 这样,它的参数被保存在词法环境中,然后返回一层新的封装 `function(b)`
143+
- 然后 `sum(1)(2)` 最后调用 `function(b)`,传入参数 `2`,它将调用传递给初始的多参数函数 `sum`
144144

145-
More advanced implementations of currying like [_.curry](https://lodash.com/docs#curry) from lodash library do something more sophisticated. They return a wrapper that allows a function to be called normally when all arguments are supplied *or* returns a partial otherwise.
145+
关于柯里函数更多高级的实现,比如 lodash 库 [_.curry](https://lodash.com/docs#curry) 所做的那样,它们更加复杂。它们会返回一个封装,允许函数提供所有的参数时被正常调用**或者**返回一个偏函数。
146146

147147
```js
148148
function curry(f) {
149149
return function(...args) {
150-
// if args.length == f.length (as many arguments as f has),
151-
// then pass the call to f
152-
// otherwise return a partial function that fixes args as first arguments
150+
// 如果 args.length == f.length(args 和 f 的参数数量相同)
151+
// 那么调用 f
152+
// 否则的话返回一个偏函数,将 args 作为第一个参数
153153
};
154154
}
155155
```
156156

157-
## Currying? What for?
157+
## 柯里化?目的是什么?
158158

159-
Advanced currying allows both to keep the function callable normally and to get partials easily. To understand the benefits we definitely need a worthy real-life example.
159+
高级的柯里化同时允许函数正常调用和获取偏函数。为了理解这样的好处,我们确实需要一个好的现实例子。
160160

161-
For instance, we have the logging function `log(date, importance, message)` that formats and outputs the information. In real projects such functions also have many other useful features like: sending it over the network or filtering:
161+
举个例子,我们有一个打印函数 `log(date, importance, message)` 格式化和输出信息。在真实的项目中,这样的函数有很多有用的特性,比如:通过网络传输或者筛选:
162162

163163
```js
164164
function log(date, importance, message) {
165165
alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
166166
}
167167
```
168168

169-
Let's curry it!
169+
让我们将它柯里化!
170170

171171
```js
172172
log = _.curry(log);
173173
```
174174

175-
After that `log` still works the normal way:
175+
操作之后 `log` 依然正常运行:
176176

177177
```js
178178
log(new Date(), "DEBUG", "some debug");
179179
```
180180

181-
...But also can be called in the curried form:
181+
但是也可以用柯里化格式调用:
182182

183183
```js
184184
log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
185185
```
186186

187-
Let's get a convenience function for today's logs:
187+
让我们来创建一个获取今天的日志的简易函数:
188188

189189
```js
190-
// todayLog will be the partial of log with fixed first argument
190+
// todayLog 会是一个首个参数确定的偏函数
191191
let todayLog = log(new Date());
192192

193-
// use it
193+
// 使用它
194194
todayLog("INFO", "message"); // [HH:mm] INFO message
195195
```
196196

197-
And now a convenience function for today's debug messages:
197+
接下来是提供今天的调试信息的简便函数:
198198

199199
```js
200200
let todayDebug = todayLog("DEBUG");
201201

202202
todayDebug("message"); // [HH:mm] DEBUG message
203203
```
204204

205-
So:
206-
1. We didn't lose anything after currying: `log` is still callable normally.
207-
2. We were able to generate partial functions that are convenient in many cases.
205+
那么:
206+
1. 柯里化之后我们没有丢失任何东西:`log` 依然可以被正常调用。
207+
2. 在很多情况下我们可以很方便生成偏函数。
208208

209-
## Advanced curry implementation
209+
## 高级柯里化实现
210210

211-
In case you're interested, here's the "advanced" curry implementation that we could use above.
211+
由于你可能感兴趣,下面是我们可以使用的「高级」柯里化实现
212212

213213
```js run
214214
function curry(func) {
@@ -231,22 +231,22 @@ function sum(a, b, c) {
231231

232232
let curriedSum = curry(sum);
233233

234-
// still callable normally
234+
// 依然可以被正常调用
235235
alert( curriedSum(1, 2, 3) ); // 6
236236

237-
// get the partial with curried(1) and call it with 2 other arguments
237+
// 得到 curried(1) 的偏函数,然后用另外两个参数调用它
238238
alert( curriedSum(1)(2,3) ); // 6
239239

240-
// full curried form
240+
// 完全柯里化形式
241241
alert( curriedSum(1)(2)(3) ); // 6
242242
```
243243

244-
The new `curry` may look complicated, but it's actually pretty easy to understand.
244+
新的「柯里函数」看上去有点复杂,但是它很容易理解。
245245

246-
The result of `curry(func)` is the wrapper `curried` that looks like this:
246+
`curry(func)` 的结果是 `curried` 函数的封装,结果如下:
247247

248248
```js
249-
// func is the function to transform
249+
// func 是被转化的函数
250250
function curried(...args) {
251251
if (args.length >= func.length) { // (1)
252252
return func.apply(this, args);
@@ -258,39 +258,39 @@ function curried(...args) {
258258
};
259259
```
260260

261-
When we run it, there are two branches:
261+
当我们运行它的时候,有两种结果:
262262

263-
1. Call now: if passed `args` count is the same as the original function has in its definition (`func.length`) or longer, then just pass the call to it.
264-
2. Get a partial: otherwise, `func` is not called yet. Instead, another wrapper `pass` is returned, that will re-apply `curried` providing previous arguments together with the new ones. Then on a new call, again, we'll get either a new partial (if not enough arguments) or, finally, the result.
263+
1. 立刻执行:当传入的 `args` 的长度和初始函数中所定义的(`func.length`)相同或者更长,那么直接将它传入需要执行的函数。
264+
2. 得到一个偏函数:当传入的 args 的长度小于初始函数中所定义的(`func.length`),`func` 暂时不被调用,取而代之的是,返回另外一层封装 `pass`,其中,将之前传入的参数合并新传入的参数一起应用于 `curried` 函数。虽然再次调用。我们要么得到一个新的偏函数(如果参数数量不够),要么,最终得到结果。
265265

266-
For instance, let's see what happens in the case of `sum(a, b, c)`. Three arguments, so `sum.length = 3`.
266+
举个例子,让我们看看用例 `sum(a, b, c)` 中发生了什么。三个参数,那么 `sum.lenght = 3`
267267

268-
For the call `curried(1)(2)(3)`:
268+
执行 `curried(1)(2)(3)`
269269

270-
1. The first call `curried(1)` remembers `1` in its Lexical Environment, and returns a wrapper `pass`.
271-
2. The wrapper `pass` is called with `(2)`: it takes previous args (`1`), concatenates them with what it got `(2)` and calls `curried(1, 2)` with them together.
270+
1. 首先调用 `curried(1)` `1` 保存在词法环境中,然后返回一层封装 `pass`
271+
2. 封装函数 `pass` 被调用,参数为 `(2)`:它会获取之前的参数 `(1)`,将它与 `(2)` 合并,一起调用 `curried(1, 2)`
272272

273-
As the argument count is still less than 3, `curry` returns `pass`.
274-
3. The wrapper `pass` is called again with `(3)`, for the next call `pass(3)` takes previous args (`1`, `2`) and adds `3` to them, making the call `curried(1, 2, 3)` -- there are `3` arguments at last, they are given to the original function.
273+
由于参数数量依然少于 3,`curry` 函数依然返回 `pass`
274+
3. `pass` 再次被调用,参数为 `(3)`, 在接下去的调用中 `pass(3)` 获取之前的参数 (`1`, `2`) 并将 `3` 与之合并,执行调用 `curried(1, 2, 3)` —— 最终有 `3` 个参数,它们被传入最原始的函数中。
275275

276-
If that's still not obvious, just trace the calls sequence in your mind or on the paper.
276+
如果这还不够清楚,那么将函数调用依次在你脑海中或者纸上过一遍。
277277

278-
```smart header="Fixed-length functions only"
279-
The currying requires the function to have a known fixed number of arguments.
278+
```smart header="只允许确定参数长度的函数"
279+
柯里化要求对应的函数,拥有已知确定数量的参数。
280280
```
281281

282-
```smart header="A little more than currying"
283-
By definition, currying should convert `sum(a, b, c)` into `sum(a)(b)(c)`.
282+
```smart header="柯里化深入一点"
283+
根据定义,柯里化应该将 `sum(a, b, c)` 转化为 `sum(a)(b)(c)`
284284
285-
But most implementations of currying in JavaScript are advanced, as described: they also keep the function callable in the multi-argument variant.
285+
但是在 JavaScript 中大多数的实现更加高级,就像所描述的那样:它们使得函数可以被多种形式的参数调用。
286286
```
287287

288-
## Summary
288+
## 总结
289289

290-
- When we fix some arguments of an existing function, the resulting (less universal) function is called *a partial*. We can use `bind` to get a partial, but there are other ways also.
290+
- 当我们确定一个函数的一些参数时,返回的函数(更加特定)被称为**偏函数**。我们可以使用 `bind` 来获取偏函数,但是也有其他方式获取。
291291

292-
Partials are convenient when we don't want to repeat the same argument over and over again. Like if we have a `send(from, to)` function, and `from` should always be the same for our task, we can get a partial and go on with it.
292+
当我们不想一遍又一遍重复相同的参数时,偏函数很方便。比如我们有函数 `send(from, to)`,并且在我们的任务中 `from` 始终是相同的,那么我们可以构造一个偏函数然后对它进行操作。
293293

294-
- *Currying* is a transform that makes `f(a,b,c)` callable as `f(a)(b)(c)`. JavaScript implementations usually both keep the function callable normally and return the partial if arguments count is not enough.
294+
- **柯里化**是将 `f(a,b,c)` 可以被以 `f(a)(b)(c)` 的形式被调用的转化。JavaScript 实现版本通常保留函数被正常调用和在参数数量不够的情况下返回偏函数这两个特性。
295295

296-
Currying is great when we want easy partials. As we've seen in the logging example: the universal function `log(date, importance, message)` after currying gives us partials when called with one argument like `log(date)` or two arguments `log(date, importance)`.
296+
当我们想要简单偏函数的时候,柯里化很棒。正如我们在 logging 例子中所看到的那样:通用函数 `log(date, importance, message)` 在柯里化之后,当我们在调用它的时候传入一个参数如 `log(date)` 或者两个参数 `log(date, importance)` 的时候,返回了偏函数。

0 commit comments

Comments
 (0)