Return a list of n-tuples, one for each possible sequence of n items in the token_list

    Arguments:
      join (bool or str): if str, then join ngrom tuples on it before returning
         True is equivalent to join=' '
         default = True

    >>> list_ngram_range('goodbye cruel world'.split(), 0, 2, join=False)
    [('goodbye',), ('cruel',), ('world',), ('goodbye', 'cruel'), ('cruel', 'world')]
    >>> list_ngram_range('goodbye cruel world'.split(), 2, join=False)
    [('goodbye',), ('cruel',), ('world',), ('goodbye', 'cruel'), ('cruel', 'world')]
    >>> list_ngram_range('goodbye cruel world'.split(), 0, 2, join='|')
    ['goodbye', 'cruel', 'world', 'goodbye|cruel', 'cruel|world']
    >>> list_ngram_range('goodbye cruel world'.split(), 0, 2, join=True)
    ['goodbye', 'cruel', 'world', 'goodbye cruel', 'cruel world']

def list_ngram_range(token_list, *args, **kwargs):
    """Return a list of n-tuples, one for each possible sequence of n items in the token_list

    Arguments:
      join (bool or str): if str, then join ngrom tuples on it before returning
         True is equivalent to join=' '
         default = True

    >>> list_ngram_range('goodbye cruel world'.split(), 0, 2, join=False)
    [('goodbye',), ('cruel',), ('world',), ('goodbye', 'cruel'), ('cruel', 'world')]
    >>> list_ngram_range('goodbye cruel world'.split(), 2, join=False)
    [('goodbye',), ('cruel',), ('world',), ('goodbye', 'cruel'), ('cruel', 'world')]
    >>> list_ngram_range('goodbye cruel world'.split(), 0, 2, join='|')
    ['goodbye', 'cruel', 'world', 'goodbye|cruel', 'cruel|world']
    >>> list_ngram_range('goodbye cruel world'.split(), 0, 2, join=True)
    ['goodbye', 'cruel', 'world', 'goodbye cruel', 'cruel world']
    """
    m, n = (args if len(args) > 1 else ((0, args[0]) if args else (0, 1)))
    join = args[2] if len(args) > 2 else kwargs.pop('join', True)
    return list(chain(*(list_ngrams(token_list, i + 1, join=join) for i in range(0, n))))