#!/usr/bin/env python
# coding: utf-8

# ## 36. 이터레이터나 제너레이터를 다룰 때는 itertools를 사용하라

# In[1]:


import itertools


# ## 여러 이터레이터 연결하기

# In[2]:


# chain

it = itertools.chain([1, 2, 3], [4, 5, 6])
print(list(it))


# In[3]:


# repeat

it = itertools.repeat('안녕', 3)
print(list(it))


# In[4]:


# cycle

it = itertools.cycle([1, 2])
result = [next(it) for _ in range(10)]
print(result)


# In[6]:


# tee

it1, it2, it3 = it = itertools.tee(['하나', '둘'], 3)
print(list(it1))
print(list(it2))
print(list(it3))


# In[7]:


# zip_longest

keys = ['하나', '둘', '셋']
values = [1, 2]

normal = list(zip(keys, values))
print('zip:', normal)

it = it = itertools.zip_longest(keys, values, fillvalue='없음')
longest = list(it)
print('zip_longest:', longest)


# ## 이터레이터에서 원소 거르기

# In[8]:


# isslice

values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

first_five = itertools.islice(values, 5)
print('앞에서 다섯개:', list(first_five))

middle_odds = itertools.islice(values, 2, 8, 2)
print('중간의 홀수들', list(middle_odds))


# In[9]:


# takewhile

values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
less_then_seven = lambda x: x < 7
it = it = itertools.takewhile(less_then_seven, values)
print(list(it))


# In[10]:


# dropwhile

values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
less_then_seven = lambda x: x < 7
it = itertools.dropwhile(less_then_seven, values)
print(list(it))


# In[11]:


# filterfalse

values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = lambda x: x % 2 == 0
filter_result = filter(evens, values)
print('filter:', list(filter_result))

filter_false_result = itertools.filterfalse(evens, values)
print('filter false:', list(filter_false_result))


# ## 이터레이터에서 원소의 조합 만들어내기

# In[12]:


# accumulate

values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sum_reduce = itertools.accumulate(values)
print('합계:', list(sum_reduce))

def sum_modulo_20(first, second):
    output = first + second
    return output % 20

modulo_reduce = itertools.accumulate(values, sum_modulo_20)
print('20으로 나눈 나머지의 합계:', list(modulo_reduce))


# In[13]:


# product

single = itertools.product([1, 2], repeat=2)
print('리스트 한 개:', list(single))

multiple = itertools.product([1, 2], ['a', 'b'])
print('리스트 두 개:', list(multiple))


# In[14]:


# permutations

it = itertools.permutations([1, 2, 3, 4], 2)
print(list(it))


# In[16]:


# combinations

it = itertools.combinations([1, 2, 3, 4], 2)
print(list(it))


# In[17]:


# combinations_with_replacement

it = itertools.combinations_with_replacement([1, 2, 3, 4], 2)
print(list(it))


# ## 기억해야 할 내용
# - 이터레이터나 제너레이터를 다루는 itertools 함수는 세 가지 범주로 나눌 수 있다.
#     - 여러 이터레이터를 연결함
#     - 이터레이터의 원소를 걸러냄
#     - 원소의 조합을 만들어냄
# - 파이썬 인터프리터에서 help(itertools)를 입력한 후 표시되는 문서를 살펴보면 더 많은 고급 함수와 추가 파라미터를 알 수 있으며, 이를 사용하는 유용한 방법도 확인할 수 있다.