[Nice-commit] Nice/stdlib/nice/functional generator.nice,NONE,1.1 iterator.nice,NONE,1.1
Brought to you by:
bonniot
From: <xo...@pr...> - 2004-01-26 18:28:29
|
Update of /cvsroot/nice/Nice/stdlib/nice/functional In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14188/stdlib/nice/functional Added Files: generator.nice iterator.nice Log Message: New iterator/generator library. Provides a number of methods for dealing with iterators (i.e., Java-style Iterator<T>), and generators (Nice methods void->T which return a new element on each call, and throw GeneratorEnd when they don't have any more elements to yield). Things like map, filter, fold, zip, unzip, and so on. --- NEW FILE: generator.nice --- /**************************************************************************/ /* N I C E */ /* A high-level object-oriented research language */ /* (c) Daniel Bonniot 2003 */ /* */ /* This package is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU Lesser General Public License as */ /* published by the Free Software Foundation; either version 2 of the */ /* License, or (at your option) any later version. */ /* */ /**************************************************************************/ /** A library for dealing with iterators - either Java-style <code>Iterator<T%gt;</code>s (in iterator.nice), or Nice-style ()->T generators (in generator.nice) @author Bryn Keller (xo...@us...) */ /** * Special class thrown by generator functions when * they should end. */ class GeneratorEnd extends Throwable {} /** * Stop a generator function. */ <A> A stop() { throw new GeneratorEnd(); } <A> ()->A generator(Iterator<A> iter) = () => { if (iter.hasNext()) return iter.next(); else return stop(); }; <A> ()->A generator(Collection<A> coll) = coll.iterator.generator; /** * Function composition. */ <A,B,C> A->C compose(B->C second, A->B first) = A input => second(first(input)); <A,B> (B, A) swap((A a, B b)) = (b, a); <A,B,C> (C, B) first((A a, B b), A->C xform) = (xform(a), b); <A,B> ()->B map(()->A gen, A->B xform) = () => xform(gen()); /** * Produces a new generator which only yields elements of the initial * generator that pass the test function. * * @param gen the original generator * @param filt the function that tests whether an item should be * in the new iterator. */ <A> ()->A filter(()->A gen, A->boolean filt) = ()=> { var A value = gen(); while(!filt(value)) value = gen(); return value; }; /** * Folds a generator. For example, * <pre> * int sum(()->int gen) = gen.fold(`+`, 0); * </pre> * * @see nice.lang#foldLeft */ <A,B> B fold(()->A gen, (B,A)->B foldfunc, B start) { try { while(true) { start = foldfunc(start, gen()); } } catch (GeneratorEnd e) { //Ignore. } return start; } /** * Checks that all elements of a generator are members of a given predicate. * Stops executing as soon as one element fails the test. */ <A> boolean all(void->A gen, A->boolean predicate) { try { while(true) { if (!predicate(gen())) return false; } } catch(GeneratorEnd e) { return true; } } /** * Checks that at least one element of a generator is a member of a given * predicate. Stops executing as soon as one element passes the test. */ <A> boolean any(void->A gen, A->boolean predicate) { try { while(true) { if (predicate(gen())) return true; } } catch(GeneratorEnd e) { return false; } } /** * Calls a function once for each item yielded by a generator. */ <A> void foreach(()->A gen, A->void func) { try { while(true) func(gen()); } catch (GeneratorEnd e) { } } /** * Creates a <code>List</code> from the elements of a generator. */ <A> List<A> toList(()->A gen) { let List<A> list = new ArrayList(); gen.foreach(A a => list.add(a)); return list; } /** * Add all the elements of a generator to a collection. */ <A> void addAll(Collection<A> coll, ()->A gen) { gen.foreach(A a => coll.add(a)); } /** * Converts 2 generators into a single generator * that yields 2-tuples */ //<A,B> ()->(A,B) zip(()->A one, ()->B two) = () => (one(), two()); <A,B> ()->(A,B) zip(()->A one, ()->B two) = unsafeZip(cast([one,two])); /** * Converts 3 generators into a single generator * that yields 3-tuples */ <A,B,C> ()->(A,B,C) zip(()->A one, ()->B two, ()->C three) = unsafeZip(cast([one,two,three])); /** * Converts 4 generators into a single generator * that yields 4-tuples */ <A,B,C,D> ()->(A,B,C,D) zip(()->A one, ()->B two, ()->C three, ()->D four) = unsafeZip(cast([one,two,three,four])); /** * Merges two generators using the given zipper function. For example: * <pre> * println(zipWith(["hello, "].generator, ["world"].generator, * (String s1, String s2) => s1 + s2)()); * </pre> * will print "hello, world". */ <A,B,C> void->C zipWith(void->A one, void->B two, (A,B)->C zipper) = unsafeZipWith(cast([one,two]), cast(zipper)); /** * Merges three generators using the given zipper function. * @see <A,B,C> void->C zipWith(void->A, void->B, (A,B)->C) */ <A,B,C,D> void->D zipWith(void->A one, void->B two, void->C three, (A,B,C)->D zipper) = unsafeZipWith(cast([one,two,three]), cast(zipper)); /** * Merges four generators using the given zipper function. * @see <A,B,C> void->C zipWith(void->A, void->B, (A,B)->C) */ <A,B,C,D,E> void->E zipWith(void->A one, void->B two, void->C three, void->D four, (A,B,C,D)->E zipper) = unsafeZipWith(cast([one,two,three,four]), cast(zipper)); /** * Back door to Nice's method dispatching. You promise that you know you're * passing a method, and arguments that make sense for the method. Nice * calls it and hands you back the results, which you promise are of the * correct type. The compiler cannot verify this, the programmer is responsible * for ensuring type safety. */ private <T,U> U apply(T fun, Object[?] args) { // The internal representation of functional values gnu.mapping.Procedure proc = cast(fun); return cast(proc.applyN(args)); } /** * Converts an array of generators into a single generator that yields * tuples of the appropriate size. Bypasses Nice's type system, so users * must ensure that types are correct. It's usually better to use one of * the strongly typed versions above instead. */ private <R> ()->R unsafeZip((()->?Object)[] generators) { let (?Object[], int->?Object)->?Object[] f = fill; return () => { ?Object[] res = f(new Object[generators.size()], int i => generators[i]()); return cast(res); }; } /** * Converts an array of generators into a single generator that yields * the results of calling <code>func</code> with arguments from the * array of generators. Bypasses Nice's type system, so users * must ensure that types are correct. It's usually better to use one of * the strongly typed versions above instead. */ private <R> ()->R unsafeZipWith(Array<()->Object> generators, Object->R func) { let (?Object[], int->Object)->Object[] f = fill; return () => apply(func, f(new Object[generators.size()], int i => generators[i]())); } private <R> R unsafeUnzip(()->Object gen) { Object[] tuple = cast(gen()); List<LinkedList<Object>> cache = new ArrayList(); tuple.size.times(int i => { cache.add(new LinkedList()); cache[i].add(tuple[i]); }); Object nextItem(int index) { if (cache[index].size == 0) { tuple = cast(gen()); tuple.size.times( int i => cache[i].add(tuple[i])); } return cache[index].removeFirst(); } let (?Object[], int->?Object)->?Object[] f = fill; ?Object[] funcs = f(new Object[tuple.size], int i => () => nextItem(i)); return cast(funcs); } /** * Converts a generator which yields 2-tuples into into a 2-tuple * containing 2 generators. */ <A,B> (()->A, ()->B) unzip(()->(A,B) gen) = unsafeUnzip(gen); /** * Converts a generator which yields 3-tuples into into a 3-tuple * containing 3 generators. */ <A,B,C> (()->A, ()->B, ()->C) unzip(()->(A,B,C) gen) = unsafeUnzip(gen); /** * Converts a generator which yields 4-tuples into into a 4-tuple * containing 4 generators. */ <A,B,C,D> (()->A, ()->B, ()->C, ()->D) unzip(()->(A,B,C,D) gen) = unsafeUnzip(gen); /** * Returns a new generator made up of elements from the original * generator which pass the test function. The first element which * doesn't pass the test function marks the end of the new generator, * and the item that failed is not included in the new generator. */ <A> ()->A takeWhile(()->A gen, A->boolean test) = ()=> { let value = gen(); if (test(value)) return value; else return stop(); }; /** * The reverse of takeWhile, this function returns a new generator * based on the original generator, but with all the leading elements * which pass the test function removed. */ <A> ()->A dropWhile(()->A gen, A->boolean test) { var failed = false; return ()=> { if (failed) return gen(); while(!failed) { let value = gen(); if (!test(value)) { failed = true; return value; } } return gen(); }; } /** * Returns a new generator which has the first <i>number</i> elements * from the original generator. */ <A> ()->A take(()->A gen, int number) requires number >= 0 { return gen.takeWhile(A a => number-- > 0); } /** * Returns a new generator which has the same contents as the original * generator, but without the first <i>number</i> elements. */ <A> ()->A drop(()->A gen, int number) requires number >= 0 { return gen.dropWhile(A a => number-- > 0); } /** * Blends two generators together, taking an element from the * first, then from the second, then again from the first, etc. */ <A> ()->A blend(()->A one, ()->A two) { boolean first = true; return () => { var A item = first ? one() : two(); first = !first; return item; }; } /** * Joins the second generator to the first. That is, produces a new * generator which yields all the elements in the first generator, * followed by all the elements in the second generator. */ <A> ()->A concat(()->A first, ()->A second) { var failed = false; return () => { if (failed) return second(); else try { return first(); } catch (GeneratorEnd e) { failed = true; return second(); } }; } /** * Returns two generators based on the generator argument - the first * generator contains all the elements of the original generator that * passed the test, until one failed. All the remaining items in the * original generator now appear in the second generator. */ <A> (()->A, ()->A) span(()->A gen, A-> boolean test) { var failed = false; LinkedList<A> passed = new LinkedList(); return (() => { if (!passed.isEmpty) return passed.removeFirst(); else { if (failed) return stop(); else { let value = gen(); if (test(value)) return value; else { failed = true; gen = (() => { return value; }).concat(gen); } } } return stop(); }, () => { while(!failed) { let value = gen(); if (test(value)) passed.add(value); else { failed = true; return value; } } return gen(); }); } /** * Returns two generators based on the generator argument - the first * generator contains all the elements of the original generator that * passed the test. Those elements that didn't pass the test will * appear in the second generator. */ <A> (()->A, ()->A) partition(()->A gen, A-> boolean test) { let LinkedList<A> passed = new LinkedList(); let LinkedList<A> failed = new LinkedList(); void load() { let v = gen(); if (test(v)) passed.add(v); else failed.add(v); } return ( () => { while (passed.isEmpty) load(); return passed.removeFirst(); }, () => { while (failed.isEmpty) load(); return failed.removeFirst(); } ); } /** * Like a for loop. */ <T> void times(int max, int->void func) { int counter = 0; while(counter < max) { try { func(counter++); } catch (GeneratorEnd e) { return; } } } /** * Like a for loop. */ <T> void times(int max, void->void func) { int counter = 0; while(counter < max) { try { func(); counter++; } catch (GeneratorEnd e) { return; } } } // ---- Some handy functions for testing ---- /** * A generator which yields natural numbers. */ ()->int naturals() { var int num = 1; return (()=>num++); } /** * Is the number even? */ private boolean _even(int num) = num % 2 == 0; /** * Is the number odd? */ private boolean _odd(int num) = !_even(num); --- NEW FILE: iterator.nice --- /**************************************************************************/ /* N I C E */ /* A high-level object-oriented research language */ /* (c) Daniel Bonniot 2003 */ /* */ /* This package is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU Lesser General Public License as */ /* published by the Free Software Foundation; either version 2 of the */ /* License, or (at your option) any later version. */ /* */ /**************************************************************************/ /** A library for dealing with iterators - either Java-style <code>Iterator<T%gt;</code>s (in iterator.nice), or Nice-style ()->T generators (in generator.nice) @author Bryn Keller (xo...@us...) */ /** * Creates an <code>Iterator</code> from a function. * The function should call <code>stop()</code> to indicate * that the iteraton should stop. */ <A> Iterator<A> iterator(()->A func) = new FunctionIterator(func: func); /** * Maps a function over an iterator, producing a new iterator. */ <A,B> Iterator<B> map(Iterator<A> it, A->B xform) = iterator(() => xform(it.next())); /** * Produces a new iterator which only yields elements of the initial * iterator that pass the test function. * * @param it the original iterator * @param filt the function that tests whether an item should be * in the new iterator. */ <A> Iterator<A> filter(Iterator<A> it, A->boolean filt) = iterator(()=> { while (it.hasNext()) { let item = it.next(); if (filt(item)) { return item; } } return stop(); }); /** * Folds an iterator. For example, * <pre> * int sum(Iterator<int> it) = it.fold(`+`, 0); * </pre> * * @see nice.lang#foldLeft */ <A,B> B fold(Iterator<A> it, (B,A)->B foldfunc, B start) { while(it.hasNext()) { start = foldfunc(start, it.next()); } return start; } /** * Folds an iterator. For example, * <pre> * int sum(Iterator<int> it) = it.fold1(`+`); * </pre> * * @see nice.lang#foldLeft */ <A> A fold1(Iterator<A> it, (A,A)->A foldfunc) { A start = it.next(); while(it.hasNext()) { start = foldfunc(start, it.next()); } return start; } /** * Checks that all elements of an iterator are members of a given predicate. * Stops executing as soon as one element fails the test. */ <A> boolean all(Iterator<A> iter, A->boolean predicate) { try { while(iter.hasNext()) { if (!predicate(iter.next())) return false; } return true; } catch(GeneratorEnd e) { return true; } } /** * Checks that at least one element of an iterator is a member of a given * predicate. Stops executing as soon as one element passes the test. */ <A> boolean any(Iterator<A> iter, A->boolean predicate) { try { while(iter.hasNext()) { if (predicate(iter.next())) return true; } return false; } catch(GeneratorEnd e) { return false; } } /** * Calls a function once for each item in an <code>Iterator</code>. * If you want to keep the results of the function calls, use <code>map</code> * instead. * * @see map */ <A> void foreach(Iterator<A> it, A->void func) { while(it.hasNext()) func(it.next()); } /** * Creates a <code>List</code> from the elements of an iterator. */ <A> List<A> toList(Iterator<A> it) { let List<A> list = new ArrayList(); list.addAll(it); return list; } /** * Add all the elements of an itertor to a collection. */ <A> void addAll(Collection<A> coll, Iterator<A> it) { while(it.hasNext()) coll.add(it.next()); } /** * Converts 2 iterators into a single iterator * that yields 2-tuples. */ <A,B> Iterator<(A,B)> zip(Iterator<A> one, Iterator<B> two) = iterator(zip(one.generator, two.generator)); /** * Converts 3 iterators into a single iterator * that yields 3-tuples. */ <A,B,C> Iterator<(A,B,C)> zip(Iterator<A> one, Iterator<B> two, Iterator<C> three) = iterator(zip(one.generator, two.generator, three.generator)); /** * Converts 4 iterators into a single iterator * that yields 4-tuples. */ <A,B,C,D> Iterator<(A,B,C,D)> zip(Iterator<A> one, Iterator<B> two, Iterator<C> three, Iterator<D> four) = iterator(zip(one.generator, two.generator, three.generator, four.generator)); /** * Converts an iterator which yields 2-tuples into into a 2-tuple * containing 2 iterators. */ <A,B> (Iterator<A>, Iterator<B>) unzip(Iterator<(A,B)> it) { (void->A one, void->B two) = unzip(it.generator); return (one.iterator, two.iterator); } /** * Converts an iterator which yields 2-tuples into into a 2-tuple * containing 3 iterators. */ <A,B,C> (Iterator<A>, Iterator<B>, Iterator<C>) unzip(Iterator<(A,B,C)> it) { (void->A one, void->B two, void->C three) = unzip(it.generator()); return (one.iterator, two.iterator, three.iterator); } /** * Converts an iterator which yields 2-tuples into into a 2-tuple * containing 4 iterators. */ <A,B,C,D> (Iterator<A>, Iterator<B>, Iterator<C>, Iterator<D>) unzip(Iterator<(A,B,C,D)> it) { (void->A one, void->B two, void->C three, void->D four) = unzip(it.generator()); return (one.iterator, two.iterator, three.iterator, four.iterator); } /** * Merges two iterators using the given zipper function. For example: * <pre> * println(zipWith(["hello, "].iterator, ["world"].iterator, * (String s1, String s2) => s1 + s2).next()); * </pre> * will print "hello, world". */ <A,B,C> Iterator<C> zipWith(Iterator<A> one, Iterator<B> two, (A,B)->C zipper) { return iterator(zipWith(generator(one), generator(two), zipper)); } /** * Merges three iterators using the given zipper function. * @see <A,B,C> Iterator<C> zipWith(Iterator<A>, Iterator<B>, (A,B)->C) */ <A,B,C,D> Iterator<D> zipWith(Iterator<A> one, Iterator<B> two, Iterator<C> three, (A,B,C)->D zipper) { return iterator(zipWith(generator(one), generator(two), generator(three), zipper)); } /** * Merges three iterators using the given zipper function. * @see <A,B,C> Iterator<C> zipWith(Iterator<A>, Iterator<B>, (A,B)->C) */ <A,B,C,D,E> Iterator<E> zipWith(Iterator<A> one, Iterator<B> two, Iterator<C> three, Iterator<D> four, (A,B,C,D)->E zipper) { return iterator(zipWith(generator(one), generator(two), generator(three), generator(four), zipper)); } /** * Returns a new iterator made up of elements from the original * iterator which pass the test function. The first element which * doesn't pass the test function marks the end of the new iterator, * and the item that failed is not included in the new iterator. */ <A> Iterator<A> takeWhile(Iterator<A> it, A->boolean test) = iterator(()=> { if (it.hasNext()) { let n = it.next(); if (test(n)) { return n; } } return stop(); }); /** * The reverse of takeWhile, this function returns a new iterator * based on the original iterator, but with all the leading elements * which pass the test function removed. */ <A> Iterator<A> dropWhile(Iterator<A> it, A->boolean test) { var failed = false; return iterator(()=> { if (failed) { if (it.hasNext()) { return it.next(); } } else { while (it.hasNext()) { let n = it.next(); if (!test(n)) { failed = true; return n; } } } return stop(); }); } /** * Returns a new iterator which has the first <i>number</i> elements * from the original iterator. */ <A> Iterator<A> take(Iterator<A> it, int number) requires number >= 0 { return iterator(()=> { if (number > 0) { number--; return it.next(); } return stop(); }); } /** * Returns a new iterator which has the same contents as the original * iterator, but without the first <i>number</i> elements. */ <A> Iterator<A> drop(Iterator<A> it, int number) requires number >= 0 { return iterator(()=> { while (number > 0) { number--; it.next(); } return it.next(); }); } /** * Blends two iterators together, taking an element from the * first, then from the second, then again from the first, etc. */ <A> Iterator<A> blend(Iterator<A> one, Iterator<A> two) { boolean first = true; return iterator( () => { var A item = cast(null); if (first) item = one.next(); else item = two.next(); first = !first; return item; }); } /** * Joins the second iterator to the first. That is, produces a new * iterator which yields all the elements in the first iterator, * followed by all the elements in the second iterator. */ <A> Iterator<A> concat(Iterator<A> first, Iterator<A> second) { return iterator(() => first.hasNext()? first.next() : second.next()); } /** * Returns two iterators based on the iterator argument - the first * iterator contains all the elements of the original iterator that * passed the test, until one failed. All the remaining items in the * original iterator now appear in the second iterator. */ <A> (Iterator<A>, Iterator<A>) span(Iterator<A> it, A-> boolean test) { (void->A left, void->A right) = it.generator().span(test); return (left.iterator(), right.iterator()); } /** * Returns two iterators based on the iterator argument - the first * iterator contains all the elements of the original iterator that * passed the test. Those elements that didn't pass the test will * appear in the second iterator. */ <A> (Iterator<A>, Iterator<A>) partition(Iterator<A> it, A-> boolean test) { (void->A pass, void->A fail) = it.generator.partition(test); return (pass.iterator, fail.iterator); } // ---- Some handy functions for testing ---- /** * An iterator which yields natural numbers. */ private Iterator<int> naturals_iter() { var int num = 1; return iterator(()=>num++); } // ---- Private implementation details ----- /** * This class wraps a function and turns it into an <code>Iterator.</code> * The function should throw <code>GeneratorEnd</code> to indicate * that the iteraton should stop. Use the <code>iterator</code> * function instead of using this class directly. * * @see iterator */ private class FunctionIterator<A> implements Iterator<A> { ()-> A func; A cache = cast(null); boolean cacheLoaded = false; boolean finished = false; hasNext() { if (cacheLoaded) return true; if (finished) return false; else { try { cache = (this.func)(); cacheLoaded = true; } catch (GeneratorEnd e) { finished = true; } catch (NoSuchElementException e) { finished = true; } return cacheLoaded; } } <A> next() { if (this.hasNext()) { A item = cache; cache = cast(null); cacheLoaded = false; return item; } else { throw new NoSuchElementException(); } } remove() { throw new UnsupportedOperationException(); } } /** * Synchronized wrapper around an <code>Iterator</code> */ // class SyncIterator<A> implements Iterator<A> // { // Iterator<A> inner; // synchronized next() // { // return inner.next(); // } // synchronized hasNext() // { // return inner.hasNext(); // } // synchronized remove() // { // return inner.remove(); // } // } void _testIterators() { var nats = naturals_iter(); let List<int> list = new ArrayList(); list.addAll(nats.take(10)); assert list.size() == 10; assert list[0] == 1; assert list[9] == 10; assert naturals_iter().take(10).drop(10).hasNext() == false; assert naturals_iter().takeWhile(int i => i < 11).toList().size() == 10; assert naturals_iter().dropWhile(int i => i < 11).next() == 11; nats = naturals_iter().map(int i => i + 2); assert nats.next() == 3; assert nats.next() == 4; nats = naturals_iter().filter(_even); assert nats.next() == 2; assert nats.next() == 4; assert nats.next() == 6; let (int,int)-> int intAdd = `+`; assert naturals_iter().take(5).fold(intAdd, 0) == 15; assert naturals_iter().take(5).fold1(intAdd) == 15; (Iterator<int> evens, Iterator<int> odds) = naturals_iter().partition(_even); assert evens.next() == 2; assert evens.next() == 4; assert odds.next() == 1; assert odds.next() == 3; (evens, odds) = naturals_iter().span(_even); assert !evens.hasNext(); assert odds.next() == 1; assert naturals_iter().take(10).concat( naturals_iter().take(10)).toList().size() == 20; (evens, odds) = naturals_iter().partition(_even); var it = odds.blend(evens); assert it.next() == 1; assert it.next() == 2; assert it.next() == 3; //Test fails due to compiler bug var zipped = zip(naturals_iter(), naturals_iter()); (int left, int right) = zipped.next(); assert left == right && left == 1; var Iterator<int> doubled = zipWith(naturals_iter(), naturals_iter(), intAdd); assert doubled.next() == 2; assert doubled.next() == 4; assert doubled.next() == 6; var Iterator<int> trebled = zipWith(naturals_iter(), naturals_iter(), naturals_iter(), (int a, int b, int c) => a + b + c); assert trebled.next() == 3; assert trebled.next() == 6; assert trebled.next() == 9; } void _testGenerators() { var nats = naturals(); let List<int> list = new ArrayList(); list.addAll(nats.take(10)); assert list.size() == 10: "" + list.size(); assert list[0] == 1; assert list[9] == 10; assert naturals().take(10).drop(10).iterator.hasNext() == false; assert naturals().takeWhile(int i => i < 11).toList().size() == 10; assert naturals().dropWhile(int i => i < 11)() == 11; nats = naturals().map(int i => i + 2); assert nats() == 3; assert nats() == 4; nats = naturals().filter(_even); assert nats() == 2; assert nats() == 4; assert nats() == 6; let (int,int)-> int intAdd = `+`; assert naturals().take(5).fold(intAdd, 0) == 15; // assert naturals().take(5).fold1(intAdd) == 15; (void->int evens, void->int odds) = naturals().partition(_even); assert evens() == 2; assert evens() == 4; assert odds() == 1; assert odds() == 3; (evens, odds) = naturals().span(_even); assert !evens.iterator.hasNext(); assert odds() == 1; assert naturals().take(10).concat(naturals().take(10)).toList().size() == 20; (evens, odds) = naturals().partition(_even); var it = odds.blend(evens); assert it() == 1; assert it() == 2; assert it() == 3; // Test fails due to compiler bug var zipped = zip(naturals(), naturals()); (int left, int right) = zipped(); assert left == right && left == 1; (void->int leftGen, void->int rightGen) = unzip(zip(naturals(), naturals())); left = leftGen(); right = rightGen(); assert left == right && left == 1; assert leftGen() == 2; assert leftGen() == 3; assert rightGen() == 2; var doubled = zipWith(naturals(), naturals(), intAdd); assert doubled() == 2; assert doubled() == 4; assert doubled() == 6; var trebled = zipWith(naturals(), naturals(), naturals(), (int a, int b, int c) => a + b + c); assert trebled() == 3; assert trebled() == 6; assert trebled() == 9; } // void main(String[] args) // { // try // { // assert 1 > 2; // throw new Exception("Assertions not enabled."); // } // catch(AssertionFailed e) { // //assertions enabled. // } // _testIterators(); // _testGenerators(); // println("Tests passed."); // } |