In [1]:
from itertools import accumulate
import itertools
import operator
from typing import Iterable, TypeVar, Tuple

In [2]:
T = TypeVar('T')

In [3]:
def accumulate_attempt1(iterable: Iterable[T]) -> Iterable[T]:
    out = []
    for item in iter(iterable):
        if len(out) > 0:
            val = out[-1] + item
            yield val
            out.append(val)
        else:
            yield item
            out.append(item)

In [4]:
test1 = [1, 2, 3, 4, 5]

In [5]:
list(accumulate_attempt1(test1))


Out[5]:
[1, 3, 6, 10, 15]

In [6]:
list(accumulate_attempt1([]))


Out[6]:
[]

In [7]:
list(accumulate_attempt1([1, 2]))


Out[7]:
[1, 3]

In [8]:
def accumulate_attempt2(iterable: Iterable[T], fn=operator.add) -> Iterable[T]:
    it = iter(iterable)
    
    try:
        total = next(it)
    except:
        return
    yield total
    for element in it:
        total = fn(total, element)
        yield total

In [9]:
list(accumulate_attempt2([1, 2, 3, 4], operator.mul))


Out[9]:
[1, 2, 6, 24]

In [10]:
q = list(itertools.tee([1,2,3,4,5,6], 2))

In [11]:
list(q[1])


Out[11]:
[1, 2, 3, 4, 5, 6]

In [12]:
x = [1, 1, 2,2,2, 1, 3, 1]

In [13]:
list(itertools.permutations([1, 2, 3, 4], r=None))


Out[13]:
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]

In [14]:
def product_attempt1(iterable1: Iterable[T],
                     iterable2: Iterable[T]) -> Iterable[Tuple[T, T]]:
    for e1 in iterable1:
        for e2 in iterable2:
            yield (e1, e2)

In [16]:
A = [1, 2]

In [17]:
list(product_attempt1(A, A))


Out[17]:
[(1, 1), (1, 2), (2, 1), (2, 2)]

In [18]:
def product_attempt2(*iters: Iterable[T]) -> Iterable[Tuple[T, ...]]:
    pools = (tuple(pool) for pool in iters)
    result = [[]]
    
    for pool in pools:
        result = [
            r + [p]
            for r in result
            for p in pool
        ]
        
    for r in result:
        yield tuple(r)

In [19]:
list(product_attempt2(A, [3, 4]))


Out[19]:
[(1, 3), (1, 4), (2, 3), (2, 4)]

In [20]:
list(itertools.permutations([1,2,3]))


Out[20]:
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

In [21]:
def perm(xs):
    out = []
    
    def _perm(head, rest):
        if len(rest) == 1:
            out.append(tuple(head + rest))
        else:
            for i in range(len(rest)):
                _perm(head + [rest[i]],
                      [rest[j] for j in range(len(rest)) if j != i])
    
    _perm([], xs)
    return out

In [22]:
perm([1,2,3])


Out[22]:
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

In [23]:
perm([1])


Out[23]:
[(1,)]

In [24]:
perm([1,2])


Out[24]:
[(1, 2), (2, 1)]

In [25]:
list(itertools.combinations([1,2,3], r=2))


Out[25]:
[(1, 2), (1, 3), (2, 3)]

In [26]:
def comb2(xs):
    out = set()
    for x in xs:
        for y in xs[1:]:
            if x != y and (x, y) not in out and (y, x) not in out:
                out.add((x, y))
    return out

In [27]:
comb2([1,2,3])


Out[27]:
{(1, 2), (1, 3), (2, 3)}

In [28]:
list(itertools.combinations([1,2,3,4], r=3))


Out[28]:
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]

In [29]:
[1,2,3][1:]


Out[29]:
[2, 3]

In [30]:
def comb(xs, r):
    out = []
    
    def _comb(head, rest):
        if len(head) == r - 1:
            for x in rest:
                out.append(tuple(head + [x]))
        else:
            for i, x in enumerate(rest):
                _comb(head + [x], rest[i + 1:])

    _comb([], xs)
    return out

In [31]:
comb([1,2,3,4], r=3)


Out[31]:
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]

In [32]:
comb([1,2,3,4,5], r=2)


Out[32]:
[(1, 2),
 (1, 3),
 (1, 4),
 (1, 5),
 (2, 3),
 (2, 4),
 (2, 5),
 (3, 4),
 (3, 5),
 (4, 5)]