遅延リスト
配列をイテレータでまわして、とやっていると遅延評価して欲しいなーって時があるよね。webmonkey 風文体。そうでもない。詳しいことはわからないのですが、適当に書いてみました。mala が何年前に通った道ですか?いやもうそういうのはいいや。このごろ別にスーパーじゃなくていい気がしてきたよ。
protptype.js の多分 1.4.0 以上に依存。
LazyList = Class.create(); LazyList.prototype = { initialize: function(_head, tail) { this._head = _head; this.tail = tail; }, head: function() { return this._head; }, _each: function(iterator) { var list = this; while(!list.isEmpty()) { iterator(list.head()); list = list.tail(); } }, isEmpty: function() { return this == $empty; }, // Enumerable のメソッドを避けるため頭に 'l' を付けてます lMap: function(iterator) { if(this.isEmpty()) return $empty; return new LazyList(iterator(this.head()), (function() { return this.tail().lMap(iterator); }).bind(this)); }, lFilter: function(iterator) { var list = this; while(!list.isEmpty()) { if(iterator(list.head())) { return new LazyList(list.head(), function() { return list.tail().lFilter(iterator); }); } list = list.tail(); } return $empty; }, lTake: function(count) { if(count == 0) return $empty; return new LazyList(this.head(), (function() { return this.tail().lTake(count - 1); }).bind(this)); } // .. こんな調子で続く }; Object.extend(LazyList.prototype, Enumerable); var $empty = LazyList.empty = new LazyList(); var $LL = LazyList.create = function() { return $A(arguments).reverse().inject($empty, function(list, value) { return new LazyList(value, function() { return list; }); }); }; var $LR = LazyList.createRange = function(start, end, incr) { if(end != undefined && start > end) return $empty; return new LazyList(start, function() { return LazyList.createRange(start + (incr || 1), end, incr); }); };
使用例
var list = $LL(1, 4, 5, 2); list.lMap(function(value) { return value * 2; }); // [2, 8, 10, 4] var range = $LR(1); // [1, 2, 3, ... ] range.lFilter(function(value) { return value % 3 == 0; }).lTake(5); // [3, 6, 9, 12, 15]
これって再帰でスタックがいっぱいになる?よくわからない。