'Factory method to mass generate Templates from a list L of lists of  Features.
#With combinations=(k1, k2), the function will in all possible ways choose k1 ... k2
#of the sublists in L; it will output all Templates formed by the Cartesian product
#of this selection, with duplicates and other semantically equivalent
#forms removed. Default for combinations is (1, len(L)).
The feature lists may have been specified
manually, or generated from Feature.expand(). For instance,
>>> from nltk.tbl.template import Template
>>> from nltk.tag.brill import Word, Pos
#creating some features
>>> (wd_0, wd_01) = (Word([0]), Word([0,1]))
>>> (pos_m2, pos_m33) = (Pos([-2]), Pos([3-2,-1,0,1,2,3]))
>>> list(Template.expand([[wd_0], [pos_m2]]))
[Template(Word([0])), Template(Pos([-2])), Template(Pos([-2]),Word([0]))]
>>> list(Template.expand([[wd_0, wd_01], [pos_m2]]))
[Template(Word([0])), Template(Word([0, 1])), Template(Pos([-2])), Template(Pos([-2]),Word([0])), Template(Pos([-2]),Word([0, 1]))]
#note: with Feature.expand(), it is very easy to generate more templates
#than your system can handle -- for instance,
>>> wordtpls = Word.expand([-2,-1,0,1], [1,2], excludezero=False)
>>> len(wordtpls)
7
>>> postpls = Pos.expand([-3,-2,-1,0,1,2], [1,2,3], excludezero=True)
>>> len(postpls)
9
#and now the Cartesian product of all non-empty combinations of two wordtpls and
#two postpls, with semantic equivalents removed
>>> templates = list(Template.expand([wordtpls, wordtpls, postpls, postpls]))
>>> len(templates)
713
will return a list of eight templates
Template(Word([0])),
Template(Word([0, 1])),
Template(Pos([-2])),
Template(Pos([-1])),
Template(Pos([-2]),Word([0])),
Template(Pos([-1]),Word([0])),
Template(Pos([-2]),Word([0, 1])),
Template(Pos([-1]),Word([0, 1]))]
#Templates where one feature is a subset of another, such as
#Template(Word([0,1]), Word([1]), will not appear in the output.
#By default, this non-subset constraint is tightened to disjointness:
#Templates of type Template(Word([0,1]), Word([1,2]) will also be filtered out.
#With skipintersecting=False, then such Templates are allowed
WARNING: this method makes it very easy to fill all your memory when training
generated templates on any real-world corpus
:param featurelists: lists of Features, whose Cartesian product will return a set of Templates
:type featurelists: list of (list of Features)
:param combinations: given n featurelists: if combinations=k, all generated Templates will have
k features; if combinations=(k1,k2) they will have k1..k2 features; if None, defaults to 1..n
:type combinations: None, int, or (int, int)
:param skipintersecting: if True, do not output intersecting Templates (non-disjoint positions for some feature)
:type skipintersecting: bool
:returns: generator of Templates'

@classmethod
def expand(cls, featurelists, combinations=None, skipintersecting=True):

    def nonempty_powerset(xs):
        k = combinations
        combrange = ((1, (len(xs) + 1)) if (k is None) else ((k, (k + 1)) if isinstance(k, int) else (k[0], (k[1] + 1))))
        return it.chain.from_iterable((it.combinations(xs, r) for r in range(*combrange)))
    seentemplates = set()
    for picks in nonempty_powerset(featurelists):
        for pick in it.product(*picks):
            if any((((i != j) and x.issuperset(y)) for (i, x) in enumerate(pick) for (j, y) in enumerate(pick))):
                continue
            if (skipintersecting and any((((i != j) and x.intersects(y)) for (i, x) in enumerate(pick) for (j, y) in enumerate(pick)))):
                continue
            thistemplate = cls(*sorted(pick))
            strpick = str(thistemplate)
            if (strpick in seentemplates):
                cls._poptemplate()
                continue
            seentemplates.add(strpick)
            (yield thistemplate)
