逆転シーケンスの再帰/再帰(+destructuring assigment)実現(JavaScript+ES 6)

3651 ワード

ここではJavaScriptを用いた逆転シーケンス(配列/文字列)の再帰的・後戻りが実現されます.また、ES 6のdestructuring assigment+spread operatorを使って、もっとfunctionalのバージョンを作ってみました.
正確な性能はテストを通過します.(私のGithubに置いてあるデモを参照してください.ついでに小さなテストフレームを書きました.)しかし、効率は疑問符を打ちます.特にES 6特性のバージョンを使いました.ここでは主にJSの関数的な特性を書いています.
1.逆転シーケンスの再帰的実現
まずHaskyellで下書きをします(自然なので)、配列版が長いです.
reverse' :: [a] -> [a]
reverse' []     = []
reverse' (x:xs) = reverse' xs ++ [x]
JavaScriptには、配列と文字列がsliceconcatがありますので、二つの「シーケンス」(離散数学などの分野ではよく両者を合わせてsequenceと呼びます)がサポートされているバージョンとして作成できます.
ノート:
  • Array.prototype.slice()
  • Array.prototype.concat()
  • String.prototype.slice()
  • String.prototype.concat()
  • いずれもECMAScript 3に登場し、JavaScript 1.2に実現しています.
    対応するJSは、ベースケースをシーケンス長が1未満に設定すると、ステップを縮小して再帰的に実現する.比較的にハスカル版のほうが一目瞭然ですね.
    function recursiveReverse(seq) {
        return seq.length > 1 ?
            recursiveReverse(seq.slice(1)).concat(seq.slice(0, 1)) : seq;
    }
    テストを経て、配列と文字列は全部通過できます.
    2.最終再帰的実現
    Haskellバージョン長は、完成した部分をretで伝達する.
    reverse' :: [a] -> [a]
    reverse' list = rev list []
      where rev []     ret = ret
            rev (x:xs) ret = rev xs (x:ret)
    対応するJSバージョンは、ここでslice(0, 0)を用いて、タイプチェックを避けて、空のシーケンスを作成する.
    function tailReverse(seq) {
        return (function rev(seq, ret) {
            return seq.length > 0 ?
                rev(seq.slice(1), seq.slice(0, 1).concat(ret)) : ret;
        })(seq, seq.slice(0, 0));
    }
    テストを経て、配列と文字列は全部通過できます.
    3.ES 6の新特性を使用した再帰バージョン
    ES 6はdestructuring assigmentとspread operatorを導入しており、このように関数式プログラミングにおけるモードマッチングを部分的に使用することができる.しかし、ES 6の新しい特性の中では、文法的にJSの関数をHaskellのようにモードマッチングをオーバーロードに融合させる組み合わせはまだ見つけられません.ここは使い方が簡単なので、直接三目演算子でいいです.
    ES 6の新しい特性に合わせて実装される簡単な再帰版は、JS内の配列と文字列は共通の機能がないので、append(要素を追加して新しい配列に戻る)の関数のようです.(destructuring assigment+spread operatorの組み合わせ[x, ...xs]は文字列を ,[ ]に分割するので、直接に下の関数に文字列を入力してから戻り値joinを文字列に戻しても同じ効果があります).
    function pmReverse([x, ...xs]) {
        return typeof x === "undefined" ? [] : pmReverse(xs).concat([x]);
    }
    テストを経て、配列は正常に逆転することができます.(ブラウザでES 6の特性を開く時だけ、このページは正常にこのバージョンのテストを逃げます.そうでなければ、一つのalertしかないです.)
    4.ES 6の新特性を使用した最終再帰版
    function pmTailReverse(list) {
        return (function rev([x, ...xs], ret) {
            return typeof x === "undefined" ? ret : rev(xs, [x].concat(ret));
        })(list, []);
    }
    テストを経て、配列は正常に逆転することができます.(ブラウザでES 6の特性を開く時だけ、このページは正常にこのバージョンのテストを逃げます.そうでなければ、一つのalertしかないです.)
    テストページについて
    簡単に書くために、ソースコードを参照してください.
    ES 6をサポートしていないブラウザはES 6の特性のコードを解析する時に文法エラーが発生しますので、try-catchでは掴めません.ここでwindow.onerrorを使って捕まえてヒントを与えました.なぜかというと、私のChromeは実験的なJSを開いてもES 6サポートができません.FireFoxはデフォルトで使えます.