SW정글 시작 + W01

HiroPark·2023년 3월 4일
0

Jungle

목록 보기
3/10

개발을 시작한지 1년이 조금 넘은 지금, SW정글에 참여하게 됐다. 처음에는 데이터 분석이나 좀 배워볼까 하고 시작한 것이 이렇게까지 올 줄은 몰랐다.
지난 1년여간 열심히 공부하였지만, 전공자가 아니고, 절대적인 시간의 부족함이 있었기에, CS지식이나 알고리즘 등 기초적인 부분에 있어 부족함을 느끼고 있었다.
그러한 부분에 대해 갈망을 느끼고 있던 와중에 좋은 기회를 잡을 수 있게 돼서 정말 다행이라고 생각하고 있다..

이곳에서 나의 목표는 간단하다.

  • 당연하게도 매주 주어진 과제를 완전히 해내는 것이 첫번째 목표이고,
  • 거기에서 더 나아가서 매주 할당량을 하루씩 일찍 끝내는것이 다음 목표이다.
    - 하루씩 일찍 끝낸 후 한 주간의 내용을 복습하거나, 다른 분들과 공유하는 것을 목표로 하고 있다.
  • 정글과는 별도로, 리트코드 딱 50문제를 추가적으로 푸는것이 마지막 목표이다.
    알고리즘 주차에 관련된 주제에 대한 문제를 풀거나, 시간이 날때 하나씩 풀 생각이다.
    하루 3~40분씩 여유를 내서 알고리즘 문제 풀 시간이 전체 커리큘럼의 절반쯤 되지 않을까..? 하는 예상을 하고 있어서 더도 말고 덜도말고 50문제로 잡았다.

또한 목표까지는 아니지만 동료들과 서로 좋은 관계로 발전하고, 유지해나가려고 노력하려 한다.
될 관계는 어떻게든 된다.. 라고 생각하고 인간관계에 대해 큰 투자를 하고 살지는 않았는데, 최근들어 사람간의 관계도 깊은 노력이 들어가야만 유지되고 좋아질수 있다는 것을 느꼈다.

아직 일주일도 채 지나지 않았지만, 여태까지는 자는 시간만 빼고 다 개발에 투입했다.
들어오자마자 바로 미니 프로젝트가 있어서 조금 무리해가면서까지 오래 작업했는데, 이런 생활이 지속가능하지는 않다고 생각해서 오늘은 잠도 좀 더자고, 운동도 시작했다.
몸은 힘들어도 이런 몰입감을 느낀것이 오랜만이라, 마음은 굉장히 기쁘다. 같은 시간을 사용해도 시간대비 효율이 집이나 도서관등에 비해서 엄청나게 좋아진것을 느낀다. 계속 이 수준으로 유지할 수 있을지는 모르겠다만, 가용한 만큼은 최대한으로 생산성을 내려고 노력하려 한다.

굳이 끝난 이후의 취업이나, 그 즈음에 나의 모습이라던가.. 를 생각하지는 않으려 한다.
발 닿는데로 가면 어디든 가 있으리라 생각하고, 눈앞의 일부터 처리하기 바쁘다 생각한다.

이번주차는 파이썬을 이용해 정렬, 재귀, 완전탐색 등 기본 알고리즘을 공부하는 주차인데, 파이썬을 사용한 것이 오랜만이다보니 문법적인 부분에서 어려움을 좀 겪었다.(물론 알고리즘 자체도 머리 깨져가며 풀었다...)

그런의미에서 문제를 풀며 만난 익숙치 못한 문법이나 라이브러리 사용법들을 정리하고 글을 끝마치려 한다.

타입 어노테이션

def solve(a:list) -> int:
    return sum(a)```
  • 코드의 가독성과 안정성을 높이기 위해 함수의 매개변수와 리턴값에 대한 타입 정보를 지정합니다
  • 직접 테스트를 해보니 a에 리스트가 아닌 다른 타입의 값을 넣는다고해서 typeError가 발생하지는 않았다.

매개변수의 타입 명시시, 다음과 같이 기본값을 줄 수도 있다

def 함수이름(매개변수1: 데이터타입1, 매개변수2: 데이터타입2 = 기본값) -> 리턴타입:
 # 함수 내용

이렇게 매개변수2처럼 기본값을 지정해주면 함수 호출시 인자를 주지 않아도 기본값으로 함수 계산을 수행한다.

굳이 함수가 아니더라도 변수명: 타입 = 값 이렇게 변수에도 타입을 설정해줄 수 있다.

x: int = 1
y: float = 0.1
z: str = "hello"

print(x, y, z, sep = '\n')

출력---------------------
1
0.1
hello

변수의 사용범위

백준 "재귀함수가 뭔가요"를 예시로 설명합니다.

import sys

n = int(sys.stdin.readline())

print("어느 한 컴퓨터공학과 학생이 유명한 교수님을 찾아가 물었다.")
depth = 0 # 전역변수

def whatIsRecursion(depth, n):
    print("____"*depth + "\"재귀함수가 뭔가요?\"")
    if depth == n: # 이 depth는 함수의 지역변수
        print("____"* depth + "\"재귀함수는 자기 자신을 호출하는 함수라네\"")
        return


    print("____"*depth + "\"잘 들어보게. 옛날옛날 한 산 꼭대기에 이세상 모든 지식을 통달한 선인이 있었어.")
    print("____"*depth + "마을 사람들은 모두 그 선인에게 수많은 질문을 했고, 모두 지혜롭게 대답해 주었지.")
    print("____"*depth + "그의 답은 대부분 옳았다고 하네. 그런데 어느 날, 그 선인에게 한 선비가 찾아와서 물었어.\"")

    depth += 1
    whatIsRecursion(depth, n)
    
    print("____"*depth + "라고 답변하였지.")

whatIsRecursion(depth, n)
print("라고 답변하였지.")

여기서 함수 안의 depth와 함수 밖의 depth는 이름은 같지만 다른 변수입니다.
바깥의 depth는 스크립트 전역에서 접근할 수 있는 전역변수이고, 안의 depth는 함수 내에서 사용되는 지역변수입니다.

현재 함수에서는 depth를 하나씩 증가시키며 n과 비교하고 있는데, 이때 depth가 하나씩 증가되는 것이 유지되는 이유는 재귀호출시 함수의 인자로 증가된 depth가 들어오기 때문입니다. 따라서 만약에 이 코드에서 매개변수의 depth를 뺀다면...

UnboundLocalError: cannot access local variable 'depth' where it is not associated with a value 

값이 지정되지 않은 전역변수가 아닌 "지역변수 n"에 접근할수 없다고 합니다.

따라서 만약 함수 밖 상단의 depth = 0 을 사용하고 싶다면
global depth
로 전역변수 depth를 사용하겠다고 알려야 합니다.

import sys

n = int(sys.stdin.readline())

print("어느 한 컴퓨터공학과 학생이 유명한 교수님을 찾아가 물었다.")
depth = 0

def whatIsRecursion(n):
    global depth
    print("____"*depth + "\"재귀함수가 뭔가요?\"")
    if depth == n:
        print("____"* depth + "\"재귀함수는 자기 자신을 호출하는 함수라네\"")
        return


    print("____"*depth + "\"잘 들어보게. 옛날옛날 한 산 꼭대기에 이세상 모든 지식을 통달한 선인이 있었어.")
    print("____"*depth + "마을 사람들은 모두 그 선인에게 수많은 질문을 했고, 모두 지혜롭게 대답해 주었지.")
    print("____"*depth + "그의 답은 대부분 옳았다고 하네. 그런데 어느 날, 그 선인에게 한 선비가 찾아와서 물었어.\"")

    depth += 1
    whatIsRecursion(n)
    
    print("____"*depth + "라고 답변하였지.")
    depth -= 1 # 전역변수인 depth를 감소시켜줘야 함

whatIsRecursion(n)
print("라고 답변하였지.")

그리고 위처럼 depth를 재귀호출의 깊이가 줄어들때부터 감소시켜줘야만, 문제가 원하는데로 ____가 늘어났다가 다시 줄어듭니다.
만약 해당 depth -= 1코드가 없다면, 전역변수인 depth가 계속 3으로 유지되기 때문에 문제의 의도와 다른 결과값이 나오게 됩니다.

자주 사용되지는 않겠다만, 함수 안에서 함수를 만들고 안의 함수에서 상위 함수의 지역변수를 사용할 수도 있습니다.

import sys

n = int(sys.stdin.readline())

print("어느 한 컴퓨터공학과 학생이 유명한 교수님을 찾아가 물었다.")

def artificial_depth():
    depth = 0
    def whatIsRecursion():
        nonlocal depth # 상위 함수의 depth를 사용 
        global n
        print("____"*depth + "\"재귀함수가 뭔가요?\"")
        if depth == n:
            print("____"* depth + "\"재귀함수는 자기 자신을 호출하는 함수라네\"")
            return


        print("____"*depth + "\"잘 들어보게. 옛날옛날 한 산 꼭대기에 이세상 모든 지식을 통달한 선인이 있었어.")
        print("____"*depth + "마을 사람들은 모두 그 선인에게 수많은 질문을 했고, 모두 지혜롭게 대답해 주었지.")
        print("____"*depth + "그의 답은 대부분 옳았다고 하네. 그런데 어느 날, 그 선인에게 한 선비가 찾아와서 물었어.\"")

        depth += 1
        whatIsRecursion()
        
        print("____"*depth + "라고 답변하였지.")
        depth -= 1
    whatIsRecursion()
    


artificial_depth()
print("라고 답변하였지.")

이렇게 상위에 함수를 하나 만들어주고, nonlocal로 상위함수의 depth 지역변수를 사용하며 문제를 풀수도 있습니다.
조금 이상하긴 하네요.

이 nonlocal은 상위 함수들 중 , 가장 가까운 함수의 지역변수부터 먼저 찾습니다.

import sys

n = int(sys.stdin.readline())

print("어느 한 컴퓨터공학과 학생이 유명한 교수님을 찾아가 물었다.")
def depth3():
    depth = 1
    def artificial_depth():
        depth = 0
        def whatIsRecursion():
            nonlocal depth
            global n
            print("____"*depth + "\"재귀함수가 뭔가요?\"")
            if depth == n:
                print("____"* depth + "\"재귀함수는 자기 자신을 호출하는 함수라네\"")
                return


            print("____"*depth + "\"잘 들어보게. 옛날옛날 한 산 꼭대기에 이세상 모든 지식을 통달한 선인이 있었어.")
            print("____"*depth + "마을 사람들은 모두 그 선인에게 수많은 질문을 했고, 모두 지혜롭게 대답해 주었지.")
            print("____"*depth + "그의 답은 대부분 옳았다고 하네. 그런데 어느 날, 그 선인에게 한 선비가 찾아와서 물었어.\"")

            depth += 1
            whatIsRecursion()
            
            print("____"*depth + "라고 답변하였지.")
            depth -= 1
        whatIsRecursion()
    artificial_depth()
    


depth3()
print("라고 답변하였지.")

nonlocal이 depth3()함수의 지역변수 depth가 아닌, 가장가까운 aritificial_depth()의 depth에 접근하는 것을 알 수 있습니다.

더불어 함수의 단계와 상관없이 global()을 사용하면 무조건 전역변수를 사용합니다.

비록 global이 존재하지만, 코드가 복잡해졌을때 전역변수를 사용하면 변수의 값을 어디서 변경하는지 알기 힘들기 때문에 함수에서 값을 주고받을때에는 매개변수와 반환값을 사용하는 것이 권장됩니다.

permutaion, combination

백준 "블랙잭" 문제에서 itertools의 combinations를 사용했는데, 앞으로 사용할 일이 많을듯 해 정리합니다

import sys
from itertools import combinations

n, m = map(int, sys.stdin.readline().split())

cards = list(map(int, sys.stdin.readline().split()))

combi = list(map(lambda x:sum(x), list(combinations(cards,3))))
combi.sort()

for i in range(len(combi)-1):
    if combi[i] == m or combi[i+1] > m:
        print(combi[i])
        exit(0)

print(combi[-1])

combination(cards, 3)을 사용하면 카드배열에서 3개를 뽑는 경우의 수(조합)들을 이터레이터로 반환합니다.

이터레이터이기 때문에 combination(cards, 3)를 통해 바로 반복문을 돌려줄 수도 있습니다.

해당 combination() 함수와 permutation()함수 모두 리스트, 튜플, 딕셔너리 와 같은 이터러블 모두에 적용할 수 있습니다.

map(function, iterable, *iterables)

이 combination()으로 나온 이터러블에 map을 적용해줍니다
map()은 위와 같이 iterable의 각 요소에 첫번째 인자인 function을 적용합니다. iterable이 여러개 올 수도 있는데, function은 각 iterable에 평행(parallel)하게 적용되다가, 가장 짧은 iterable이 끝나면 멈추게 됩니다.

map은 이터레이터인 "map"클래스를 반환하는데(원본 리스트를 변경하지 않음), 이를 리스트로 변경해서 사용해줍니다.

profile
https://de-vlog.tistory.com/ 이사중입니다

0개의 댓글