├── .gitignore
├── README.md
├── lazy.js
├── package.json
└── test
├── bucket.js
├── complex.js
├── custom.js
├── em.js
├── filter.js
├── foldr.js
├── forEach.js
├── head.js
├── join.js
├── lines.js
├── map.js
├── pipe.js
├── product.js
├── range.js
├── skip.js
├── sum.js
├── tail.js
├── take.js
└── takeWhile.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Lazy lists for node
2 | ===================
3 |
4 |
5 | # Table of contents:
6 |
7 | [Introduction](#Introduction)
8 |
9 | [Documentation](#Documentation)
10 |
11 |
12 | # Introduction
13 | Lazy comes really handy when you need to treat a stream of events like a list.
14 | The best use case currently is returning a lazy list from an asynchronous
15 | function, and having data pumped into it via events. In asynchronous
16 | programming you can't just return a regular list because you don't yet have
17 | data for it. The usual solution so far has been to provide a callback that gets
18 | called when the data is available. But doing it this way you lose the power of
19 | chaining functions and creating pipes, which leads to not that nice interfaces.
20 | (See the 2nd example below to see how it improved the interface in one of my
21 | modules.)
22 |
23 | Check out this toy example, first you create a Lazy object:
24 | ```javascript
25 | var Lazy = require('lazy');
26 |
27 | var lazy = new Lazy;
28 | lazy
29 | .filter(function (item) {
30 | return item % 2 == 0
31 | })
32 | .take(5)
33 | .map(function (item) {
34 | return item*2;
35 | })
36 | .join(function (xs) {
37 | console.log(xs);
38 | });
39 | ```
40 |
41 | This code says that 'lazy' is going to be a lazy list that filters even
42 | numbers, takes first five of them, then multiplies all of them by 2, and then
43 | calls the join function (think of join as in threads) on the final list.
44 |
45 | And now you can emit 'data' events with data in them at some point later,
46 | ```javascript
47 | [0,1,2,3,4,5,6,7,8,9,10].forEach(function (x) {
48 | lazy.emit('data', x);
49 | });
50 | ```
51 |
52 | The output will be produced by the 'join' function, which will output the
53 | expected [0, 4, 8, 12, 16].
54 |
55 | And here is a real-world example. Some time ago I wrote a hash database for
56 | node.js called node-supermarket (think of key-value store except greater). Now
57 | it had a similar interface as a list, you could .forEach on the stored
58 | elements, .filter them, etc. But being asynchronous in nature it lead to the
59 | following code, littered with callbacks and temporary lists:
60 | ```javascript
61 | var Store = require('supermarket');
62 |
63 | var db = new Store({ filename : 'users.db', json : true });
64 |
65 | var users_over_20 = [];
66 | db.filter(
67 | function (user, meta) {
68 | // predicate function
69 | return meta.age > 20;
70 | },
71 | function (err, user, meta) {
72 | // function that gets executed when predicate is true
73 | if (users_over_20.length < 5)
74 | users_over_20.push(meta);
75 | },
76 | function () {
77 | // done function, called when all records have been filtered
78 |
79 | // now do something with users_over_20
80 | }
81 | )
82 | ```
83 | This code selects first five users who are over 20 years old and stores them
84 | in users_over_20.
85 |
86 | But now we changed the node-supermarket interface to return lazy lists, and
87 | the code became:
88 | ```javascript
89 | var Store = require('supermarket');
90 |
91 | var db = new Store({ filename : 'users.db', json : true });
92 |
93 | db.filter(function (user, meta) {
94 | return meta.age > 20;
95 | })
96 | .take(5)
97 | .join(function (xs) {
98 | // xs contains the first 5 users who are over 20!
99 | });
100 | ```
101 | This is so much nicer!
102 |
103 | Here is the latest feature: .lines. Given a stream of data that has \n's in it,
104 | .lines converts that into a list of lines.
105 |
106 | Here is an example from node-iptables that I wrote the other week,
107 | ```javascript
108 | var Lazy = require('lazy');
109 | var spawn = require('child_process').spawn;
110 | var iptables = spawn('iptables', ['-L', '-n', '-v']);
111 |
112 | Lazy(iptables.stdout)
113 | .lines
114 | .map(String)
115 | .skip(2) // skips the two lines that are iptables header
116 | .map(function (line) {
117 | // packets, bytes, target, pro, opt, in, out, src, dst, opts
118 | var fields = line.trim().split(/\s+/, 9);
119 | return {
120 | parsed : {
121 | packets : fields[0],
122 | bytes : fields[1],
123 | target : fields[2],
124 | protocol : fields[3],
125 | opt : fields[4],
126 | in : fields[5],
127 | out : fields[6],
128 | src : fields[7],
129 | dst : fields[8]
130 | },
131 | raw : line.trim()
132 | };
133 | });
134 | ```
135 | This example takes the `iptables -L -n -v` command and uses .lines on its output.
136 | Then it .skip's two lines from input and maps a function on all other lines that
137 | creates a data structure from the output.
138 |
139 |
140 | # Documentation
141 |
142 | Supports the following operations:
143 |
144 | * lazy.filter(f)
145 | * lazy.forEach(f)
146 | * lazy.map(f)
147 | * lazy.take(n)
148 | * lazy.takeWhile(f)
149 | * lazy.bucket(init, f)
150 | * lazy.lines
151 | * lazy.sum(f)
152 | * lazy.product(f)
153 | * lazy.foldr(op, i, f)
154 | * lazy.skip(n)
155 | * lazy.head(f)
156 | * lazy.tail(f)
157 | * lazy.join(f)
158 |
159 | The Lazy object itself has a .range property for generating all the possible ranges.
160 |
161 | Here are several examples:
162 |
163 | * Lazy.range('10..') - infinite range starting from 10
164 | * Lazy.range('(10..') - infinite range starting from 11
165 | * Lazy.range(10) - range from 0 to 9
166 | * Lazy.range(-10, 10) - range from -10 to 9 (-10, -9, ... 0, 1, ... 9)
167 | * Lazy.range(-10, 10, 2) - range from -10 to 8, skipping every 2nd element (-10, -8, ... 0, 2, 4, 6, 8)
168 | * Lazy.range(10, 0, 2) - reverse range from 10 to 1, skipping every 2nd element (10, 8, 6, 4, 2)
169 | * Lazy.range(10, 0) - reverse range from 10 to 1
170 | * Lazy.range('5..50') - range from 5 to 49
171 | * Lazy.range('50..44') - range from 50 to 45
172 | * Lazy.range('1,1.1..4') - range from 1 to 4 with increment of 0.1 (1, 1.1, 1.2, ... 3.9)
173 | * Lazy.range('4,3.9..1') - reverse range from 4 to 1 with decerement of 0.1
174 | * Lazy.range('[1..10]') - range from 1 to 10 (all inclusive)
175 | * Lazy.range('[10..1]') - range from 10 to 1 (all inclusive)
176 | * Lazy.range('[1..10)') - range grom 1 to 9
177 | * Lazy.range('[10..1)') - range from 10 to 2
178 | * Lazy.range('(1..10]') - range from 2 to 10
179 | * Lazy.range('(10..1]') - range from 9 to 1
180 | * Lazy.range('(1..10)') - range from 2 to 9
181 | * Lazy.range('[5,10..50]') - range from 5 to 50 with a step of 5 (all inclusive)
182 |
183 | Then you can use other lazy functions on these ranges.
184 |
185 |
186 |
--------------------------------------------------------------------------------
/lazy.js:
--------------------------------------------------------------------------------
1 | var EventEmitter = require('events').EventEmitter;
2 | var util = require('util');
3 | var stream = require('stream');
4 |
5 | function Lazy(em, opts) {
6 | if (!(this instanceof Lazy)) return new Lazy(em, opts);
7 | EventEmitter.call(this);
8 | var self = this;
9 |
10 |
11 | self.once = function (name, f) {
12 | self.on(name, function g () {
13 | self.removeListener(name, g);
14 | f.apply(this, arguments);
15 | });
16 | }
17 |
18 | if (!opts) opts = {};
19 | var dataName = opts.data || 'data';
20 | var pipeName = opts.pipe || 'pipe';
21 | var endName = opts.pipe || 'end';
22 |
23 | if (pipeName != endName) {
24 | var piped = false;
25 | self.once(pipeName, function () { piped = true });
26 | self.once(endName, function () {
27 | if (!piped) self.emit(pipeName);
28 | });
29 | }
30 |
31 | self.push = function (x) {
32 | self.emit(dataName, x);
33 | }
34 |
35 | self.end = function () {
36 | self.emit(endName);
37 | }
38 |
39 | if (em && em.on) {
40 | em.on(endName, function () {
41 | self.emit(endName);
42 | });
43 | self.on(pipeName, function () {
44 | em.emit(pipeName);
45 | });
46 | // Check for v0.10 or Greater (Stream2 has Duplex type)
47 | if (stream.Duplex && em instanceof(stream)) {
48 | em.on('readable', function () {
49 | var x = em.read();
50 | self.emit(dataName, x);
51 | });
52 | } else {
53 | // Old Stream1 or Event support
54 | em.on(dataName, function (x) {
55 | self.emit(dataName, x);
56 | });
57 | }
58 | }
59 |
60 | function newLazy (g, h, l) {
61 | if (!g) {
62 | g = function () {
63 | return true;
64 | };
65 | }
66 | if (!h) {
67 | h = function (x) {
68 | return x;
69 | };
70 | }
71 | var lazy = new Lazy(null, opts, l);
72 | self.on(dataName, function (x, y) {
73 | if (g.call(lazy, x)) {
74 | lazy.emit(dataName, h(x), y);
75 | }
76 | });
77 | self.once(pipeName, function () {
78 | lazy.emit(pipeName);
79 | });
80 | return lazy;
81 | }
82 |
83 | self.filter = function (f) {
84 | return newLazy(function (x) {
85 | return f(x);
86 | });
87 | }
88 |
89 | self.forEach = function (f) {
90 | return newLazy(function (x) {
91 | f(x);
92 | return true;
93 | });
94 | }
95 |
96 | self.map = function (f) {
97 | return newLazy(
98 | function () { return true },
99 | function (x) { return f(x) }
100 | );
101 | }
102 |
103 | self.head = function (f) {
104 | var lazy = newLazy();
105 | lazy.on(dataName, function g (x) {
106 | f(x)
107 | lazy.removeListener(dataName, g)
108 | })
109 | }
110 |
111 | self.tail = function () {
112 | var skip = true;
113 | return newLazy(function () {
114 | if (skip) {
115 | skip = false;
116 | return false;
117 | }
118 | return true;
119 | });
120 | }
121 |
122 | self.skip = function (n) {
123 | return newLazy(function () {
124 | if (n > 0) {
125 | n--;
126 | return false;
127 | }
128 | return true;
129 | });
130 | }
131 |
132 | self.take = function (n) {
133 | return newLazy(function () {
134 | if (n == 0) self.emit(pipeName);
135 | return n-- > 0;
136 | });
137 | }
138 |
139 | self.takeWhile = function (f) {
140 | var cond = true;
141 | return newLazy(function (x) {
142 | if (cond && f(x)) return true;
143 | cond = false;
144 | self.emit(pipeName);
145 | return false;
146 | });
147 | }
148 |
149 | self.foldr = function (op, i, f) {
150 | var acc = i;
151 | var lazy = newLazy();
152 | lazy.on(dataName, function g (x) {
153 | acc = op(x, acc);
154 | });
155 | lazy.once(pipeName, function () {
156 | f(acc);
157 | });
158 | }
159 |
160 | self.sum = function (f) {
161 | return self.foldr(function (x, acc) { return x + acc }, 0, f);
162 | }
163 |
164 | self.product = function (f) {
165 | return self.foldr(function (x, acc) { return x*acc }, 1, f);
166 | }
167 |
168 | self.join = function (f) {
169 | var data = []
170 | var lazy = newLazy(function (x) {
171 | data.push(x);
172 | return true;
173 | });
174 | lazy.once(pipeName, function () { f(data) });
175 | return self;
176 | }
177 |
178 | self.bucket = function (init, f) {
179 | var lazy = new Lazy(null, opts);
180 | var yieldTo = function (x) {
181 | lazy.emit(dataName, x);
182 | };
183 |
184 | var acc = init;
185 |
186 | self.on(dataName, function (x) {
187 | acc = f.call(yieldTo, acc, x);
188 | });
189 |
190 | self.once(pipeName, function () {
191 | lazy.emit(pipeName);
192 | });
193 |
194 | // flush on end event
195 | self.once(endName, function () {
196 | var finalBuffer = mergeBuffers(acc);
197 | if (finalBuffer) {
198 | yieldTo(finalBuffer);
199 | }
200 | });
201 |
202 | return lazy;
203 | }
204 |
205 | // Streams that use this should emit strings or buffers only
206 | self.__defineGetter__('lines', function () {
207 | return self.bucket([], function (chunkArray, chunk) {
208 | var newline = '\n'.charCodeAt(0), lastNewLineIndex = 0;
209 | if (typeof chunk === 'string') chunk = new Buffer(chunk);
210 | if (chunk){
211 | for (var i = 0; i < chunk.length; i++) {
212 | if (chunk[i] === newline) {
213 | // If we have content from the current chunk to append to our buffers, do it.
214 | if (i > 0) {
215 | chunkArray.push(chunk.slice(lastNewLineIndex, i));
216 | }
217 |
218 | // Wrap all our buffers and emit it.
219 | this(mergeBuffers(chunkArray));
220 | lastNewLineIndex = i + 1;
221 | }
222 | }
223 | }
224 |
225 | if (lastNewLineIndex > 0) {
226 | // New line found in the chunk, push the remaining part of the buffer.
227 | if (lastNewLineIndex < chunk.length) {
228 | chunkArray.push(chunk.slice(lastNewLineIndex));
229 | }
230 | } else {
231 | // No new line found, push the whole buffer.
232 | if (chunk && chunk.length) {
233 | chunkArray.push(chunk);
234 | }
235 | }
236 | return chunkArray;
237 | });
238 | });
239 | }
240 |
241 | Lazy.range = function () {
242 | var args = arguments;
243 | var step = 1;
244 | var infinite = false;
245 |
246 | if (args.length == 1 && typeof args[0] == 'number') {
247 | var i = 0, j = args[0];
248 | }
249 | else if (args.length == 1 && typeof args[0] == 'string') { // 'start[,next]..[end]'
250 | var arg = args[0];
251 | var startOpen = false, endClosed = false;
252 | if (arg[0] == '(' || arg[0] == '[') {
253 | if (arg[0] == '(') startOpen = true;
254 | arg = arg.slice(1);
255 | }
256 | if (arg.slice(-1) == ']') endClosed = true;
257 |
258 | var parts = arg.split('..');
259 | if (parts.length != 2)
260 | throw new Error("single argument range takes 'start..' or 'start..end' or 'start,next..end'");
261 |
262 | if (parts[1] == '') { // 'start..'
263 | var i = parts[0];
264 | infinite = true;
265 | }
266 | else { // 'start[,next]..end'
267 | var progression = parts[0].split(',');
268 | if (progression.length == 1) { // start..end
269 | var i = parts[0], j = parts[1];
270 | }
271 | else { // 'start,next..end'
272 | var i = progression[0], j = parts[1];
273 | step = Math.abs(progression[1]-i);
274 | }
275 | }
276 |
277 | i = parseInt(i, 10);
278 | j = parseInt(j, 10);
279 |
280 | if (startOpen) {
281 | if (infinite || i < j) i++;
282 | else i--;
283 | }
284 |
285 | if (endClosed) {
286 | if (i < j) j++;
287 | else j--;
288 | }
289 | }
290 | else if (args.length == 2 || args.length == 3) { // start, end[, step]
291 | var i = args[0], j = args[1];
292 | if (args.length == 3) {
293 | var step = args[2];
294 | }
295 | }
296 | else {
297 | throw new Error("range takes 1, 2 or 3 arguments");
298 | }
299 | var lazy = new Lazy;
300 | var stopInfinite = false;
301 | lazy.on('pipe', function () {
302 | stopInfinite = true;
303 | });
304 | if (infinite) {
305 | process.nextTick(function g () {
306 | if (stopInfinite) return;
307 | lazy.emit('data', i++);
308 | process.nextTick(g);
309 | });
310 | }
311 | else {
312 | process.nextTick(function () {
313 | if (i < j) {
314 | for (; ij; i-=step) {
320 | lazy.emit('data', i)
321 | }
322 | }
323 | lazy.emit('end');
324 | });
325 | }
326 | return lazy;
327 | }
328 |
329 | var mergeBuffers = function mergeBuffers(buffers) {
330 | // We expect buffers to be a non-empty Array
331 | if (!buffers || !Array.isArray(buffers) || !buffers.length) return;
332 |
333 | var finalBufferLength, finalBuffer, currentBuffer, currentSize = 0;
334 |
335 | // Sum all the buffers lengths
336 | finalBufferLength = buffers.reduce(function(left, right) { return (left.length||left) + (right.length||right); }, 0);
337 | finalBuffer = new Buffer(finalBufferLength);
338 | while(buffers.length) {
339 | currentBuffer = buffers.shift();
340 | currentBuffer.copy(finalBuffer, currentSize);
341 | currentSize += currentBuffer.length;
342 | }
343 |
344 | return finalBuffer;
345 | }
346 |
347 |
348 | util.inherits(Lazy, EventEmitter);
349 | module.exports = Lazy;
350 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lazy",
3 | "version": "1.0.11",
4 | "description": "Lazy lists for node",
5 | "main": "./lazy.js",
6 | "keywords": [
7 | "lazy lists", "functional"
8 | ],
9 | "author": {
10 | "name": "Peteris Krumins",
11 | "email": "peteris.krumins@gmail.com",
12 | "web": "http://www.catonmat.net",
13 | "twitter": "pkrumins"
14 | },
15 | "license": {
16 | "type": "MIT"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "http://github.com/pkrumins/node-lazy.git"
21 | },
22 | "devDependencies" : {
23 | "expresso" : ">=0.7.5"
24 | },
25 | "engines": {
26 | "node": ">=0.2.0"
27 | },
28 | "scripts": {
29 | "test": "expresso"
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/test/bucket.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var Lazy = require('..');
3 |
4 | exports.bucket = function () {
5 | var joined = false;
6 | var lazy = new Lazy;
7 | lazy.bucket('', function splitter (acc, x) {
8 | var accx = acc + x;
9 | var i = accx.indexOf('\n');
10 | if (i >= 0) {
11 | this(accx.slice(0, i));
12 | return splitter.call(this, accx.slice(i + 1), '');
13 | }
14 | return accx;
15 | }).join(function (lines) {
16 | assert.deepEqual(lines, 'foo bar baz quux moo'.split(' '));
17 | joined = true;
18 | });
19 |
20 | setTimeout(function () {
21 | lazy.emit('data', 'foo\nba');
22 | }, 50);
23 | setTimeout(function () {
24 | lazy.emit('data', 'r');
25 | }, 100);
26 | setTimeout(function () {
27 | lazy.emit('data', '\nbaz\nquux\nm');
28 | }, 150);
29 | setTimeout(function () {
30 | lazy.emit('data', 'oo');
31 | }, 200);
32 | setTimeout(function () {
33 | lazy.emit('data', '\nzoom');
34 | lazy.emit('end');
35 | assert.ok(joined);
36 | }, 250);
37 | };
38 |
--------------------------------------------------------------------------------
/test/complex.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var Lazy = require('..');
3 | var expresso = expresso;
4 |
5 | function range(i, j) {
6 | var r = [];
7 | for (;i 5 }).join(function (xs) {
16 | assert.deepEqual(xs, [6,7,8,9]);
17 | executed = true;
18 | });
19 |
20 | range(0,10).forEach(function (x) {
21 | lazy.emit('data', x);
22 | });
23 | lazy.emit('pipe');
24 |
25 | assert.ok(executed, 'join didn\'t execute');
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/test/foldr.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var Lazy = require('..');
3 | var expresso = expresso;
4 |
5 | function range(i, j) {
6 | var r = [];
7 | for (;i 0);
13 | })
14 | .join(function (lines) {
15 | assert.deepEqual(
16 | lines.map(function (x) { return x.toString() }),
17 | 'foo bar baz quux moo'.split(' ')
18 | );
19 | joined = true;
20 | });
21 | ;
22 |
23 | setTimeout(function () {
24 | lazy.push(new Buffer('foo\nbar'));
25 | lazy.push(new Buffer('\nbaz\nquux\nmoo'));
26 | lazy.push(new Buffer(''));
27 | lazy.push(new Buffer('\ndoom'));
28 | lazy.end();
29 | assert.ok(joined);
30 | }, 50);
31 | };
32 |
33 | exports['string lines'] = function () {
34 | var lazy = Lazy();
35 | var joined = false;
36 | lazy.lines
37 | .forEach(function (line) {
38 | assert.ok(line instanceof Buffer);
39 | assert.ok(!line.toString().match(/\n/));
40 | assert.ok(line.length > 0);
41 | })
42 | .join(function (lines) {
43 | assert.deepEqual(
44 | lines.map(function (x) { return x.toString() }),
45 | 'foo bar baz quux moo'.split(' ')
46 | );
47 | joined = true;
48 | });
49 | ;
50 |
51 | setTimeout(function () {
52 | lazy.push('foo\nbar');
53 | lazy.push('\nbaz\nquux\nmoo');
54 | lazy.push('');
55 | lazy.push('\ndoom');
56 | lazy.end();
57 | assert.ok(joined);
58 | }, 50);
59 | };
60 |
61 | exports.endStream = function () {
62 | var to = setTimeout(function () {
63 | assert.fail('never finished');
64 | }, 2500);
65 |
66 | var em = new EventEmitter;
67 | var i = 0;
68 | var lines = [];
69 | Lazy(em).lines.forEach(function (line) {
70 | i ++;
71 | lines.push(line);
72 | if (i == 2) {
73 | clearTimeout(to);
74 | assert.eql(lines.map(String), [ 'foo', 'bar' ]);
75 | }
76 | });
77 |
78 | setTimeout(function () {
79 | em.emit('data', 'fo');
80 | }, 100);
81 |
82 | setTimeout(function () {
83 | em.emit('data', 'o\nbar');
84 | }, 150);
85 |
86 | setTimeout(function () {
87 | em.emit('end');
88 | }, 200);
89 | };
90 |
--------------------------------------------------------------------------------
/test/map.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var Lazy = require('..');
3 | var expresso = expresso;
4 |
5 | function range(i, j) {
6 | var r = [];
7 | for (;i i) for (;ij;i-=s) r.push(i);
10 | return r;
11 | }
12 |
13 | exports['infinite range'] = function () {
14 | var joinExecuted = false;
15 | Lazy.range('10..').take(10).join(function (xs) {
16 | joinExecuted = true;
17 | assert.deepEqual(xs, range(10, 20));
18 | assert.equal(xs.length, 10);
19 | });
20 |
21 | setTimeout(function () {
22 | assert.ok(joinExecuted, 'join didn\'t execute');
23 | }, 2000);
24 | }
25 |
26 | exports['infinite range half-open'] = function () {
27 | var joinExecuted = false;
28 | Lazy.range('(10..').take(10).join(function (xs) {
29 | joinExecuted = true;
30 | assert.deepEqual(xs, range(11, 21));
31 | assert.equal(xs.length, 10);
32 | });
33 |
34 | setTimeout(function () {
35 | assert.ok(joinExecuted, 'join didn\'t execute');
36 | }, 2000);
37 | }
38 |
39 | exports['range i'] = function () {
40 | var joinExecuted = false;
41 | Lazy.range(10).join(function (xs) {
42 | joinExecuted = true;
43 | assert.deepEqual(xs, range(0, 10));
44 | assert.equal(xs.length, 10);
45 | });
46 |
47 | setTimeout(function () {
48 | assert.ok(joinExecuted, 'join didn\'t execute');
49 | }, 2000);
50 | }
51 |
52 | exports['range i,j (ij)'] = function () {
79 | var joinExecuted = false;
80 | Lazy.range(10, 0, 2).join(function (xs) {
81 | joinExecuted = true;
82 | assert.deepEqual(xs, range(10, 0, 2));
83 | assert.equal(xs.length, 5);
84 | });
85 |
86 | setTimeout(function () {
87 | assert.ok(joinExecuted, 'join didn\'t execute');
88 | }, 2000);
89 | }
90 |
91 | exports['range i,j (i>j)'] = function () {
92 | var joinExecuted = false;
93 | Lazy.range(10, -8).join(function (xs) {
94 | joinExecuted = true;
95 | assert.deepEqual(xs, range(10, -8));
96 | assert.equal(xs.length, 18);
97 | });
98 |
99 | setTimeout(function () {
100 | assert.ok(joinExecuted, 'join didn\'t execute');
101 | }, 2000);
102 | }
103 |
104 | exports['range i..j (ij)'] = function () {
118 | var joinExecuted = false;
119 | Lazy.range('50..44').join(function (xs) {
120 | joinExecuted = true;
121 | assert.deepEqual(xs, range(50, 44));
122 | assert.equal(xs.length, 6);
123 | });
124 |
125 | setTimeout(function () {
126 | assert.ok(joinExecuted, 'join didn\'t execute');
127 | }, 2000);
128 | }
129 |
130 | exports['range i,next..j (ij)'] = function () {
144 | var joinExecuted = false;
145 | Lazy.range('4,3.9..1').join(function (xs) {
146 | joinExecuted = true;
147 | assert.deepEqual(xs, range(4,1,0.1));
148 | assert.equal(xs.length, 30);
149 | });
150 |
151 | setTimeout(function () {
152 | assert.ok(joinExecuted, 'join didn\'t execute');
153 | }, 2000);
154 | }
155 |
156 | exports['range [i..j] (ij)'] = function () {
170 | var joinExecuted = false;
171 | Lazy.range('[10..1]').join(function (xs) {
172 | joinExecuted = true;
173 | assert.deepEqual(xs, range(10,0));
174 | assert.equal(xs.length, 10);
175 | });
176 |
177 | setTimeout(function () {
178 | assert.ok(joinExecuted, 'join didn\'t execute');
179 | }, 2000);
180 | }
181 |
182 | exports['range [i..j) (ij)'] = function () {
196 | var joinExecuted = false;
197 | Lazy.range('[10..1)').join(function (xs) {
198 | joinExecuted = true;
199 | assert.deepEqual(xs, range(10,1));
200 | assert.equal(xs.length, 9);
201 | });
202 |
203 | setTimeout(function () {
204 | assert.ok(joinExecuted, 'join didn\'t execute');
205 | }, 2000);
206 | }
207 |
208 | exports['range (i..j] (ij)'] = function () {
222 | var joinExecuted = false;
223 | Lazy.range('(10..1]').join(function (xs) {
224 | joinExecuted = true;
225 | assert.deepEqual(xs, range(9,0));
226 | assert.equal(xs.length, 9);
227 | });
228 |
229 | setTimeout(function () {
230 | assert.ok(joinExecuted, 'join didn\'t execute');
231 | }, 2000);
232 | }
233 |
234 | exports['range (i..j) (ij)'] = function () {
248 | var joinExecuted = false;
249 | Lazy.range('(10..1)').join(function (xs) {
250 | joinExecuted = true;
251 | assert.deepEqual(xs, range(9,1));
252 | assert.equal(xs.length, 8);
253 | });
254 |
255 | setTimeout(function () {
256 | assert.ok(joinExecuted, 'join didn\'t execute');
257 | }, 2000);
258 | }
259 |
260 | exports['range [i,step..j]'] = function () {
261 | var joinExecuted = false;
262 | Lazy.range('[5,10..50]').join(function (xs) {
263 | joinExecuted = true;
264 | assert.deepEqual(xs, range(5,51,5));
265 | assert.equal(xs.length, 10);
266 | });
267 |
268 | setTimeout(function () {
269 | assert.ok(joinExecuted, 'join didn\'t execute');
270 | }, 2000);
271 | }
272 |
273 |
--------------------------------------------------------------------------------
/test/skip.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var Lazy = require('..');
3 | var expresso = expresso;
4 |
5 | function range(i, j) {
6 | var r = [];
7 | for (;i