읽기 편한 코드 VS 이해하는데 시간이 걸리지만 빠른 코드

나는 제 3자가 읽어도 개념적 구조를 무리 없이 이해할 수 있게 “무엇을 넣고, 어떤 것이 기대하는지” 신경을 쓰며 코드를 작성한다. 그러나 백준 온라인 저지의 문제를 매일 최소 한문제씩 풀고, 23시에 회고를 하는 “데일리 백준” 모임에 참여하게 되면서 이런 취향에서 어느 정도까지 타협을 해야 될지 고민하고 있다.

제 3자가 무리 없이 개념적 구조를 이해할 수 있는가?

‘무리 없이’라는 것은 코드 자체가 컴퓨터에서 느릴 수 있지만, 사람에게 ‘구문’으로서 쉽게 이해할 수 있다는 나의 주관적인 가치이다. 그렇지만 무리가 좀 있어도 구현체가 충분히 빠르고, 테스트 또는 문서의 도움으로 개념적 구조를 이해할 수 있으면 클린 코드라 부를 자격이 있어 보인다고 최근의 경험을 통해 생각이 바뀌였다.

사례 : 스노우볼 문제

17950 : 스노우볼 문제는 내가 데일리 백준에 참여하게 되면서 처음 풀어본 문제이다. Θ(n)의 시간 복잡도를 의도하고 출제된 문제로 보였다. 평소 하던 대로 모든 입력 정보를 함수의 인자로 넘겨 계산하니 시간 초과가 났다. 이미 입력 아이템1을 배열로 묶어 넘기는데에 Θ(n)을 소모했기 떄문이다.

solved.ac 등급으로 브론즈 2로 평가된 쉬운 문제이니 한번 풀어보고 이어지는 코드 블럭을 읽어보는 것을 추천한다.

HEIGHT, MULTIPLY_TO = map(int, input().split( ))
DIV_TO = 1000000007

multiplied = 1
answer = 0

for __ in range(HEIGHT):
    multiplied *= MULTIPLY_TO
    multiplied %= DIV_TO
    answer += int(sys.stdin.readline()) * multiplied
    answer %= DIV_TO

위 문제는 단순한 가산 문제이므로 입력하면서 계산을 같이 수행하면 기대한 Θ(n) 시간으로 풀 수 있다.

백준 온라인 저지를 풀때는 “무엇을 넣고, 어떤 것이 기대되는지” 모두 포기하면서 풀이를 제출해야 될까?

다행이 파이썬은 sys.stdin 으로 입력을 대신 처리할 수 있어서 백준 채점 서버와 내 로컬의 pytest 환경 모두 평가할 수 있게 아래처럼 타협할 수 있다.

import sys


def main():
    HEIGHT, MULTIPLY_TO = map(int, input().split( ))
    DIV_TO = 1000000007

    multiplied = 1
    answer = 0

    for __ in range(HEIGHT):
        multiplied *= MULTIPLY_TO
        multiplied %= DIV_TO
        answer += int(sys.stdin.readline()) * multiplied
        answer %= DIV_TO

    return answer


def test_main():
    sys.stdin = open(problems/17950.txt, r)
    assert main() == 2830


if __name__ == __main__:
    print(main())

생각해보기

O(n) 복잡도를 가졌고 가독성은 보통인 대신에 테스트에 개념적 구조를 잘 녹아냈다고 가정한 구현체가 있다고 가정해보자, 또 다른 구현체로 O(nlogn) 시간 복잡도를 가지면서 구현체의 가독성이 높다. 이중에 어느 쪽이 더 클린 코드에 어울릴까?

마치며

어떤 것이 더 클린 코드에 부합하는지 모르겠다. 상황에 따라 적절한 것을 사용하는 것이 최선인 것 같다.


  1. H줄 동안 과제산에서 만들어진 스노우볼의 개수 ↩︎