逆転シーケンスの再帰/再帰(+destructuring assigment)実現(JavaScript+ES 6)
3651 ワード
ここではJavaScriptを用いた逆転シーケンス(配列/文字列)の再帰的・後戻りが実現されます.また、ES 6のdestructuring assigment+spread operatorを使って、もっとfunctionalのバージョンを作ってみました.
正確な性能はテストを通過します.(私のGithubに置いてあるデモを参照してください.ついでに小さなテストフレームを書きました.)しかし、効率は疑問符を打ちます.特にES 6特性のバージョンを使いました.ここでは主にJSの関数的な特性を書いています.
1.逆転シーケンスの再帰的実現
まずHaskyellで下書きをします(自然なので)、配列版が長いです.
ノート: いずれもECMAScript 3に登場し、JavaScript 1.2に実現しています.
対応するJSは、ベースケースをシーケンス長が1未満に設定すると、ステップを縮小して再帰的に実現する.比較的にハスカル版のほうが一目瞭然ですね.
2.最終再帰的実現
Haskellバージョン長は、完成した部分を
3.ES 6の新特性を使用した再帰バージョン
ES 6はdestructuring assigmentとspread operatorを導入しており、このように関数式プログラミングにおけるモードマッチングを部分的に使用することができる.しかし、ES 6の新しい特性の中では、文法的にJSの関数をHaskellのようにモードマッチングをオーバーロードに融合させる組み合わせはまだ見つけられません.ここは使い方が簡単なので、直接三目演算子でいいです.
ES 6の新しい特性に合わせて実装される簡単な再帰版は、JS内の配列と文字列は共通の機能がないので、
4.ES 6の新特性を使用した最終再帰版
テストページについて
簡単に書くために、ソースコードを参照してください.
ES 6をサポートしていないブラウザはES 6の特性のコードを解析する時に文法エラーが発生しますので、try-catchでは掴めません.ここで
正確な性能はテストを通過します.(私のGithubに置いてあるデモを参照してください.ついでに小さなテストフレームを書きました.)しかし、効率は疑問符を打ちます.特にES 6特性のバージョンを使いました.ここでは主にJSの関数的な特性を書いています.
1.逆転シーケンスの再帰的実現
まずHaskyellで下書きをします(自然なので)、配列版が長いです.
reverse' :: [a] -> [a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]
JavaScriptには、配列と文字列がslice
とconcat
がありますので、二つの「シーケンス」(離散数学などの分野ではよく両者を合わせてsequenceと呼びます)がサポートされているバージョンとして作成できます.ノート:
Array.prototype.slice()
Array.prototype.concat()
String.prototype.slice()
String.prototype.concat()
対応する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はデフォルトで使えます.