All Downloads are FREE. Search and download functionalities are using the official Maven repository.

me.shaftesbury.utils.functional.Functional Maven / Gradle / Ivy

There is a newer version: 1.17
Show newest version
/**
 * Created with IntelliJ IDEA.
 * User: Bob
 * Date: 16/10/13
 * Time: 20:04
 * To change this template use File | Settings | File Templates.
 */
/**
 * Created with IntelliJ IDEA.
 * User: Bob
 * Date: 16/10/13
 * Time: 20:04
 * To change this template use File | Settings | File Templates.
 */
package me.shaftesbury.utils.functional;

import org.javatuples.Pair;
import org.javatuples.Triplet;

import java.util.*;

public final class Functional
{
    private Functional() {}

    public final static boolean isNullOrEmpty(final String s)
    {
        return s==null || s.isEmpty();
    }

    public final static String join(final String delimiter, final Iterable strs)
    {
        if(strs==null) return "";
        final Iterator it = strs.iterator();
        final StringBuilder sb = new StringBuilder();
        boolean isFirst = true;
        while(it.hasNext())
        {
            if(!isFirst) sb.append(delimiter);
            sb.append(it.next());
            isFirst=false;
        }
        return sb.toString();
    }

    public final static String indentBy(final int howMany, final String unitOfIndentation, final String indentThis)
    {
        final Collection indentation = init(
                new Func() {
                    @Override
                    public String apply(Integer integer) {
                        return unitOfIndentation;
                    }
                }, howMany);
        return fold(new Func2() {
            @Override
            public String apply(String state, String str) {
                return str + state;
            }
        }, indentThis, indentation);
    }

    public final static Pair> foldAndChoose(
            final me.shaftesbury.utils.functional.Func2>> f,
            final A initialValue, final Iterable input) throws OptionNoValueAccessException
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        A state = initialValue;
        final List results = new ArrayList();
        for (final B b : input)
        {
            final Pair> intermediate = f.apply(state, b);
            state = intermediate.getValue0();
            if (!intermediate.getValue1().isNone())
                results.add(intermediate.getValue1().Some());
        }
        return new Pair>(state, new UnmodifiableList(results));
    }

    public static final List toList(final Enumeration input)
    {
        final List output = new ArrayList();
        while(input.hasMoreElements())
            output.add(input.nextElement());
        return new UnmodifiableList(output);
    }

    /// 
    /// Analogue of string.Join for List<T> with the addition of a user-defined map function
    /// 
    /// 
    /// 
    /// 
    /// 
    /// 
    public final static String join(final String separator, final Iterable l, final Func fn)
    {
        if (l == null) throw new IllegalArgumentException("l");
        if (fn == null) throw new IllegalArgumentException("fn");

        return join(separator, map(fn,l));
    }

    /// return lowerBound < val < upperBound
    public final static >boolean between(final T lowerBound, final T upperBound, final T val)
    {
        if (val == null) throw new IllegalArgumentException("val");

        return val.compareTo(lowerBound) == 1 && val.compareTo(upperBound) == -1;
    }

    ///  find: (A -> bool) -> A list -> A
    public final static A find(Func f, Iterable input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        for(final A a : input)
            if(f.apply((a)))
                return a;
        throw new KeyNotFoundException();
    }

    public final static Func,A> find(final Func f)
    {
        return new Func, A>() {
            @Override
            public A apply(Iterable input) {
                return Functional.find(f,input);
            }
        };
    }

    ///  findIndex: (A -> bool) -> A list -> int
    public static int findIndex(Func f, Iterable input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        int pos = 0;
        for (final A a : input)
            if (f.apply(a))
                return pos;
            else pos++;
        throw new IllegalArgumentException();
    }

    ///  findLast: (A -> bool) -> A list -> A
    public final static A findLast(final Func f, final Iterable input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        final Pair,Iterable> p = takeNAndYield(input,1);
        final Pair seed = Pair.with(p.getValue0().get(0),f.apply(p.getValue0().get(0)));
        final Pair result = fold(new Func2,A,Pair>(){
            @Override public Pair apply(final Pair state, final A item){return f.apply(item)?Pair.with(item,true):state;}
        },seed,p.getValue1());

        if(result.getValue1()) return result.getValue0();
        throw new KeyNotFoundException();
    }

    ///  findLast: (A -> bool) -> A list -> A
    public final static A findLast(final Func f, final List input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        for (final A a : Enumerators.ReverseEnum(input))
            if (f.apply(a))
                return a;
        throw new KeyNotFoundException();
    }

    public final static Func,A> findLast(final Func f)
    {
        return new Func, A>() {
            @Override
            public A apply(List input) {
                return Functional.findLast(f,input);
            }
        };
    }

    ///  pick: (A -> B option) -> A list -> B
    public static B pick(final Func> f, final Iterable input)
    {
        if (f == null) throw new IllegalArgumentException("f");
        if (input == null) throw new IllegalArgumentException("input");

        for(final A a : input)
        {
            final Option intermediate = f.apply(a); // which is, effectively, if(f(a)) return f(a), but without evaluating f twice
            if (!intermediate.isNone())
                return intermediate.Some();
        }
        throw new KeyNotFoundException();
    }

    public static Func,B> pick(final Func> f)
    {
        return new Func, B>() {
            @Override
            public B apply(final Iterable input) {
                return Functional.pick(f,input);
            }
        };
    }

    public final static  B In( final A input, final Func f)
    {
        return f.apply(input);
    }

    public final static  Func Then(final Func f, final Func g)
    {
        return new Func()
        {
            @Override
            public C apply(A x)
            {
                return g.apply(f.apply(x));
            }
        };
    }

    public final static Func Identity()
    {
        return new Func() {
            @Override
            public T apply(T t) {
                return t;
            }
        };
    }

    public static final Func IsEven = new Func()
    {
        @Override
        public Boolean apply(Integer i)
        {
            return i % 2 == 0;
        }
    };
    public static final Func IsOdd = new Func()
    {
        @Override
        public Boolean apply(Integer i)
        {
            return i % 2 != 0;
        }
    };
    public static final me.shaftesbury.utils.functional.Func2 Count =
            new Func2() {
                @Override
                public Integer apply(Integer state, Integer b) {
                    return state + 1;
                }
            };

    ///  init: int -> (int -> A) -> A list
    public final static List init(final Func f,final int howMany)
    {
        //if (f == null) throw new ArgumentNullException("f");

        final List output = new ArrayList();
        for(int i=0; i map: (A -> B) -> A list -> B list
    public final static  List map(final Func f, final Iterable input)
    {
        final List output = new ArrayList();
        for(final A a : input)
            output.add(f.apply(a));
        return new UnmodifiableList(output);
    }

    public final static  Func,List> map(final Func f)
    {
        return new Func, List>() {
            @Override
            public List apply(Iterable input) {
                return Functional.map(f,input);
            }
        };
    }

    ///  sortWith: (A -> A -> int) -> A list -> A list
    public final static List sortWith(final Comparator f, final List input)
    {
        final List output = new ArrayList(input);
        Collections.sort(output, f);
        return new UnmodifiableList(output);
    }

    public final static >int Sorter(final A left, final A right)
    {
        return left.compareTo(right);
    }
    public final static Comparator dSorter = new Comparator()
    {
        @Override public int compare(Integer i, Integer j) { return Sorter(i, j); }
    };

    public final static  String Stringify(final T a) { return a.toString(); }
    public final static Func dStringify()
    {
        return new Func()
        {
            @Override public String apply(T i) { return Stringify(i); }
        };
    }

    ///  forAll2: (A -> B -> bool) -> A list -> B list -> bool
    public final static boolean forAll2(final me.shaftesbury.utils.functional.Func2 f, final Iterable input1, final Iterable input2)
    {
        final Iterator enum1 = input1.iterator();
        final Iterator enum2 = input2.iterator();
        boolean enum1Moved = false, enum2Moved = false;
        do
        {
            enum1Moved = enum1.hasNext();
            enum2Moved = enum2.hasNext();
            if (enum1Moved && enum2Moved && !f.apply(enum1.next(), enum2.next()))
                return false;
        } while (enum1Moved && enum2Moved);
        if( enum1Moved != enum2Moved)
            throw new IllegalArgumentException();
        return true;
    }

    public final static List filter(final Func pred, final Iterable input)
    {
        final List output = new ArrayList();
        for(final A element : input)
        {
            if(pred.apply(element))
                output.add(element);
        }
        return new UnmodifiableList(output);
    }

    public static final Func,List> filter(final Func f)
    {
        return new Func, List>() {
            @Override
            public List apply(final Iterable input) {
                return Functional.filter(f,input);
            }
        };
    }

    ///  exists: (A -> bool) -> A list -> bool
    public final static boolean exists(final Func f, final Iterable input)
    {
        for(final A a : input)
            if(f.apply(a))
                return true;
        return false;
    }

    public final static Func,Boolean> exists(final Func f)
    {
        return new Func, Boolean>() {
            @Override
            public Boolean apply(Iterable input) {
                return Functional.exists(f,input);
            }
        };
    }

    ///  not: (A -> bool) -> (A -> bool)
    public final static Func not(final Func f)
    {
        return new Func(){@Override public Boolean apply(A a) { return !f.apply(a);}};
    }

    ///  forAll: (A -> bool) -> A list -> bool
    public final static boolean forAll(final Func f, final Iterable input)
    {
        return !exists(not(f), input);
    }

    public final static Func,Boolean> forAll(final Func f)
    {
        return new Func, Boolean>() {
            @Override
            public Boolean apply(Iterable input) {
                return Functional.forAll(f,input);
            }
        };
    }

    ///  not2: (A -> B -> bool) -> (A -> B -> bool)
    public final static  me.shaftesbury.utils.functional.Func2 not2(final me.shaftesbury.utils.functional.Func2 f)
    {
        return new Func2(){@Override public Boolean apply(A a, B b) { return !f.apply(a,b);}};
    }

    ///  partition: (A -> bool) -> A list -> A list * A list
    ///  (list * list). The first list contains all items for which f(a) is true. The second list contains the remainder.
    public final static org.javatuples.Pair,List> partition(final Func f, final Iterable input)
    {
        final List left = new ArrayList();
        final List right = new ArrayList();
        for (final A a : input)
            if (f.apply(a))
                left.add(a);
            else
                right.add(a);
        return new org.javatuples.Pair,List>(new UnmodifiableList(left), new UnmodifiableList(right));
    }

    public final static Func,org.javatuples.Pair,List>> partition(final Func f)
    {
        return new Func, Pair, List>>() {
            @Override
            public Pair, List> apply(Iterable input) {
                return Functional.partition(f,input);
            }
        };
    }

    ///  choose: (A -> B option) -> A list -> B list
    public final static List choose(final Func> f, final Iterable input)
    {
        final List results = new ArrayList();
        for(final A a : input)
        {
            final Option intermediate = f.apply(a);
            if (!intermediate.isNone())
                results.add(intermediate.Some());
        }
        return new UnmodifiableList(results);
    }

    public final static Func,List> choose(final Func> f)
    {
        return new Func, List>() {
            @Override
            public List apply(Iterable input) {
                return Functional.choose(f,input);
            }
        };
    }


    ///  fold: (A -> B -> A) -> A -> B list -> A
    public final static A fold(final me.shaftesbury.utils.functional.Func2 f, final A initialValue, final Iterable input)
    {
        A state = initialValue;
        for (final B a : input)
            state = f.apply(state, a);
        return state;
    }

    public final static Func,A> fold(final me.shaftesbury.utils.functional.Func2 f, final A initialValue)
    {
        return new Func, A>() {
            @Override
            public A apply(Iterable input) {
                return Functional.fold(f,initialValue,input);
            }
        };
    }

    public final static Map toDictionary(final Func keyFn, final Func valueFn, Iterable input)
    {
        if(keyFn==null) throw new IllegalArgumentException("keyFn");
        if(valueFn==null) throw new IllegalArgumentException("valueFn");

        final Map output = new HashMap();
        for(final T element : input) output.put(keyFn.apply(element),valueFn.apply(element));
        return new UnmodifiableMap(output);
    }

    //public final static T[] toArray(final Iterable input)
    public final static Object[] toArray(final Iterable input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.toArray(Iterable): input is null");

        final List output = new ArrayList();
        for(final T element: input) output.add(element);

        return output.toArray(); // this needs to be output.toArray(new T[0]) but that doesn't appear to be allowable Java :-(
    }

    public static final List toList(Iterable input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.toList(Iterable): input is null");

        if(input instanceof List) return (List)input;

        final List output = new ArrayList();
        for(final T element: input) output.add(element);

        return output;
    }

    public static final T last(final Iterable input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.last(Iterable): input is null");

        T state = null;
        for(final T element: input) state = element;

        return state;
    }

    public static final T last(final T[] input)
    {
        if(input==null||input.length==0) throw new IllegalArgumentException("Functional.last(Iterable): input is null or empty");

        return input[input.length-1];
    }

    public static final List concat(final List list1, final List list2)
    {
        if(list1==null) throw new IllegalArgumentException("Functional.concat(List,List): list1 is null");
        if(list2==null) throw new IllegalArgumentException("Functional.concat(List,List): list2 is null");

        if(list1.size()==0) return new UnmodifiableList(list2);
        if(list2.size()==0) return new UnmodifiableList(list1);

        final List newList = new ArrayList(list1);
        final boolean didItChange = newList.addAll(list2);
        return new UnmodifiableList(newList);
    }

    public static finalList take(final int howMany, final Iterable list)
    {
        if(howMany<0) throw new IllegalArgumentException("Functional.take(int,Iterable): howMany is negative");
        if(list==null) throw new IllegalArgumentException("Functional.take(int,Iterable): list is null");

        if(howMany==0) return new ArrayList(0);

        final List output = new ArrayList(howMany);
        final Iterator iterator = list.iterator();
        for(int i=0;iFunc,List> take(final int howMany)
    {
        return new Func, List>() {
            @Override
            public List apply(Iterable input) {
                return Functional.take(howMany,input);
            }
        };
    }

    public static final Func Constant(final T constant)
    {
        return new Func() {
            @Override
            public T apply(Integer integer) {
                return constant;
            }
        };
    }

    public static final List> zip(final Iterable l1, final Iterable l2)
    {
        if(l1==null) throw new IllegalArgumentException("Functional.zip(Iterable,Iterable): l1 is null");
        if(l2==null) throw new IllegalArgumentException("Functional.zip(Iterable,Iterable): l2 is null");

        final List> output = new ArrayList>();
        final Iterator l1_it = l1.iterator();
        final Iterator l2_it = l2.iterator();

        while(l1_it.hasNext() && l2_it.hasNext()) output.add(new org.javatuples.Pair(l1_it.next(),l2_it.next()));
        if(l1_it.hasNext() || l2_it.hasNext()) throw new IllegalArgumentException("Functional.zip(Iterable,Iterable): l1 and l2 have differing numbers of elements");

        return new UnmodifiableList(output);
    }

    public static final List> zip3(final Iterable l1, final Iterable l2, final Iterable l3)
    {
        if(l1==null) throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l1 is null");
        if(l2==null) throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l2 is null");
        if(l3==null) throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l3 is null");

        final List> output = new ArrayList>();
        final Iterator l1_it = l1.iterator();
        final Iterator l2_it = l2.iterator();
        final Iterator l3_it = l3.iterator();

        while(l1_it.hasNext() && l2_it.hasNext() && l3_it.hasNext()) output.add(new org.javatuples.Triplet(l1_it.next(),l2_it.next(),l3_it.next()));
        if(l1_it.hasNext() || l2_it.hasNext() || l3_it.hasNext())
            throw new IllegalArgumentException("Functional.zip3(Iterable,Iterable,Iterable): l1, l2 and l3 have differing numbers of elements");

        return new UnmodifiableList(output);
    }

    public static final org.javatuples.Pair,List> unzip(final Iterable> input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.unzip(Iterable>): input is null");

        final List l1 = new ArrayList();
        final List l2 = new ArrayList();

        for(org.javatuples.Pair pair:input)
        {
            l1.add(pair.getValue0());
            l2.add(pair.getValue1());
        }

        return new org.javatuples.Pair(new UnmodifiableList(l1),new UnmodifiableList(l2));
    }

    public static final Triplet,List,List> unzip3(final Iterable> input)
    {
        if(input==null) throw new IllegalArgumentException("Functional.unzip(Iterable>): input is null");

        final List l1 = new ArrayList();
        final List l2 = new ArrayList();
        final List l3 = new ArrayList();

        for(org.javatuples.Triplet triplet:input)
        {
            l1.add(triplet.getValue0());
            l2.add(triplet.getValue1());
            l3.add(triplet.getValue2());
        }

        return new org.javatuples.Triplet(new UnmodifiableList(l1),new UnmodifiableList(l2),new UnmodifiableList(l3));
    }

    public static final List collect(final Func> f, final Iterable input)
    {
        List output = new ArrayList();
        for(final T element : input)
            output = Functional.concat(output, Functional.toList(f.apply(element)));
        return new UnmodifiableList(output);
    }

    public static final Func,List> collect(final Func> f)
    {
        return new Func, List>() {
            @Override
            public List apply(Iterable input) {
                return Functional.collect(f,input);
            }
        };
    }

    public static final Pair,Iterable> takeNAndYield(final Iterable input, final int howMany)
    {
        if (input == null) throw new IllegalArgumentException("Functional.takeNAndYield: input is null");

        int counter = 0;
        final List output = new ArrayList();
        final Iterator position = input.iterator();
        if(howMany>0&&position.hasNext())
        {
            while(counter) new Iterable() {
                @Override
                public Iterator iterator() {
                    return position;
                }
            });
        }
        return Pair.with(output, input);
    }

    public static final class seq
    {
        public static final Iterable map(final Func f, final Iterable input)
        {
            if(f==null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            return new Iterable() {
                @Override
                public final Iterator iterator() {
                    return new Iterator() {
                        private final Iterator _input=input.iterator();
                        private final Func _f = f;
                        @Override
                        public final boolean hasNext() {
                            return _input.hasNext();
                        }

                        @Override
                        public final U next() {
                            return _f.apply(_input.next());
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.map(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        public static final Func,Iterable> map(final Func f)
        {
            return new Func, Iterable>() {
                @Override
                public Iterable apply(final Iterable input) {
                    return Functional.seq.map(f, input);
                }
            };
        }

        public static final Iterable concat(final Iterable list1, final Iterable list2)
        {
            if(list1==null) throw new IllegalArgumentException("Functional.seq.concat(Iterable,Iterable): list1 is null");
            if(list2==null) throw new IllegalArgumentException("Functional.seq.concat(Iterable,Iterable): list2 is null");

            return new Iterable()
            {
                public Iterator iterator()
                {
                    return new Iterator() {
                        private final Iterator _s1 = list1.iterator();
                        private final Iterator _s2 = list2.iterator();
                        @Override
                        public boolean hasNext() {
                            return _s1.hasNext() || _s2.hasNext();
                        }

                        @Override
                        public T next() {
                            return _s1.hasNext() ? _s1.next() : _s2.next();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.concat(Iterable,Iterable): remove is not supported");
                        }
                    };
                }
            };
        }

        public static final Iterable filter(final Func f, final Iterable input) //throws NoSuchElementException, IllegalArgumentException, UnsupportedOperationException
        {
            if(f==null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            return new Iterable() {
                @Override
                public final Iterator iterator() {
                    return new Iterator() {
                        private final Iterator _input=input.iterator();
                        private final Func _f = f;
                        private T _next = null;
                        @Override
                        public final boolean hasNext() {
                            while(_next==null && // ie we haven't already read the next element
                                _input.hasNext())
                            {
                                final T next = _input.next();
                                if(_f.apply(next))
                                {
                                    _next=next;
                                    return true;
                                }
                            }
                            return _next!=null;
                        }

                        @Override
                        public final T next() {
                            if(hasNext())
                            {
                                final T next = _next;
                                _next=null;
                                return next;
                            }
                            throw new NoSuchElementException();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.filter(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        public static final Func,Iterable> filter(final Func f)
        {
            return new Func,Iterable>(){
                @Override
                public Iterable apply(Iterable input) {
                    return Functional.seq.filter(f,input);
                }
            };
        }

        public static final Iterable choose(final Func> f, final Iterable input)
        {
            if(f==null) throw new IllegalArgumentException("f");
            if (input == null) throw new IllegalArgumentException("input");

            return new Iterable() {
                @Override
                public final Iterator iterator() {
                    return new Iterator() {
                        private final Iterator _input=input.iterator();
                        private final Func> _f = f;
                        private Option _next = Option.None();
                        @Override
                        public final boolean hasNext() {
                            while(_next.isNone() && // ie we haven't already read the next element
                                    _input.hasNext())
                            {
                                final Option next = _f.apply(_input.next());
                                if(next.isSome())
                                {
                                    _next=next;
                                    return true;
                                }
                            }
                            return _next.isSome();
                        }

                        @Override
                        public final U next()
                        {
                            if(hasNext())
                            {
                                final Option next = _next;
                                _next=Option.None();
                                try {
                                    return next.Some();
                                } catch(OptionNoValueAccessException e) { throw new NoSuchElementException(); }
                            }
                            throw new NoSuchElementException();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.choose(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        public static final Func,Iterable> choose(final Func> f)
        {
            return new Func, Iterable>() {
                @Override
                public Iterable apply(Iterable input) {
                    return Functional.seq.choose(f,input);
                }
            };
        }

        ///  init: int -> (int -> A) -> A list
        public final static Iterable init(final Func f,final int howMany)
        {
            if(f==null) throw new IllegalArgumentException("f");
            if(howMany<0) throw new IllegalArgumentException("howMany");

            return new Iterable()
            {
                @Override
                public Iterator iterator() {
                    return new Iterator()
                    {
                        private int _counter=0;
                        private final Func _f = f;
                        @Override
                        public boolean hasNext() {
                            return _counter,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        ///  init: int -> (int -> A) -> A list
        public final static Iterable init(final Func f)
        {
            if(f==null) throw new IllegalArgumentException("f");

            return new Iterable()
            {
                @Override
                public Iterator iterator() {
                    return new Iterator()
                    {
                        private int _counter=0;
                        private final Func _f = f;
                        @Override
                        public boolean hasNext() {
                            return true;
                        }

                        @Override
                        public T next() {
                            return _f.apply(_counter++);
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.init(Func,Iterable): Removing elements is strictly prohibited");
                        }
                    };
                }
            };
        }

        public static final Iterable collect(final Func> f, final Iterable input)
        {
            if(f==null) throw new IllegalArgumentException("Functional.seq.collect: f is null");
            if(input==null) throw new IllegalArgumentException("Functional.seq.collect: input is null");

            return new Iterable(){

                @Override
                public Iterator iterator() {
                    return new Iterator(){
                        private final Iterator it = input.iterator();
                        private List cache = new ArrayList();
                        private Iterator cacheIterator = cache.iterator();
                        @Override
                        public boolean hasNext() {
                            return it.hasNext() || cacheIterator.hasNext();
                        }

                        @Override
                        public U next() {
                            if(cacheIterator.hasNext()) return cacheIterator.next();
                            cache = toList(f.apply(it.next()));
                            cacheIterator=cache.iterator();
                            return cacheIterator.next();
                        }

                        @Override
                        public void remove() {
                            throw new UnsupportedOperationException("Functional.seq.collect: remove is not supported");
                        }
                    };
                }
            };
        }

        public static final Func,Iterable> collect(final Func> f)
        {
            return new Func, Iterable>() {
                @Override
                public Iterable apply(Iterable input) {
                    return Functional.seq.collect(f,input);
                }
            };
        }
    }


    // Recursive implementations of the functions
    public final static class rec
    {
        private static final Iterable filter(Func f, Iterator input, List accumulator)
        {
            if(input.hasNext())
            {
                A next = input.next();
                if(f.apply(next)) accumulator.add(next);
                return filter(f,input,accumulator);
            }
            else return accumulator;
        }
        public static final Iterable filter(Func f, Iterable input)
        {
            return filter(f,input.iterator(),new ArrayList());
        }

        private static final Iterable map(Func f, Iterator input, List accumulator)
        {
            if(input.hasNext())
            {
                accumulator.add(f.apply(input.next()));
                return map(f,input,accumulator);
            }
            else return accumulator;
        }
        public static final Iterable map(Func f, Iterable input)
        {
            return map(f,input.iterator(),new ArrayList());
        }

        private final static A fold(final Func2 f, final A initialValue, final Iterator input)
        {
            if(input.hasNext())
            {
                B next = input.next();
                return fold(f,f.apply(initialValue,next),input);
            }
            else return initialValue;
        }
        ///  fold: (A -> B -> A) -> A -> B list -> A
        public final static A fold(final me.shaftesbury.utils.functional.Func2 f, final A initialValue, final Iterable input)
        {
            return fold(f,initialValue,input.iterator());
        }
    }
        /*
        // Following are functions for non-list collections
        */

    public static final Map map_dict(Func> f, Iterable input)
    {
        final Map results = new HashMap();
        for (final A a : input)
        {
            final Map.Entry intermediate = f.apply(a);
            results.put(intermediate.getKey(), intermediate.getValue());
        }
        return results;
    }
}