본문 바로가기

카테고리 없음

함수형 패러다임( recursive / comprehension / iterator / generator)

참고 paper

- 라이브러리 :  함수형 프로그래밍 모듈  https://docs.python.org/ko/3/library/index.html

       - itertools : 효율적인 루핑을 위한 이터레이터를 만드는 함수

       - functools : 고차 함수와 콜러블 객체에 대한 연산

       - operator : 함수로서의 표준 연산자

- 파이썬  HOWTO

       - 함수형 프로그래밍 HOWTO    https://docs.python.org/ko/3/howto/functional.html

- 수업 자료 : Functional Programming in Python

 

 

first class function = function object로 사용

higher order function -> function을 argument로 사용 / function을 return 값으로 사용

수학 함수

 

함수형 프로그래밍으로 수학 관련된 걸 가장 잘 할 수 있다. -> 형식적 증명 가능성

수학함수로 합성함수 형태를 사용하여 원하는 결과값을 낸다. 선형형 프로그래밍이라고도 한다. 

                (피드포워드 형태)

 

[함수형 프로그래밍 언어] 

 LISP -> list programming  : 동시에 여러 데이터를 처리하는데 효율적임. 

 Haskel 

    - recursive   (함수형 재귀는 lazy : tail recursion elimination)    :   잘 안 쓴다. 파이썬의 언어적인 특성. 

    - comprehension   = comp

    - *iterator / generator

    - map, filter, reduce (higher order)

 

Accumulator Pattern 

: 누적을 해서 결과값을 낸다. 초기값을 준다.

sum_ = 0
for i in range(1,11):
    sum_ += i
 
sum_

>>> 55

temp = []
for i in range(1,11):
    temp.append(i)
 
temp

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

 

Look and Search

: 반복을 통해서 원하는 결과값을 얻는다. 

temp = []
for i in range(1,11):
    if i%2 ==0:
        temp.append(i)
 
temp

>>> [2, 4, 6, 8, 10]

 


기법 정리

Iterable  :  __iter__

iterable 이란 iteraot가 될 수 있는 아이를 의미. 즉, iterator는 iterable한 아이만 될 수 있다.

 

dir()에 __next__가 있으면 iterable하다. 

 

"Duck Typing"    VS  "상속" : 백조한테 태어나야지만 백조다.  (from collections.abc import Iterab

 

결론적으로  __Iiter__를 가지고 있으면 모두 iterable하다. 

 

iterable ? : element 1개씩 lazy하게 추출한다.

 

a = 'abcd'
dir(a)      # __iter__ 가 있는지 확인
list(a)
>>> ['a', 'b', 'c', 'd']

 

in 다음에는 iterable만 올 수 있다. 
1 in 2
>>> TypeError: argument of type 'int' is not iterable
1 in [1,2,3]
>>> True

 

인위적으로 iterator로 바꿔주는 iter()
a = [1,2,3,4]
dir(a)

>>> ['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__',

...'insert', 'pop', 'remove', 'reverse', 'sort']

----> __next__ 없음

 

이걸 인위적으로 iterator로 변신 시켜줌

b=iter(a)
dir(b)

>>> ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']

------> __next__ 있음

 

b가 메모리에 올라가지 않기 때문에  indexing이 안 된다. (lazy technic)

 b[0]
 >>> TypeError: 'list_iterator' object is not subscriptable

그럼, next로 꺼내올 수 있다. 

next(b)
>>> 1

list로 나머지를 꺼내온다. 

list(b)
>>> [2, 3, 4]

 

할당한다고 메모리에 올라가는게 아니다. 

a = [1,2,3,4]
c = next(iter(a))
 
next(c)
>>> TypeError: 'int' object is not an iterator

 

 

Iterator 대표적인 것들 

for
sum
list
all
enumerate   :  index 만들어줌. 
zip  
set

 

# zip

t = zip([1,2,3],[2,3,4])
next(t)
>>> (1, 2)

 

 

 

 

Recursive

함수형 재귀는 lazy하게 처리한다. 

ex) 피보나치 수열 f(10)을 계산하기 위해서는 f(8) + f(9)를 계산해야 한다. 

      반복하다보면, f(8)부터는 중복적으로 계산하게 되는데, 

      lazy기법은 계산은 하지 않고 미루고 미루다가 중복된  것을 딱 한 번만 처리하도록 했다. 

 

def f(n):
    if n==1 or n==2:
        return 1
    return f(n-1) + f(n-2)
f(5)
>>> 5
f(10)
>>> 55

 

가급적 사용하지 않을 것. 

 

Comprehension

컴프리헨션은 딥러닝에서 추천하는 방식이 아니다. 메모리에 한 번에 올리기 때문에 큰 용량의 데이터는 x

TB를 넘어가지 않는 선에서는 사용해도 괜찮다.  (자연어)

-5가지 문법 사용 예

[x for x in range(1,11)]
>>> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[x+2 for x in range(1,11)]
>>> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
[x+2 for x in range(1,11) if x%2==0]
>>> [4, 6, 8, 10, 12]
[x if x%2==0 else '' for x in range(1,11)]
>>> ['', 2, '', 4, '', 6, '', 8, '', 10]
[(x,y) for x in range(5) for y in range(6,10)]
>>> [(0, 6), (0, 7), (0, 8), (0, 9), (1, 6), (1, 7), (1, 8), (1, 9), (2, 6), (2, 7), (2, 8), (2, 9), (3, 6), (3, 7), (3, 8), (3, 9), (4, 6), (4, 7), (4, 8), (4, 9)]

 

%%timeit
temp=[]
for i in range(1000000):
    temp.append(i)
temp
>>> 64.1 ms ± 380 µs per loop (mean ± std. dev. of 7 runs, 10 loops each
%timeit [x for x in range(1000000)]
>>> 47 ms ± 293 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

 

- COMPREHENSION 종류 3가지

(1) list

(2) set

(3) dictionary

 

위 3가지 공통점 : mutable   (comrehension에서 tuple을 쓰면 generator)

* Haskel에는 mutable data type이 없다. 

 

 

노트북 메모리보다 데이터 메모리가 크면, 가상 메모리를 만드는데, 이 가상 메모리는 굉장히 오래 걸린다. 

고로, 딥러닝에서는 사용하지 않는다. 

Generator

iterator의 사촌 : 만드는 방식만 다르다. 

generator는 comprehension 문법으로 튜플을 써서 만든다. 

함수 안에 __yield__ 가 있으면 generator이다. 

tf.keras.preprocessing.image.DirectoryIterator
tf.keras.preprocessing.image.ImageDataGenerator
tf.data.Dataset.from_generator

똑같이 lazy technic 을 사용한다. 

(x for x in range(10))
 
>>> <generator object <genexpr> at 0x00000297EEFA29B0>

next로 불러들이고, 다 불러들이면 StopIteration 에러가 난다. 

t = (x for x in range(10))
 
next(t)

 

 

 

-generator 2가지 

 

 

 

 

 

 

while을 for로 바꿀 수 있지만, for를 while로는 못 바꾼다. 
단, 무한루프는 for문 보다는 while이 편하다. 

 

 

 

 

 

 

 

map, filter, reduce
To be continued...