2007-10-15
「再帰→ループ」の変換が大変だった件
まず、ループは再帰で表現できる
ループというのはすべて再帰呼び出しで表現できる。
たとえば、コレは
var array = [1, 2, 3]; for (var i = 0; i < array.length; i ++) alert(array[i]);
こんな感じになる
(function f(array, i) { if (i < array.length) { alert(array[i]); return f(array, i+1); } })([1, 2, 3], 0);
もし、 array がこの目的以外に使われないならコッチのがキレイかも
(function f(array) { alert(array.shift()); if (array.length) return f(array); })([1, 2, 3]);
ということは、再帰はループで表現できるはず
ということで、 document ノードから全部の DOM を再帰的に辿っていく例をループで表現してみようと思った
まずは、元のコード
(function f(node) { console.log(node); for (node = node.firstChild; node; node = node.nextSibling) f(node); })(document);
これをループで表現してみる。
(function f(node) { console.log(node); node = node.firstChild; while (node) { f(node); node = node.nextSibling; } })(document);
次に、 function を goto 文とスタックに分解。
var stack = []; var node = document; f: console.log(node); node = node.firstChild; while (node) { stack.push(node); goto f; ret: node = node.nextSibling; } node = stack.pop(); if (stack.length) goto ret;
次に、 f から goto f をループで表現します。 ret: 以下の部分は外に出します。
var stack = []; var node = document; while (true); console.log(node); node = node.firstChild; label0: if (!node) goto label1; stack.push(node); } ret: node = node.nextSibling; goto label0; label1: node = stack.pop(); if (stack.length) goto ret;
だんだんコードがスパゲッティになってきました。。
次に、 ret には goto 文以外で到達するケースがないため、自由に移動できます。
var stack = []; var node = document; while (true) { console.log(node); node = node.firstChild; label0: if (!node) goto label1; stack.push(node); } label1: node = stack.pop(); if (stack.length) goto ret; else goto end; ret: node = node.nextSibling; goto label0; end:
次に goto label1 を break にして、 goto ret を削除します。
var stack = []; var node = document; while (true) { console.log(node); node = node.firstChild; label0: if (!node) break; stack.push(node); } node = stack.pop(); if (stack.length) { node = node.nextSibling; goto label0; }
次に、 label0 を while ループの外に出します。無限ループは巻き紙のようなものなので、一行ずつ上にずらして一番上の行を外に出していけば OK です。
var stack = []; var node = document; console.log(node); node = node.firstChild; label0: while (true) { if (!node) break; stack.push(node); console.log(node); node = node.firstChild; } node = stack.pop(); if (stack.length) { node = node.nextSibling; goto label0; }
次に、 goto label0 をループで表現します。
var stack = []; var node = document; console.log(node); node = node.firstChild; while (true) { while (true) { if (!node) break; stack.push(node); console.log(node); node = node.firstChild; } node = stack.pop(); if (stack.length) { node = node.nextSibling; } else break; }
あとは、少しコードをキレイにして
var stack = []; var node = document; console.log(node); node = node.firstChild; while (true) { while (node) { stack.push(node); console.log(node); node = node.firstChild } node = stack.pop(); if (!stack.length) break; node = node.nextSibling; }
はい!完成!わーわーぱちぱちぱち。
まとめ
トラックバック - http://d.hatena.ne.jp/amachang/20071015/1192435915
リンク元
- 260 http://reader.livedoor.com/reader/
- 211 http://himazin.jp/
- 123 http://d.hatena.ne.jp/
- 110 http://jyouhouya3.sakura.ne.jp/
- 109 http://b.hatena.ne.jp/
- 108 http://www.google.co.jp/search?sourceid=navclient&hl=ja&ie=UTF-8&rlz=1T4DBJP_ja___JP220&q=MSXML2.XMLHTTP+javascript+onreadystatechange
- 105 http://b.hatena.ne.jp/entrylist?sort=hot
- 87 http://crocro.com/
- 78 http://www.google.com/search?hl=ja&lr=lang_ja&ie=UTF-8&oe=UTF-8&q=javascript+変数+スコープ&num=50
- 70 http://www.google.co.jp/search?sourceid=navclient&aq=t&hl=ja&ie=UTF-8&rls=GGLD,GGLD:2005-17,GGLD:ja&q=IT戦記
