파이썬으로 배우는 수학적 프로그래밍

1

파이썬을 조금 더 깊이 있게 바라볼 수 있게 해주는 책이다. 용어적인 측면에서 배울 것이 많고, 간단한 대수학을 통한 바라본 파이썬 세상에 대해서 느낄 수 있다.

2

당부의 말씀을 드리자면 ‘1~3’은 그냥 읽지 않고 넘어가서 4장부터 읽어보시길 권한다. 1~3장은 너무 간략하고 단순해서 약간 김이 빠진다.


파이썬은 코루틴, 서브루틴, 프로시저, 함수를 섞어쓰고 있는데 잘 구분해야 한다.

지금까지 살펴본 것처럼 파이썬 함수 중 어떤 것은 값을 계산하고 어떤 것은 부수 효과를 갖고 있다. 특히 부수 효과를 가진 함수를 일부 프로그래밍 언어에서는 ”서브루틴(subroutines)” 또는 ”프로시저(procedure)” 라고 부르지만, 파이썬은 이 모두를 통틀어 ”함수(function)”라고 정의한다.

재귀함수를 잘 설계하는 방법은 수학시간에 점화식을 통해서 배웠던 것 같다.

재귀 함수를 잘 설계하려면 디음의 특정을 이해하고 따라야 한다. 재귀 함수는 발생 가능한 모든 경우를 바탕으로 정의된다(파이씬에서는 대체로 if-문과 함께 이용한다). 따라서 최소한 하나 이상은 재귀가 아니어야 한다. 즉, 정의된 함수를 재귀 참조하는 것이 아니라 값을 바로 반환하도록 정의해야 한다. 이를 기저 조건(basis case)이라고 부른다. […] 모든 재귀 호출 시퀀스는 결국 기저 조건을 호출하는 방향으로 진행되어야 한다. 대체로 기저 조건은 인수가 “작거나” 또는 “간단하거나”, “사소한” 상태를 다룬다. 반면에 재귀 조건(기조 조건이 아닐 때)은 함수의 매개 변수를 받아서 그보다 “작거나”, “간단하거나” 등 기저 조건과 더 가까운 인수로 함수 자신을 재귀적으로 호출하도록 설계한다

자바스크립트의 ‘1급 함수’

다른 함수를 인수로 취하거나 함수를 반환하거나 또는 둘 다에 해당하는 함수를 고계 함수(higher-order function)라고 부른다.

고계 함수를 사용하는 팁?

일반적인 기법은 함수의 모든 인수를 바인딩하지 않고 일부만 바인딩해서 나머지 인수를 남겨두는 것이다. 이를 부분 적용(partial application)이라고 한다.

def partial(f,x):
    def f_x(y):
        return f(x,y)
    return f_x

람다에 대한 몇가지 정의 중 하나?

파이썬에서 함수가 값이라면, 함수를 값으로 갖는 식은 없을까? 물론 있다. 이러한 표현식을 람다식(lambda expression)이라고 부른다. “람다(lambda)”는 그리스 문자이며 수학용어다.

람다의 활용

람다식은 다른 함수를 반환하는 함수를 만들고 싶을 때 유용하게 사용할 수 있다.

튜플의 개념

n-튜플(n-tuple)은 특정한 쌍의 수를 표현하기 위해서 n에 숫자를 대입하여 사용한다. […] n-튜플은 일반적으로 수학에서 사용하는 개념이기 때문에 유한한(대체로 적은 수) 구성 요소를 가진다. 또한, 모든 구성 요소가 같은 종류의 수학적 객체일 필요는 없다. 한편, 구성요소는 집합에 속하기 때문에 모두 다를 필요도 없다.

쉼표도 연산자임

쉼표 연산자는 여러 값을 하나의 객체로 묶는(pack) 역할을 한다. 튜플의 묶음을 풀기(unpack) 위해서는 튜플의 묶음 문법을 반대로 적용하여 처리할 수 있다. 즉, 좌변에 하나가 넘는 이름이 쉼표로 구분된 할당문을 이용하는 것이다.[…] 또 다른 튜플의 묶음을 푸는 일반적인 문법으로 동시 할당(simultanceous assignment)이 있다.

튜플과 시퀸스의 특징

우리가 연산에 사용하는 시퀀스 대부분은 동질적(homogeneous)인 특성이 있다.

문자열에 결합법칙을 적용

문자열의 연결 연산에는 결합법칙이 성립한다. 만약 +가 연결 연산자라면 임의의 문자열 a,b,c에 대해 (a+b)+c=a+(b+c)가 성립한다. 물론 문자열뿐만 아니라 다른 형식의 시퀀스에도 똑같이 연결 연산의 결합법칙이 성립한다.

결합법칙을 적용시킬 수 있는 구조

연결 연산에 대한 문자열과 더하기 연산에 대한 정수 모두 모노이드(monoid)라는 수학적 구조의 예이다. 모노이드는 결합법칙이 성립하는 이항 연산자와 항등원을 지닌 집합이다.

  • 모노이드 - 닫힘성, 결합법칙, 항등원
  • 모노이드에서 항등원이 제외되면 반군(semigroup)이라고 함
  • 역원(inverse)과 항등원을 모두 가진 반군은 군(group)임

표기의 중요성

(2,3,5,7,11)과 같은 구조를 “튜플 표시(tuple display)”라고 생각할 수 있으나 실제로는 그렇지 않다. 앞에서 본 것처럼 쉼표는 이항 연산자고 튜플을 만들어내는 것은 괄호가 아닌 쉼표다. 하지만 괄호를 이용하는 튜플 표기법과 대괄호를 이용하는 리스트 표시는 겉으로 보기에도 비슷할 뿐만 아니라 파이썬 문법 규칙 덕분에 비슷한 의미도 있다.

불변성

튜플과 리스트의 가장 큰 차이점이라면 리스트는 변경할 수 있는 객체(mutable object)고 튜플은 그렇지 않다는 점이다.

고계함수를 사용한 필터 적용

고계 함수를 이용해서 시퀀스에 대한 다양한 연산을 쉽게 처리할 수 있다. 전형적인 고계 함수인 map, filter, reduce의 정의와 사용방법에 대해서 알아보자.

map에 대하여

map은 시퀀스의 모든 인수에 인수가 하나인 함수를 적용하고 결괏값을 튜플로 생성한다. […] sequence가 for-문의 헤더로 사용되면 시퀀스를 생성하는 모든 값이 될 수 있다. 즉, 튜플이나 리스트, range, 반복자 등이 될 수 있다.

def map(f, sequence):
    result = ()
    for a in sequence:
        result += (f(a),)
    return result

filter에 대하여

filter는 부울 값과 시퀀스를 반환하는 인수가 하나의 함수를 취한다. 함수 filter는 시퀀스의 각 원소에 주어진 함수를 적용하고 튜플을 반환하는데, 이때 튜플에는 주어진 함수가 True값을 반환하는 원소만 포함된다.

filter(lambda x: x > 0, (2,3,0,-5,7,-11)  # 결괏값은 (2,3,7)

reduce에 대하여

세 번째 함수인 reduce는 또 다른 함수와 값의 시퀀스를 받아 주어진 함수를 이용해 연산을 수행하여 하나의 값으로 줄인다. […] 함수 reduce는 모노이드 형태인 임의의 값과 연산자에 적용할 수 있다. 이때 모노이드 항등원을 시작값으로 사용한다.

reduce(f, sequence, initial)
    result = initial
    for a in sequence:
        result = f(result, a)
    return result

리스트 내장

리스트 내장으로 하나 또는 그 이상의 다른 시퀀스를 바탕으로 리스트를 생성할 수 있다. […] 하지만 처리 결과의 구조를 더 직접적으로 나타내는 문법을 사용한다.

[표현식 for 이름 in 시퀀스 if 조건]
[line[5:] for line in open("names") if line.startwith("John")]

스트림에 대하여

여기서는 스트림이라는 용어를 값이 동적으로 생성된 시퀀스를 대표하는 의미로 사용, 파이썬에서 스트림은 두 가지 종료의 객체 즉, 반복자와 range 객체에 의해서 생성된다.

항상 두근두근하게 사용하는 yield

발생자 함수는 다음과 같이 yield-문을 실행하여 값을 산출(yield)한다.

def fibs(n):
    a=1
    b=1
    for i in range(n):
        yield a
        a,b = b, a+b

K-V 구조에 대하여

파이썬에서 집합은 튜플과 리스트처럼 값을 가질 수 있는 자료구조며 파이썬에서는 이러한 구조를 컨테이너(container)라고 부른다. […] 파이썬에서는 사전(dictionary)이라는 컨테이너도 있다.

반복자에 대하여

정적 시퀀스나 스트림과 마찬가지로 파이썬의 집합은 for-문을 이용해서 집합의 모든 원소를 반복할 수 있는 객체다. 이런 객체는 파이썬 용어로 이터러블(iterable)이라고 한다.

여기도 해싱, 저기도 해싱..!

파이썬은 해싱(hashing)이라는 기술을 이용해서 집합을 구현한다.

Written on September 28, 2015