ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript 関数プログラミングで実装を作成する

JavaScript 関数プログラミングで実装を作成する

黄舟
黄舟オリジナル
2017-03-02 14:49:291672ブラウズ

前の記事では、javascript 関数型プログラミングでの curry (カリー化) の実装を紹介しました。 もちろん、カリー化は限定されたパラメーターのカリー化です。無限パラメータのカリー化、今回は主に javascript 関数プログラミングにおけるもう 1 つの非常に重要な関数 compose について説明します。compose 関数の役割は次のとおりです。関数は連続して実行され、1 つの関数の出力結果が別の関数の入力パラメーターとして、ドミノのように推測されて実行されます。 javascript函数式编程中curry(柯里化)的实现,当然那个柯里化是有限参数的柯里化,等有机会在补上无限参数的那一种柯里化,这次主要说的是javascript函数式编程中另外一个很重要的函数composecompose函数的作用就是组合函数的,将函数串联起来执行,将多个函数组合起来,一个函数的输出结果是另一个函数的输入参数,一旦第一个函数开始执行,就会像多米诺骨牌一样推导执行了。

简介

比如有这样的需求,要输入一个名字,这个名字有由firstName,lastName组合而成,然后把这个名字全部变成大写输出来,比如输入jacksmith我们就要打印出来,‘HELLO,JACK SMITH’

我们考虑用函数组合的方法来解决这个问题,需要两个函数greeting, toUpper

var greeting = (firstName, lastName) => 'hello, ' + firstName + ' ' + lastName
var toUpper = str => str.toUpperCase()
var fn = compose(toUpper, greeting)
console.log(fn('jack', 'smith'))
// ‘HELLO,JACK SMITH’

这就是compose大致的使用,总结下来要注意的有以下几点

  • compose的参数是函数,返回的也是一个函数

  • 因为除了第一个函数的接受参数,其他函数的接受参数都是上一个函数的返回值,所以初始函数的参数是多元的,而其他函数的接受值是一元

  • compsoe函数可以接受任意的参数,所有的参数都是函数,且执行方向是自右向左的,初始函数一定放到参数的最右面

知道这三点后,就很容易的分析出上个例子的执行过程了,执行fn('jack', 'smith')的时候,初始函数为greeting,执行结果作为参数传递给toUpper,再执行toUpper,得出最后的结果,compose的好处我简单提一下,如果还想再加一个处理函数,不需要修改fn,只需要在执行一个compose,比如我们再想加一个trim,只需要这样做

var trim = str => str.trim()
var newFn = compose(trim, fn)
console.log(newFn('jack', 'smith'))

就可以了,可以看出不论维护和扩展都十分的方便。

实现

例子分析完了,本着究其根本的原则,还是要探究与一下compose到底是如何实现的,首先解释介绍一下我是如何实现的,然后再探求一下,javascript函数式编程的两大类库,lodash.jsramda.js是如何实现的,其中ramda.js实现的过程非常函数式。

我的实现

我的思路是,既然函数像多米诺骨牌式的执行,我首先就想到了递归,下面就一步一步的实现这个compose,首先,compose返回一个函数,为了记录递归的执行情况,还要记录参数的长度len,还要给返回的函数添加一个名字f1

var compose = function(...args) {
    var len = args.length
    return function f1() {

    }
}

函数体里面要做的事情就是不断的执行args中的函数,将上一个函数的执行结果作为下一个执行函数的输入参数,需要一个游标count来记录args函数列表的执行情况。

var compose = function(...args) {
    var len = args.length
    var count = len - 1
    var result
    return function f1(...args1) {
        result = args[count].apply(this, args1)
        count--
        return f1.call(null, result)
    }
}

这个就是思路,当然这样是不行的,没有退出条件,递归的退出条件就是最后一个函数执行完的时候,也就是count0的时候,这时候,有一点要注意,递归退出的时候,count游标一定要回归初始状态,最后补充一下代码

var compose = function(...args) {
        var len = args.length
        var count = len - 1
        var result
        return function f1(...args1) {
            result = args[count].apply(this, args1)
            if (count <= 0) {
                count = len - 1
                return result
            } else {
                count--
                return f1.call(null, result)
            }
        }
    }

这样就实现了这个compose函数。后来我发现递归这个完全可以使用迭代来实现,使用while函数看起来更容易明白,其实lodash.js就是这么实现的。

lodash实现

lodash的思路同上,不过是用迭代实现的,我就把它的源代码贴过来看一下

var flow = function(funcs) {
    var length = funcs.length
    var index = length
    while (index--) {
        if (typeof funcs[index] !== &#39;function&#39;) {
            throw new TypeError(&#39;Expected a function&#39;);
        }
    }
    return function(...args) {
        var index = 0
        var result = length ? funcs[index].apply(this, args) : args[0]
        while (++index < length) {
            result = funcs[index].call(this, result)
        }
        return result
    }
}
var flowRight = function(funcs) {
    return flow(funcs.reverse())
}

可以看出,lodash的本来实现是从左到右的,但也提供了从右到左flowRight,还多了一层函数的校验,而且接收的是数组,不是参数序列,而且从这行var result = length ? funcs[index].apply(this, args) : args[0]可以看出允许数组为空,可以看出还是非常严谨的。我写的就缺少这种严谨的异常处理。

结论

这次主要介绍了函数式编程中的compose函数的原理和实现方法,由于篇幅原因,我把打算分析的ramda.js源码实现放到下一篇来介绍,可以说ramda.js实现的compose

はじめに

たとえば、このような要件がある場合、この名前は firstNamelastName で構成されます。次に、この名前を入力します。すべての出力を大文字に変更します。たとえば、jacksmith と入力すると、'HELLO, JACK SMITH' と出力されます。コード>。 🎜🎜この問題を解決するには、関数の組み合わせを使用することを検討します。<code>greetingtoUpper という 2 つの関数が必要です。🎜rrreee🎜これが compose の一般的な使用法です。注意点は以下の通りです🎜
  • 🎜compose のパラメータは関数であり、返されるものも関数です🎜
  • 🎜なぜなら、最初の関数で受け取ったパラメータと他の関数で受け取ったパラメータは前の関数の戻り値であることに加えて、最初の関数のパラメータは複数ですcode> ですが、他の関数で受け入れられる値は <code>univariate の 🎜
  • 🎜compsoe 関数は任意のパラメータを受け入れることができ、すべてのパラメータは関数です、実行方向は 右から左 です。はい、初期関数は パラメータの右端に配置する必要があります🎜
🎜Afterこれら 3 つの点を理解していれば、fn('jack', 'smith') を実行すると、最初の関数は greeting になります。前の例の実行プロセスを分析するのは簡単です。実行結果はパラメータとして toUpper に渡され、その後 toUpper に渡されて最終結果が得られます。 compose の利点について簡単に説明します。処理関数の場合、fn を変更する必要はありません。たとえば、trim を追加する場合は、 compose を実行するだけで済みます。 code> を実行するだけです🎜rrreee🎜。これは保守と拡張に非常に便利であることがわかります。 🎜

実装

🎜 基本原則に従って、例を分析した後、compose がどのように実装されるかを検討したいと思います。まず、それをどのように実装したかを説明します。次に、javascript 関数プログラミングの 2 つの主要なライブラリ、lodash.jsramda.js がどのように実装されているかを調べます。このプロセスは javascript によって実装されます。 code>ramda.js は非常に機能的です。 🎜

私の実装

🎜 私のアイデアは、関数がドミノのように実行されるため、最初に再帰を考えました。次に、この compose を段階的に実装します。 、compose は関数を返します。再帰の実行を記録するには、パラメータ len の長さと名前 f1 も記録する必要があります。 code> を返された関数 code> に追加する必要があります。 🎜rrreee🎜関数本体で行う必要があるのは、<code>args 内の関数を継続的に実行し、前の関数の実行結果を次の実行関数の入力パラメーターとして使用することです。 args 関数リストの実行を記録するには code>countcount が のときです。 0 このとき、注意点が 1 つあります。再帰が終了すると、count カーソルは初期状態に戻る必要があります。最後に、このようにコードを追加します。 compose は機能を実現します。その後、反復を使用して再帰を実装できることがわかりました。実際には、これが lodash.js 関数の実装方法です。 🎜

lodash の実装

🎜lodash は上記と同じ考え方ですが、繰り返し実装されていますので、見てみるためにソース コードを投稿します。 rrreee 🎜lodash の元の実装は left to right ですが、right to leftflowRight も提供していることがわかります。 には、関数検証の追加層があり、パラメータ シーケンス ではなく 配列 を受け取り、この行から var result = length を受け取ります。 ? funcs[index].apply(this, args) : args[0] 配列が空であることが許可されており、依然として非常に厳密であることがわかります。私が書いたものには、この厳密な例外処理が欠けていました。 🎜

結論

🎜 今回は主に関数型プログラミングにおけるcompose関数の原理と実装方法を紹介しますが、スペースの都合上、ramda.jsを解析していきます。 ソース コードの実装については、次の記事で紹介します。ramda.js によって実装された compose は、より機能的であり、分析する必要があると言えます。別々に。 🎜

上記は、JavaScript 関数プログラミングで compose によって実装されたコンテンツです。その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) に注目してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。