본문 바로가기

노력/인공지능

5. 파이썬 개인공부 5 (함수형 패러다임)

functional paradigm에서는 어떤 값을 튜플로 주로 사용한다.




모든 것은 함수 기반이다.


a = 0

b = a+1

a = 3


- 절차적 패러다임




절차적 패러다임은 일일히 다 보아야 사용할 수 있다.


함수형 패러다임은 유지보수가 간편하다. (함수가 잘 짜여져 있는지만 확인하면 된다.)


우리의 주 패러다임은 객체지향이다. 객체지향과 함수형 패러다임은 비슷한 편.


%timeit ((((1+2)+3)+4)+5)

100000000 loops, best of 3: 14.7 ns per loop


%timeit (1+(2+(3+(4+5))))

10000000 loops, best of 3: 14.9 ns per loop


괄호 하나차이로도 속도의 차이가 크게 난다.


파이썬은 앞에서부터 계산하기 때문.


절차적 패러다임과 함수형 패러다임은 계산 상으로 보면 값이 같다.

하지만 함수형 패러다임은 값을 함수로만 변경한다.



• Functions are first class (objects). That is, everything you can do
with “data” can be done with functions themselves (such as
passing a function to another function).

• Recursion is used as a primary control structure. In some lan‐
guages, no other “loop” construct exists.

- 반복이 아예 없는 언어도 있다. 언어 자체에 반복이 없다. (FP), 함수형 패러다임이라도 반복 대신에 recursion을 사용하지 않는다. 너무 느리기 때문에.(tail-recursion optimization이 안된다.)
1. loop -> recursion (점화식, 일반식을 만들 수 있으면 바로 만들 수 있다.) -> 이해하기도 좋고 구현하기도 간단하다. 하지만 퍼포먼스가 좋지 못하다.(tail-recursion)
2. for, while, comp(하스켈에서 베껴옴)



• There is a focus on list processing (for example, it is the source
of the name Lisp). Lists are often used with recursion on sublists
as a substitute for loops.

- list 처리하는데 최적화돼있다. 데이터 하나 바꾸기에는 함수형 패러다임이 적합하지 않다. 함수 안에 iterable 넣는 애들이 대부분 함수형 패러다임에서 나온 것이다.

• “Pure” functional languages eschew side effects. This excludes
the almost ubiquitous pattern in imperative languages of assign‐
ing first one, then another value to the same variable to track
the program state.

- input에 대한 output값만 바꾸어야 한다. side effect를 없애는 것이 함수형 패러다임이다. mutable보다 immutable을 더 좋아한다.




-- functional programming : 추상화가 되면 이해하기 쉽다. 버그가 없는 코드를 짤 수 있다. 빠르게 개발할 수 있다.

Advocates of functional programming argue that all these character‐
istics make for more rapidly developed, shorter, and less bug-prone
code. 


함수로 만들면 재사용이 가능하다. 절차적 프로그래밍은 재사용이 불가능하다.


comprehension : 아래 형태의 초기화값 만들 때 사용한다., 식이다.

a = [i for i in range(10)] -> 초기화값 만들 때, 매우 유용하며 속도도 빠르다.

{z for z in range(100)} : set

{z:'1' for z in range(100)} : dictionary



(z for z in range(100))


반복이지만 문이 아니라 반복식이다. 이를 반복식이라고 부르지 않고 comprehension이라고 부른다.


[z for z in range(100)] : z에 대한 조건식. 조건식 다 들어갈 수 있다.

collection = [d if condition(d) else modify(d) for d in data_set] : 이런 조건식도 comp에 달 수 있다. 

comp는 기본적으로 사용해야 한다.




generator와 iterator는 

iter(a) : iter는 iterable의 기능 + iter라고 변신하는 순간 next를 쓸 수 있다.

next의 마지막에서는 StopIteration 오류가 발생한다.

a = [1,2,3,4,5]
b = iter(a)

next(b) : 1
list(b) : 2,3,4,5

next를 할 때 하나씩 빠지는 것.

lazy한 기법을 사용할 수 있다.


iterator 중에서도 특별한 애가 generator다.

tuple로 comprehension 만들었을 때 generator가 된다.

a = range(10)

a : range(0, 10) -> 제너레이터, 이터레이터




map(lambda x:x+1, [1,2,3,4,5])
<map at 0x2607727e5f8> : 이렇게 꺽새가 나오면 객체를 뜻한다.


def a():
    yield 1
    yield 2
    
이걸 부르면 한번은 yield 1, 한번은 yield 2 이렇게 번갈아가면서 부른다.

for i in a():
  print(i)
  
  
next(a()) 이렇게 하면 할 때마다 새로운 a()가 할당된다.



x = a()

next(x)

이렇게 할당해서 사용하면 이제야 사용할 수 있다.



def a():
    yield 1
    yield 2
    return 3
    
next(x) : 1, 2가 하나씩 출력되고 3에서 StopIteration 오류가 발생한다.


(comp) : 제너레이터식.

제너레이터 : 2가지, comp를 튜플을 이용해서 만들거나, 함수에 yield를 써서 만들 수 있다.
이터레이터 : 1가지, iter를 사용해서 만든다. next를 이용해서 다음으로 넘어간다.






def t():

  for i in range(10):

    print(i)

    

    

    

거대한 시퀀스 메모리에 효과적이다 : generator



한 번 실행 시킬 때마다 하나씩 나오니까 하나씩 필요할 때, 제너레이터가 효율적이다. (메모리 활용에 유리하다)


그게 아니고 한 번에 필요한 것이 많다면 그냥 할당해서 사용하는 편이 좋다.



comprehension : 반복식. 반복식이라고 하지만 그냥 comprehension이라고 부르자.


decorator: 클로저와 매우 흡사한 기능을 한다. 함수를 parameter로 설정하고 사용할 때 argument를 함수로 받아서 받은 함수를 되돌려주는 것.




assert condition : 조건에 맞지 않으면 에러를 내줘!


예외처리이다.  디버깅 모드에서만 효과가 있으며 릴리즈에서는 아무런 일도 하지 않는다. 예외처리와 비슷하지만 조금 다른 상황에서 사용한다.





map은 for를 없앨 수 있는 것이다.


for를 그냥 쓰는 것은 함수형 패러다임이 아니다. 생각의 차이이다.


더 우아하고 간단하게 같은 것을 할 수 있는데 코드를 길게 쓸 이유가 없다.



reduction : 축소, 하나의 결과값으로 축약한다.



sum([1,2,3,4,5]) -> 15 : 결과를 하나로 축약해서 보여주었다. 이를 reduction이라고 한다.


여러개의 값이 들어가서 값이 하나만 나온다면 reduction이다. ex) sum, all



min([1,2,3])

min(1,2,3,4,5,)

함수형 패러다임



is로 시작하거나 able로 끝나면 대부분 True, False 반환.


predicate : 참, 거짓을 반환하는 애.


객체지향, 값과 행동을 동시에 묶어서 사용. 안에 value와 method가 있으면.



값을 만드는 방식: 리터럴 vs 객체방식


a = int(값) -> 객체방식


5가지 문 : 할당문, 조건문, 반복문, 선언문, 예외처리문


type(a) : 
__main__.A   -> __main__은 현재 파일이라는 뜻, 현재 파일의 A라는 클래스

int a
dir(a) : a로 할 수 있는 것들을 알 수 있다. 클래스에 따라서 할 수 있는 행동과 값이 정해져 있다.

프로그램이 커지거나 하는 경우를 대비해서 객체지향이 관리가 용이하다. 클래스()

type은 클래스 이름을 반환한다.


b = 'wow'

b = str('wow')


리터럴 방식은 대응하는 객체 방식이 있다.

현재 b는 모두 str타입이다.





class A:

    a = 3

    

x = A()


x.a : 3






class A:

    a = 3

    def t(self):

        return 4

        

x = A()



x.t() : 4



클래스 안에 있는 함수는 메소드이다.


메소드는 클래스의 이름을 붙여서 사용한다. A().t() : 이렇게 메소드로 사용하는 것이 함수보다는 실수가 적다.


함수보다는 클래스의 메소드가 더 우아하다.




 - 객체지향은 또 패러다임이다. (함수형, 절차적) -> 파이썬은 이를 모두 사용할 수 있는 멀티 패러다임이라 매우 강력하다.

 

인스턴스화.


리터럴방식 vs 인스턴스화하는 방식(객체)


callable(A) -> A라는 클래스에 괄호를 붙일 수 있냐 없냐, 보통 괄호를 붙일 수 있는 것은 인스턴스화 할 수 있는 것과 함수인 경우. -> A 뒤에 괄호 붙일 수 있는지 없는지... True, False 반환, function or method




람다에는 이름이 붙지 않기 때문에, 익명함수라고 한다. 할당해서 이름 붙일 수 있다.



functional paradigm, 수학적 개념이 매우 중요하다.



lambda에는 callable을 붙이면 True




>>> adders = []

>>> for n in range(5):

 adders.append(lambda m: m+n)


accumuation pattern 



>>> [adder(10) for adder in adders]

[14, 14, 14, 14, 14]

>>> n = 10

>>> [adder(10) for adder in adders]

[20, 20, 20, 20, 20]




클래스 안을 보고 yield가 있다면 generator이다.




generator는 iterator의 일종이다. 그렇기에 next를 쓸 수 있다.





>>> for _, prime in zip(range(10), primes):      ->      _는 어떤뜻인가?

... print(prime, end=" ")

....

7 11 13 17 19 23 29 31 37 41




_는 4가지 사용법이 있다 : 변수 이름지을 때, 첫번째는 특수문자 못오지만 첫번째는 쓸 수 있다.


두번째 세번째는 객체에서 한다.


네번째는 for _, prime in zip(range(10), primes):  이런방식에서 사용될때 쓴다. _는 관례상 안쓴다는 것을 나타내주기 위해서 사용한다.


zip도 객체


<zip at 0x26077295948>


list(zip([1,2,3])) -> 객체가 눈에 잘 안보일때 사용하자.

[(1,), (2,), (3,)]



list(zip([1,2,3], [4,5,6,7])) -> 수에 맞게 이어주고 넘치는 것은 삭제해준다.

[(1, 4), (2, 5), (3, 6)]


*도 여러 사용방법이 있다 : 방법들은 꼭 외우자



next(zip([1,2,3], [4,5,6,7])) : 이터레이터라는 것을 알 수 있다.

(1, 4)




for _, prime in zip(range(10), primes): 결국 이 코드는 range 10개 코드는 관례상 사용하지 않겠다고하는 _에 들어가고, prime에는 primes 값이 들어간다.


zip은 next가 불릴 때마다 값이 불린다.




predicate :  논리학에서 많이 사용되는데 True, False로 반환되는 값.



lazy evaluation : 뭔가를 부를 때 딱 해주는 것이 lazy evaluation이다.



itertools : 고급 반복기법을 지원해주는 툴들, 기본적으로 next로 실행한다.


functools : 



from ..... import .... : 이렇게 쓰면 namespace 안에 바로 입력된다. 따로 .....qwerwqf라고 사용하지 않아도 그냥 내 네임스페이스에 기록되서 바로바로 쓸 수 있따. qwerwqf





t = chain([1,2,3], [2,3,4], [1], [2,3]) -> next(t) : 반복

같은 특성을 가진 연속된 수를 연결해준다.


t = chain([1,2,3], (2,3,4), [1], [2,3]) : 여기에서는 2,3과 2,3이 튜플과 리스트이기 때문에 연결되지 않는다.