brunch

You can make anything
by writing

C.S.Lewis

by Younggi Seo May 03. 2022

파이썬의 간결성

파이썬 창시자는 재귀 구현에 공을 들이지 않았지만 파이썬은 최강의 언어다




나는 우주의 원리가 아름답고 단순할 것이라고 굳게 믿는다.

                                                                                         - 알버트 아인슈타인



일론 머스크가 동생과 함께 사업 초창기에 스타트업을 시작할 때였다. 그때 한국인을 한 명 고용한 뒤, 회사로 가기 위해서 BMW를 운전시켰는데, 가다가 바퀴가 빠져서 사고 날 뻔한 적이 있었다. 다행히 차만 찌그려졌지만, 그때의 사고가 현재 테슬라가 도로 위를 주행하게 만든 아이디어의 원천이 된 건지도 모르겠다.


어쨌든 일론은 남아프리카 공화국에서 태어나 자라면서 백과사전 2질을 섭렵하고 서점가에서 쫓겨날 정도로 독서광이기도 하다. 그의 넘쳐나는 지식의 인풋량이 지금의 그를 만든 것임에는 틀림없지만, 그가 처음 코딩했던 데이터 수집 관련 마케팅 앱은 *리팩토링(코드 개선 작업) 할 게 천지였던 모양이다.


그 당시 새로 고용한 개발자 신임 둘이 그가 짜 놓은 일방통행식 코드를 보고 사흘 밤낮을 세워서 리팩토링을 해야만 했다. 그리고 이후 그가 새로운 스펙을 요청받으면 개발자들에게 하루, 아니면 삼일의 소요기간을 배정했는데, 그 말은 즉슨 하루는 일주일, 삼일은 보름 정도로 개발자들이 구현할 수 있다고 간주하곤 했다는 그의 전기에서 독단적이지만 과감한 전략이 강점인 일론의 일면을 엿볼 수 있다.


어쨌든 그 머리 좋은 공상가인 일론 머스크도 코딩할 때는 난잡하게 만드는데, 프로그래밍의 기본은 간결성에 있다고 해도 과언이 아니다. 간결할수록 더 단순하고, 단순할수록 성능이 좋으므로(너무 단순하면, 코드 가독성 떨어질 수도 있지만) 서비스 출시 이후 유지보수를 하는데도 용이하다.


그런 의미에서 파이썬은 엄청난 강점을 지닌 언어임에 틀림없다. 그 한 예로 순열 알고리즘을 코딩하는데 파이썬은  단지 n의 계승(n!, 팩토리얼) 재귀 함수에서 뽑는 횟수를 추가한 식으로도 구현할 수 있다. 자연수 n개 원소의 k순열은 서로 다른 n개의 원소 중에서 k개를 뽑아서 한 줄로 세우는 방법으로, 그 가능한 가짓수는 P(n, k)로 표현하며 다음과 같은 함수로 구할 수 있다(도경구, 2021).

def permutation(n, k):
        def loop(n, k, all):
                if k > 0:
                    return loop(n-1, k-1, all*n)
                else:
                    return all
        return loop(n, k, 1)

Test 출력

permutation(3,2)

6

함수 실행 추적

=> loop(3,2,1)

=> loop(2,1,3)

=> loop(1,0,6)

=> 6


반면에 이 순열 알고리즘을 C++ 언어로 구현하면 아래와 같다.

swap이며 Array배열까지 생각할 거리가 많아진다. 분명히 같은 220V 코드를 사용하고 있는데도 말이다...




압축하여 훨씬 쉽고 간결하게 구현할 수 있는 능력이 프로그래머가 지향해야 할 기술 중 하나라면, 파이썬은 그것에 부합하는 언어라고 생각한다. 현대의 프로그래밍 교육은 프로그램이 동작하는 근본적인 방식보다 프로그래밍 언어의 문법, 즉 외연(컴퓨터 과학)에 많이 치중되어 있다. 그런데 파이썬의 강점은 언어 자체의 문법에 너무 치우치지 않고도 프로그램 구현이 가능하다는 '간결성'에 있다.



다음은 '오늘부터 n일 뒤는 몇 년 몇 월 며칠?'인지를 계산해주는 프로그램의 코딩 순서다. 현재 학습하고 있는 책의 드디어 4장의 마지막을 정리하는 문제로 이틀 만에 완벽히 구현해내는 데 성공했다. 사실 이 알고리즘을 구현하는 데는 '월별로 날짜수가 다른 사실'과 평년/윤년에 따라 2월의 날짜 수도 다른 점'을 고려해야 한다. 이 코드는 이미 3장의 연습문제에서 모두 구현해놨었기 때문에 그 함수들을 모듈처럼 쓸 수 있었다.


프로그래밍하기 전에 스스로 짠 **pseudo-code다.

- plus_date 알고리즘

0) 프로그램 시작

1) 오늘 날짜 year/month/day 와 경과일수 n을 확인한다. (유효한 날짜라는 가정하에)

2) 해당 월(month)의 날짜수를 구한다(days_in_month 함수 호출함).

3) 그 날짜수에  현재일(day)을 경과일 수 n을 뺀 일 수를 남은 일수(days_left) 신규 변수에 기록한다.

4) 제어 조건

  - if then: 남은 일수(days_left)가 dd(현재일)보다 경과일수(n)보다 적을 경우 입력받은 오늘 날짜를 출력,

  - else: 경과일수 n에 남은 일수(days_left)만큼 뺀 값을 다시 n에 기록한다.

  - 현재 , 월을 입력값으로 next_month 함수 넣은 , 월을 갱신(다음 ) 하거나 12월이면 (다음 ) 갱신한다.

  - 갱신한 , 월을 days_in_month 함수 입력값으로 받아서 '갱신한 달의 날짜 '(days_this_month)라는 신규 변수에 기록한다.

5) 위의 제어 조건 내부에 재귀 함수(while loops)를 만들어 n(경과일 수)이 갱신한 달의 날짜 수(days_this_month) 보다 작아질 때까지 계속해서 년월을 갱신한다(재귀 구조).

6) 최종적으로 n(경과일 수)이 갱신한 달의 날짜 수보다 적으면 그때의 n을 일자로 그리고 최종적으로 갱신된 년과 월을 함께 출력하면서 while-loop와 if 제어 조건 블록을 빠져나온다.

7) 프로그램 종료


<github source>


재귀의 매력도 파이썬에서 구현하여 비로소 깨달을  있었다. 지금까지 알듯 모를듯한 수학공식을 기억해내면서 재귀 함수 작성과 꼬리 재귀 함수 그리고 while loop 버전으로 다시 코딩하면서 결국 이것을 응용해서 써먹을  있는 프로그램이 경과일 수만큼의 날짜를 구하는 것이었다. 재귀 함수에서 꼬리 재귀 함수나 while루프 구조로 바꾸면 계산 공간을 낭비하지 않는다는 것을 알았고 나눠 풀기(***Divide and Conqure, 분할 정복) 알고리즘을 통해서 계산시간의 소요는 절반 이상 준다는 알았다.



이것은 나중에 '알고리즘 최적화'라는 기술에 응용할  있는데,  서비스에서 일시적으로 사용자가 몰릴 , 시스템 과부하를 줄이기 위해 해당 코드로의 리팩토링이 유용할 수도 있다. 실제로  서비스에서 가장 위험이  해킹 공격  하나로 '가용성(병목현상, DDoS 공격)' 저해 차지한다.  



* 리팩토링(refactoring)

** 의사(pseudo-code) : 어떠한 방식이라도 간략하게 문제 해결 과정을 나타내는 초안. 더 정확하게는 의사 코드((프로그램이 실행되기 전에 기계 코드로 번역될 필요가 있는 것))


*** Divide and Conqure: 동적 알고리즘 종류의 하나로 가장 단순한 예로 '피보나치수열'을 들 수 있다. 최적화와 연관이 알고리즘으로 코딩 테스트에서 어려운 문제 중 하나로 출제를 하긴 하나, 출제기관에서도 기피하는 경향이 있다고 한다.





매거진의 이전글 재귀 함수의 이해와 정리
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari