ibcadmin 发表于 2019-10-12 10:22:34

Currying 及应用

<table >
<tbody >
    <tr >
      <td >
<p>Currying,中文多翻译为<strong>柯里化</strong>,感觉这个音译还没有到达类似 Humor 之于幽默的传神地步,反面直接利用 Currying。</p>
<h2>什么是 Currying</h2>
<p>Currying 是这么一种<strong>机制</strong>,它将一个接收多个参数的函数,拆分成多个接收单个参数的函数。</p>
<p>观察下面的代码:</p>
function add (a, b) {
return a + b;
}

add(3, 4); // returns 7
<p><code>add</code> 接收两个参数 <code>a</code>,<code>b</code>,并返回它们的和 <code>a+b</code>。</p>
<p>颠末 curry 化处理处罚后,函数成了如下情势:</p>
function add (a) {
return function (b) {
    return a + b;
}
}
<p>如今 <code>add</code> 接收一个参数 <code>a</code>,返回另一个接收一个参数 <code>b</code> 的函数。</p>
add(3)(4);

var add3 = add(3);

add3(4);
<p>如今当调用 <code>add(3)</code> 后,得到的不是和,而是另一个接收一个参数的函数,因此,<code>add</code> 的返回可以继承被调用,<code>add(3)(4)</code> 反面的这次调用才会将 4 加到 3 上得到和。</p>
<p>而 <code>var add3 = add(3)</code> 如许的单次调用,得到的函数结果相当于是将 3 保存在了新函数的闭包中,该函数会对传入的参数加 3。</p>
<p>留意这里提到了将入参 3 <strong>保存</strong> 到了闭包中后续利用,很轻易联想到 <code>Function.prototype.bind()</code>,它就可以对传入的函数提前绑定一些预设的入参:</p>
function.bind(thisArg[, arg1[, arg2[, ...]]])
<p>反面会看到,正由于 <code>bind</code> 和 Currying 有点关系,在实现任意函数的 Currying 化时会用到它。</p>
<p>留意到 Currying 化的界说,实在是将多个参数打散到多个函数中,这个过程可通过代码来主动化,以到达将任意多入参函数举行 Currying 化的目标,反面讨论实现。</p>
<h2>偏函数/Partial Application</h2>
<p>区别与 Currying,如果在拆分入参的过程中,这些拆分出来的函数不是一次只应用其中的一个,而是任意多个,则这些函数就是部分应用(Parital application)了原函数中的入参,称作偏函数。</p>
<p>观察下面的 <code>add</code> 函数,实在是将前面示例中的 <code>add</code> 入参举行了扩充,由两个增长到四个:</p>
function add(a, b, c, d) {
return a + b + c + d;
}
<p>那么如下的函数就都是偏函数,它们都部分应用了 <code>add</code> 的入参:</p>
function partial1(a) {
return function(c) {
    return a + b + c + d;
};
}
function partial2(a, b) {
return function(c, d) {
    return a + b + c + d;
};
}
function partial3(a, b, c) {
return function(d) {
    return a + b + c + d;
};
}
<p>偏函数中这种入参的拆分和部分应用,并不但限于一层的拆分,可以是任意多次的:</p>
function partial1(a, b) {
return function partial2(c) {
    return function partial3(d) {
      return a + b + c + d;
    };
};
}

partial1(1)(2, 3)(4); // 10
<p>其中,<code>partial1</code>、<code>partial2</code>、<code>partial3</code> 一起构成了原 <code>add</code> 函数的偏函数。</p>
<p>可以看到,偏函数是 Curring 更加一样平常(general)的情势,下面看如何实现将任意函数举行 Currying 化,或偏函数化。</p>
<h2>将一样平常化函数举行 Currying 化</h2>
<p>我们必要构造这么一个函数假设名叫 <code>curry</code>,</p>
function curry(fn){
// 待实现
}
<p>调用 <code>curry</code> 后,我们可以得到原函数 Curry 化后的版本,</p>
function add (a, b) {
return a + b;
}

var currified = curry(add);
<p>即上述 <code>currified</code> 应该等效为:</p>
function currified (a) {
return function (b) {
    return a + b;
}
}
<p>起首,通过 <code>Function.length</code> 是可以知道一个给定函数其预期的入参个数的。</p>
<p>再加上前面提到的 <code>bind</code> 函数,可以得到如下的实现:</p>
function curry(f) {
return function currify() {
    const args = Array.prototype.slice.call(arguments);
    return args.length >= f.length ?
      f.apply(null, args) :
      currify.bind(null, ...args)
}
}
<p>下面测试一下:</p>
function add(a, b) {
return a + b;
}

var currified = curry(add);

currified(1)(2); // 3
<p>而且以上实现不但是简朴的 Currying 化,可以是任意数目和任意次数的 parial application:</p>
function add(a, b, c, d) {
return a + b + c + d;
}

var currified = curry(add);

currified(1)(2)(3)(4); // 10
currified(1)(2, 3)(4); // 10
currified(1, 2)(3, 4); // 10
<p>总之就是各种外形<ruby>和<rp>(</rp><rt>hàn</rt><rp>)</rp></ruby>姿势,各种颜色<ruby>和<rp>(</rp><rt>hàn</rt><rp>)</rp></ruby>皮肤的组合。</p>
<p>主动化的 CurryIng 倒是实现了,可说了半天,它详细有什么实用价值。</p>
<h2>函数的组合(function composition)</h2>
<p>我们知道代数里面可以有函数的组合,譬如:</p>
<code>f(x) = x * x
g(y) = y + 1
g(f(x)) = x * x + 1

g(f(2)) = 2 * 2 + 1 = 5
</code>
<p>上述代数表达转成 JavaScript 即:</p>
function f(x) {
return x ** 2;
}

function g(y) {
return y + 1;
}

g(f(2)) // 5
<p>这里用到了两个函数 <code>f</code>,<code>g</code> 团结起来得到一个结果,他们都分别只接收一个入参同时返回一个结果。</p>
<p>像如许只接收一个入参并返回一个结果的函数,便符合组装的需求,可像上面如许自由组合。通过上面的讨论我们知道,任意函数都可颠末 Currying 化处理处罚后变成多个只接收单个入参的函数。这就为函数的组合提供了底子。</p>
<p>因此我们可以将 <code>f</code>,<code>g</code> 的结合形成一个新的函数,这个函数作为对外的接口被调用即可。</p>
const compose = fn1 => fn2 => input => fn1(fn2(input));
<p>利用:</p>
const myFn = compose(f)(g);
myFn(2); // 5
<p>像上面的 <code>compose</code> 还不敷一样平常化,他只接收两个函数并对其举行结合,下面来看更加一样平常化的函数组合,将实现接收任意多个函数。</p>
const pipe = (...fns) => input => fns.reduce((mem, fn) => fn(mem), input)

const double = x => x * 2
const addOne = x => x + 1
const square = x => x * x

pipe(square, double, addOne)(2)
<p>上面的 <code>pipe</code> 将对输入依次应用 入参中的各函数,以是取名 <code>pipe</code> 管道流。</p>
<p>以上,函数的组装。</p>
<h2>相关资源</h2>
<ul>
<li>What is 'Currying'?</li>
<li>Curry and Function Composition</li>
<li>An elegant and simple curry(f) implementation in Javascript</li>
<li>lodash - curry</li>
<li>Function.length</li>
<li>Wikipedia- Currying</li>
<li>MDN - Function.prototype.bind()</li>
<li>Currying versus partial application (with JavaScript code)</li>
<li>Currying is not idiomatic in JavaScript</li>
<li>Functional JS #6: Function composition</li>
<li>Functional JavaScript: Function Composition For Every Day Use</li>
</ul></td>
    </tr>
</tbody>
</table><br><br/><br/><br/><br/><br/>来源:<a href="https://www.cnblogs.com/Wayou/p/currying_and_function_compose.html" target="_blank">https://www.cnblogs.com/Wayou/p/currying_and_function_compose.html</a>
页: [1]
查看完整版本: Currying 及应用