[코드트리 조별과제] Python 11, 12. 함수, 재귀함수

김지민·2024년 8월 15일

1. 기본 함수 정의 및 형태

 def print_5_stars():
    print("*" * 5)


for _ in range(4):
    print_5_stars()

def print_n_lines(n): # n: 함수의 인자
    코드 작성

def print_rect(n, m):
    코드 작성

def add(a, b):
    return a + b

add(a,b) # 함수 호출

def add(a, b, c=0): # 기본값 설정해주기
	return a + b + c  # 함수는 `return` 을 만나게 되면 그 즉시 종료
  • 함수와 함수 혹은 다른 코드와의 줄 간격은 일반적으로 2줄을 띄기
  • 의미 단위로 _를 붙여 구분해주는 snake case
  • 도전문제: 최소공배수 구하는 문제
k = list(map(int, input().split()))
n, m = k[0], k[1]

def guess_min(n,m):
    a = 1
    x, y = 0, 0
    i = 2

    while i <= min(n,m):
        x = n % i
        y = m % i
        if x == 0 and y == 0:
            n = n // i
            m = m // i
            a = a*i
            # 같은 i로 실행되게 해야 돼
        else:
            i += 1
    a = a*n*m
    print(a)
   
guess_min(n,m)
  • 모범답안: 최소공배수 = a*b / (a와b의 최대공약수)
# 변수 선언 및 입력:
n, m = tuple(map(int, input().split()))

# n과 m의 최소공배수를 출력합니다.
def find_lcm(n, m):
    gcd = 0
    for i in range(1, min(n, m) + 1):
        if n % i == 0 and m % i == 0:
            gcd = i

    print(n * m // gcd)
   

find_lcm(n, m)
  • 참고: 그렇게 되는 이유
    ![[Pasted image 20240804233647.png]]

  • 여러수의 최소공배수.. 접근방법 팁 (스포주의)

  • 기본적인 두 수의 최대공약수, 최소공배수를 구하고

두 수 a, b의 최대공약수 gcd
두 수 a, b의 최소공배수 lcm = (a * b) // gcd
  • 처음에 잘못된 접근방법
세 수 a, b, c의 최대공약수 = 두 수 a, b의 최대공약수인 g와 c의 최대공약수 gcd
세 수 a, b, c의 최소공배수 = (a // gcd) * (b // gcd) * (c // gcd) * gcd

샘플케이스는 통과 하지만 간단하게 arr = [2,3,4] 이런 경우만 생각해봐도
리턴값은 12가 나와야하지만, 위의 방식으로 접근하면24라는 잘못된 값이 나오는걸 알 수 있습니다.

  • 제대로된 접근방법
세 수 a, b, c의 최소공배수 = 두 수 a, b의 최소공배수인 l과 c의 최소공배수 lcm
    while len(arr) >= 2:
        a = arr.pop()
        b = arr.pop()
        arr.append(lcm(a,b))

리스트에서 맨 뒤 2개의 수를 꺼내 최소공배수를 구하고, 리스트에 추가해주는 방식을 반복
마지막에 arr 리스트에 1개의 값만 있을 경우, 이 값이 n개수의 최소공배수가 됩니다.

단순하게 2개씩 구하면서 넘어가면되는 간단한 문젠데, 저처럼 방법때문에 삽질하시는 분들 계시다면....
참고하셔도 좋을것 같네요 ㅎㅎ

  • 인자가 2개가 넘어오든, 3개, 4개 몇 개가 넘어오든 넘어온 값들의 합을 반환해주는 함수
    - *(asterisk)을 사용하면 인자 개수에 상관없이 tuple이라는 형태로 값들을 받아올 수 있음
def add(*args):
    return sum(args)

>> add(1, 3, 2, 6, 5, 4)
21
  • 고난도 문제 함수로 해결하기
    - 단 하나라도 라는 조건이 들어가는 경우에는, 주로 True / False 값을 갖는 bool 타입의 변수를 하나 선언하여 해결
    - if 조건문으로만 해결하기에는 조건이 상당히 까다로울 때, 함수와 bool type 변수를 통해, return으로 구현

2. Call by value / Call by reference / 두 정수 값 교환하기

  • Python에서는 변수의 type에 따라 immutable과 mutable한 것으로 나뉨
    - ==[Call by value] immutable한 type: tuple, string 그리고 int, bool 등을 포함한 primitive type==
    - immutable 특성을 갖는 변수가 함수의 인자로 넘어가게 되면, 변할 수 없는 특성 때문에 그 변수가 그대로 넘어가는 것이 아닌, 변수가 갖고 있던 값을 복사하여 값을 넘겨줌
    - 함수 안에서 어떠한 일이 일어나더라도 전혀 밖에 있는 변수에 영향을 끼치지 않음
    - 함수 안에서의 역할이 밖에 있는 변수에도 영향을 끼치고 싶다면, 아래와 같이 해당 문자열 자체를 반환하는 함수 작성
    - ==[Call by reference] mutable한 type: list, dict 등==
    - mutable한 변수를 함수 인자로 넘기게 되면 해당 변수 자체의 주소가 넘어가게 됨
    - 함수 안에서 해당 인자에 주는 모든 변화가 고스란히 함수 호출시 넘겼던 변수에 남게 됨
    -  함수로 값을 넘길때 다음과 같이 slicing을 하게 되면 기존 리스트와 동일한 원소를 갖는 새로운 리스트를 만들어 값을 넘겨주면 이거 방지 가능
# n과 m의 값이 바뀌지 않음
def swap(a, b):
    a, b = b, a
    print(a, b)


n, m = 10, 20
swap(n, m)
print(n, m)
	
# 바뀜
def swap(a, b):
    a, b = b, a
    return a, b

n, m = 10, 20
n, m = swap(n, m)
print(n, m)

>> 20 10
def modify(arr): # arr는 _list와 관련이 없다.
    arr[0] = 10


_list = [1, 2, 3, 4]
modify(_list[:]) # 새로운 리스트를 만들어 넘기기

for elem in _list:
    print(elem, end=" ")

>> 1 2 3 4 # 값에 변화가 없다

3. 변수의 영역

1) 전역 변수(Global variable)

  •  global 변수: 함수 위에 값들을 정의하게 되면, 어디에서도 쓰일 수 있음
    - 함수 안에서 바로 참조 가능
  • local 변수: 함수 안에서 정의한 변수는, 함수를 벗어나면 더 이상 쓰일 수 없음
    - 함수가 종료되는 순간에 변수도 같이 사라지기 때문에, 다른 곳에서 사용이 불가함

modify 함수 안의 _list 변수를 global 변수로 인식하게 하려면, 다음과 같이 함수 안에 global 표식을 해줘야 합니다.

_list = [1, 2, 3, 4]


def modify():
    global _list # global 변수인 _list를 가져다 쓸 것이다.
    _list = [5, 6, 7, 8]


modify()
for elem in _list:
    print(elem, end=" ")

>> 5 6 7 8

num = 10


def modify():
    global num
    num = 5

modify()
print(num)

>> 5
  • 같은 변수 이름을 갖는 경우 가장 가까이에 있는 변수를 사용


1. 재귀함수 형태

이 함수는 print_star(n - 1) 를 먼저 수행하여 1번째부터 n - 1번째 줄까지의 별을 알맞게 출력한 뒤, n번째 줄을 출력해보는 식

def print_star(n):    # 1부터 n번째 줄까지 별을 출력하는 함수
    if n == 0:        # n이 0이라면, 더 이상 진행하지 않고
        return        # 퇴각합니다.

    print_star(n - 1) # 1부터 n - 1번째 줄까지 출력하는 함수
    print("*" * 5)    # n번째 줄에 해당하는 별 출력


print("start")
print_star(3)
print("end")
  • 종료 조건이 무조건 있어야 됨!!!!!!!!!!
    - 값을 반환하지 않는 함수에서, 해당 함수를 종료하고 싶을 때는 값 없이 return 적어주면 됨
def print_star(n):    # 1부터 n번째 줄까지 별을 출력하는 함수
    if n == 0:        # 종료 조건
        return

    print_star(n - 1) # 1부터 n - 1번째 줄까지 출력하는 함수
    print("*" * n)    # n번째 줄에는 n개의 별을 출력합니다.

위의 코드를 실행하면 예상과 동일한 결과를 얻을 수 있게 됩니다.

print_star(3)

>> *
   **
   ***

하지만 다음과 같이 print 함수와 print_star 함수를 호출하는 부분의 순서가 바뀌어있는 경우라면, 어떤 출력 결과를 받게 될지 예상이 되시나요?

def print_star(n):    # 1부터 n번째 줄까지 별을 출력하는 함수
    if n == 0:        # 종료 조건
        return

    print("*" * n)    # n번째 줄에는 n개의 별을 출력합니다.
    print_star(n - 1) # 1부터 n - 1번째 줄까지 출력하는 함수

바로 뒤집어진 삼각형 모양을 받게 됩니다. 그 이유는 1부터 n - 1번째 줄까지 출력을 진행하기 전에, 먼저 n개의 별을 출력하였기 때문입니다.

print_star(3)

>> ***
   **
   *

한 가지 예시를 더 살펴보겠습니다. 다음 코드를 실행하면 어떤 결과가 나오게 될까요?

def print_star(n):  
    if n == 0:      
        return

    print("*" * n)
    print_star(n - 1)
    print("*" * n)

바로 두 직각삼각형이 위아래로 붙어있는 모양을 받아보게 됩니다. 크게 봤을 때 상상을 해보면, print_star(n - 1)을 실행하기 직전에 n개의 별을 출력하고, n - 1개를 처리한 이후에 다시 n개의 별을 출력하므로 위아래로 n개를 출력하고 계속 안쪽으로 별을 채워넣어가는 모양이 될 것이라 예상할 수 있습니다.

print_star(3)

>> ***
   **
   *
   *
   **
   ***

이렇듯 재귀함수에서는 print문을 어디에 정의하는지에 따라 예상과는 아예 다른 결과를 받아보게 됩니다.
-> 너무너무 헷갈림!!!!!!!!!!

2. 값을 반환하는 재귀함수

 - 값을 반환하는 재귀함수에서의 종료조건은 지극히 자명한계산 없이도 바로 결과를 알 수 있는 경우로 설정
- 값을 return해도 종료조건이 따로 있어야 됨

  • 팩토리얼 함수
    - 1부터 n까지의 곱을 반환
def fact(n):
    if n == 1:
        return 1

    return fact(n - 1) * n


print(fact(3))

>> 6
  • 점화식
    - An = An-1 + 2 * An-2
def f(n):
    # 종료 조건
    if n == 1:
        return 2
    if n == 2:
        return 7

    # 점화식
    return f(n - 1) + 2 * f(n - 2)


print(f(4))

>> 25
profile
열혈개발자~!!

0개의 댓글