[코테] 기초편 1-1 if 조건문

Bpius·2023년 4월 5일
0

알고리즘 입문

목록 보기
1/17
post-thumbnail

처음엔 피이썬 공부를 암기방식으로 접했다.

결과는 망각의 곡선처럼 여지없이 자료형이나 문법의 기억은 다 사라졌다. 시간이 조금 지나 생각해 봤을 때 접근 방법을 달리 했다면 어땠을까 하는 생각을 해본다. 그 방법은 코딩테스트 문제풀이 방법이다. 실전처럼 문제풀이 과정 중에 코딩하는 것이 제대로 그리고 옳바르게 작동할까?를 생각하고 오류가 발생하면 그때그때 원인을 찾아내고 때론 새로운 것을 습득한 것이 머리와 손에 스며들도록.

파이썬과 같은 언어를 배우는 것은 직접적으로 코딩하여 써먹기 위한 것임을 자주 잊어버리곤 한다. 아직도 기초단계에 머물러 있지만 그동안 풀었던 문제 속에서 자주 오류가 나고 생각과는 다르게 작동하는 코드들을 지나오면서 나름 스스로 얻었던 노하우나 주의점을 정리해보려 한다. 앞서 장장 4시간이나 써내려갔던 글을 잘못해서 삭제하고 다시금 쓰는 지금, 너무 세세하게 서술하지 않고 최대한 단순하고 간단하게, 한 번에 너무 많은 양이 아니라 요점만 쓰기로 한다. 어차피 한 번만 쓸 것도 아니기에. for i in range(n) n만큼 반복해서 쓸 예정이다. 기본적으로 파이썬은 사용할 수 있다는 전제하에 문제풀이에 좀 더 집중할 것이다.
이것은 스스로 정리하는 노트이기도 하다.

1. if 조건문을 사용할 때 주의해야 할 점

가장 많이 오류를 경험했던 것들 중 중요하다고 생각되는 것은 3가지, 한 번만 사용되며 순서있게 경우의 수를 따져야 한다는 것.
사실 3가지는 각각 발생되기도 하고 묶여서 한 번에 같이 발생되기도 한다.

1.1 단발성

if문은 True일 때 수행할 문장을 수행하고 False일 때 지나친다. 매우 기본적인 것이지만 이것이 반복문(for, while, 재귀함수)과 같이 쓰일 때 문제가 종종 된다. 반복문을 통해서 그 아래의 수행할 문장이 반복되는 것임에도 불구하고, 마치 if문 자체를 반복문처럼 사용할 때 문제가 된다.

from  collections import deque       # 넣은 데이터 순서대로 빼낼 때 사용되는 자료구조(선입선출 방식 : FIFO)
n = [1, 1, 1, 0, 1, 1]                 백날 정리해만 봐야 망각의 늪에 빠지기 마련이다. 문제풀이하며 직접 실전으로 써봐야 한다.
dQ = deque(n)
count = 0
while n:                             
    a = dQ.popleft()
    if a == 1:
        count += 1
        if a == 0:
            break
    else:
        dQ.append(0)

억지스러운 예시이긴 하나 마땅한 예시가 생각이 안 나네요
숫자 1이 dQ에서 빼낸 후 if문을 만나 숫자를 1더하고 다시금 반복문이 반복된다.
그러다 숫자 0이 나오게 되면 else문으로 가서 dQ에 다시 추가됨으로써 무한루프에 빠지게 된다.
코드를 타이핑 할 때 반복문과 조건문을 한몸으로 여겨 if문이 반복문처럼 작성된다면
숫자를 더한 후 다음 반복문이 적용될 때, 숫자 0이 2중 if문을 만나 아래 수행문 break로 반복문을 빠져나올 수 있다는생각의 오류가 발생한다.
쉽게 말해 숫자 0은 2중 if문으로 진입을 할 수 없다.
이런 오류는 반복문 그 중에서도 특히 재귀함수를 사용할 때 빈번하게 발생된다.

1.2 순서

if~elif~else로 조건을 여러개 늘어서 쓸 수 있다. 이 때 문제의 조건들을 잘 보면서 논리적이며 선형적인 순서대로 코딩해야 한다.

n = [('국어', 92), ('수학', 73), ('영어', 88)]
for course, score in n:              # 리스트 자료구조 안에 튜플 자료구조 형태로 각각(과목과 점수)을 변수를 지정하여 받을 수 있다.
    if score >= 70:
        print(course, 'C')
    elif score >= 90:
        print(course, 'A')
    elif score >= 80:
        print(course, 'B')
결과:
국어 C
수학 C
영어 C

'A'와 'B'를 받아야 하는 과목도 존재한다. 하지만 논리적 순서를 지키지 않아 제일 위의 70점에 모든 과목이 True가 됨으로써
모든 과목을 'C'로 반환하게 되는 오류가 발생한다. 여기서도 if~elif~elif~ else 구조가 마치 반복문 처럼 모두 실행될 것이라는 생각을 할 수 있다. if문은 위에서 차례로 내려오다 조건이 만족될 시 아래의 다른 조건문 모두는 작동하지 않는다.
if문은 한 번만 작동되고 종료한다.

1.3 경우의 수

기초를 접할 때에는 if~else로 간단히 사용하면서 이것 아니면 저것이라는 이분법 논리로 경우의 수는 쉽게 얻어진다.
그래서 기초 때 간단히 사용한 것을 바탕으로 경우의 수를 놓치는 경우가 발생한다.
특히 if 조건문 안에 if를 다시 두는 2중 조건문이라든지,
if~elif~elif~~~else와 같이 조건을 나열하게 되면서 놓치게 되는 경우가 종종 발생한다.

n가지의 과일 종류가 바구니에 m개 담겨있다. 바구니에서 모든 과일을 꺼내어 '* 과일은 맛있다'라고 출력하라.

n = ['a', 'b', 'b', 'a', '2', '1', '3', '3', '1'] # 리스트는 모두 문자로 구성되었다.
for i in n:
    if i.isalpha():                               # '문자' 형태라면 True, 아니라면 False로 반환하는 함수
        if i == 'a':                              # '숫자'형태의 문자: isnumeric(), isdecimal(), isdigit() - 차이점은 구글링 해보기
            print('a 과일은 맛있다')
        else:
            print('b 과일은 맛있다')
    else:
        if i == '1':
            print('1 과일은 맛있다')
        else:
            print('2 과일은 맛있다')
결과:
a 과일은 맛있다
b 과일은 맛있다
b 과일은 맛있다
a 과일은 맛있다
2 과일은 맛있다
1 과일은 맛있다
2 과일은 맛있다
2 과일은 맛있다
1 과일은 맛있다

'3'번 과일이 나와야 하는 경우의 수를 놓쳤다. 매우 간단한 예시이지만 문제의 조건들이 까다롭고 복잡하면 쉽게 놓치는 부분이다. 경우의 수를 놓친 것 뿐만이 아니라 '3'번 과일을 '2'번 과일로 잘못 출력하는 오류까지 더한다.

1.4 그리고

당연히 조건을 생각할 때 순서랑 경우의 수는 당연히 생각하지 않느냐라고 물을 수도 있을 것이다. 하지만 코딩이라는 세상 속으로 들어오기 전까지의 지니고 있었던 논리가 코드에서 표현되는 논리와 상이하다고 보여지는 것들이 있는 것도 사실이다. 사실 상이하다는 표현보다는 그런 식으로 세상을 보는 논리에 익숙하지 않다고 보는 것이 맞다.
뜬금없지만 이야기해보자면 초등학생 3학년인 철수는 게임을 주말에 게임을 하다가 목이 말라 컵에 물을 부어 마시려다 컵을 떨어뜨려 깨버렸다. 그리고 엄마에게 혼날 것이 무서워 컵을 치우고 방에 들어가 게임을 끄고 공부를 시작한다. 철수는 컵을 깨뜨린 순간부터 앞으로 일어날 일을 예상하여 최대한 엄마의 심기를 거스르지 않으려는 선택을 이어간다. 반대로 밖에 외출을 마치고 들어온 엄마는 공부를 하는 철수를 보며 생각한다. 게임을 하고 있을 아들이 조용히 앉아 공부를 하고 있는 생경한 모습을 보았다. 그리고 무슨 문제를 쳤구나라고 짐작하며 주위를 둘러보다 주방이 깨끗하게 청소가 되어있는 것을 발견한다. 왜 주방이 깨끗하게 청소가 되어있는지 생각하며 주위를 둘러보던 중 컵 하나가 사라진 것을 발견하고 아들을 불러 물어본다. '컵 깼니?'
철수는 연역적으로 생각했고 엄마는 귀납적으로 생각했다. 사람은 연역적 논리에 더 익숙하다. 어떤 사건이 일어난 것을 보고 들을 때마다 그 원인이 무엇인지 되짚어 생각하는 것보다, 앞으로 일어날 일이 무엇일지 생각하는 것에 더 익숙하다는 이야기다. 알고리즘도 마찬가지로 익숙하지 않은 논리를 적용하여 적용하려면 여간 애를 먹는 것이 아니다. 추후 이야기를 할 것이지만, 자기 자신을 재호출한다는 이상한 논리를 지닌 재귀 알고리즘이나 점진적 논리를 가지고 풀어야 하는 플로이드 워셜(Floyd Warshall) 알고리즘 등을 짚어보기 위해서는 익숙한 논리 뿐만이 아니라 익숙하지 않았던 논리에 대해서도 접근하기 위해서는 '정말 순서는 필요없을까? 정말 모든 경우의 수를 짚었는가?'라고 계속 물어서 익숙해져야 한다.

2. 문제

100%는 아니지만 최대한 주제에 맞춘 문제를 가져와 보려한다.

어떤 문제는 출처를 제시하고 쓸 수도 있지만

그대로 문제를 가져다 쓸 경우 저작권 등의 이유로 문제가 될 수 있기에 변형 및 아이디어를 얻어 변경했다.

문제:
미슐랭 가이드 평가원인 왕먹방씨는 서울의 어느 레스토랑에 들러 코스요리를 주문하고 점수를 체점하려 한다.
음식이 맛있으면 '1'을 맛이 없으면 '0'을 준다.
맛있는 음식이 연속으로 나왔을 경우 추가점수를 준다.
추가점수를 포함하여 연속으로 맛있는 음식이 나왔을 경우의 점수는 2번 연속일 때는 2점, 3번연속이면 3점을 주는 형식이다.
n가지의 코스요리가 나왔을 때 점수를 반환하는 프로그램을 작성해보자.(1 < n <= 100)

예)
1 1 0 0 1 1 1 0 1 : 요리 순서
1 2 0 0 1 2 3 0 1 : 요리 점수
위와 같이 요리가 나왔을 때 점수는 다 합하여 10점이다.

풀이는 다음 챕터에...

많은 사람들이 코테 실력을 높이려면 많이 풀어봐야 한다고 하나같이 입을 모아 이야기한다.
그 안에는 바로 답을 보는 것뿐이 아니라 풀려고 노력한 고민까지 포함된 것이다.

profile
데이터 굽는 타자기

0개의 댓글