Lesson 8
함수와 재귀
1. 기능들
수학에서 n의 계승 (factorial)은 n으로 정의됨을 상기하라
# 3을 계산하라! res = 1 for i in range(1, 4): res *= i print(res) # 5를 계산하십시오! res = 1 for i in range(1, 6): res *= i print(res)
그러나 초기 코드에서 실수를하면이 오류 코드가 계승 계산을 복사 한 모든 위치에 나타납니다. 또한 코드가 될 수있는 것보다 길다. 프로그래밍 언어에서 동일한 로직을 다시 작성하지 않으려면 함수가 있어야합니다.
함수 는 나머지 프로그램과 분리되어 호출 될 때만 실행되는 코드 섹션입니다. 이미 sqrt()
, len()
및 print()
함수를 만났습니다. 그들은 모두 공통점이 있습니다. 매개 변수 (0, 1 또는 그 중 몇 개)를 취할 수 있으며 반환 할 수는 있지만 값을 반환 할 수 있습니다. 예를 들어 sqrt()
함수는 하나의 매개 변수를 허용하고 값 (주어진 숫자의 제곱근)을 반환합니다. print()
함수는 다양한 인수를 취할 수 있으며 아무 것도 반환하지 않습니다.
이제 factorial()
이라는 함수를 작성하는 방법을 보여 드리겠습니다. factorial()
은 하나의 매개 변수 (숫자 및 그 값의 계승 값 factorial()
를 취하는 함수를 작성하는 방법을 보여줍니다.
def factorial(n): res = 1 for i in range(1, n + 1): res *= i return res print(factorial(3)) print(factorial(5))
우리는 몇 가지 설명을하고 싶다. 첫째, 함수 코드는 프로그램 시작 부분에 위치해야합니다 factorial()
정확히 말하면 factorial()
함수를 사용하기 전에). 이 예제의 첫 번째 줄 def factorial(n):
은 우리 함수에 대한 설명입니다. factorial 이라는 단어는 식별자 (우리 함수의 이름)입니다. 식별자 바로 다음에 우리 함수가받는 매개 변수 목록이 있습니다 (괄호 안에). 목록은 매개 변수의 쉼표로 구분 된 식별자로 구성됩니다. 우리의 경우 목록은 하나의 매개 변수 n
구성됩니다. 행 끝에 콜론을 넣으십시오.
그런 다음 함수 본문으로 이동합니다. 파이썬에서 본문 은 반드시 들여 쓰기 되어야 합니다 (항상 Tab 또는 4 개의 공백으로). 이 함수는 n! 변수 res
에 저장합니다. 함수의 마지막 줄은 return res
기능을 종료하고 변수의 값을 반환하는 res
.
return
문은 함수의 모든 위치에 나타날 수 있습니다. 실행은 함수를 종료하고 함수가 호출 된 위치에 지정된 값을 리턴합니다. 함수가 값을 반환하지 않으면 return 문은 실제로는 사용할 수 있지만 실제로는 값을 반환하지 않습니다. 일부 함수는 값을 반환 할 필요가 없으며 return 문을 생략 할 수 있습니다.
우리는 다른 예를 제시하고자합니다. 다음은 두 개의 숫자를 받아들이고 최대 값을 반환하는 max()
함수입니다 (실제로이 함수는 이미 파이썬 구문의 일부가되었습니다).
def max(a, b): if a > b: return a else: return b print(max(3, 5)) print(max(5, 3)) print(max(int(input()), int(input())))
이제 세 개의 숫자를 취하여 최대 값을 반환하는 max3()
함수를 작성할 수 있습니다.
def max(a, b): if a > b: return a else: return b def max3(a, b, c): return max(max(a, b), c) print(max3(3, 5, 4))
파이썬에서 내장 함수 max()
는 다양한 인수를 받아들이고 최대 값을 리턴 할 수 있습니다. 다음은 그러한 함수를 작성하는 방법의 예입니다.
def max(*a): res = a[0] for val in a[1:]: if val > res: res = val return res print(max(3, 5, 4))
a
라는 단일 튜플에 매개 변수를 수집합니다.
2. 로컬 및 전역 변수
함수 내부에서 선언 된 변수를 사용할 수 있습니다.
def f(): print(a) a = 1 f()
여기서, 변수 a
1로 설정되고 기능한다 f()
우리가 선언 될 때 기능한다는 사실에도 불구하고,이 값을 출력 f
이 변수가 초기화되어 있지 않습니다. 그 이유는 함수 f()
(마지막 문자열)를 호출 할 때 변수 a
이미 값을 가지고 있기 때문 입니다. 그래서 f()
함수가이를 표시 할 수 있습니다.
이러한 변수 (함수 밖에서 선언되었지만 함수 내에서 사용 가능)는 전역 변수라고합니다.
그러나 함수 내부에서 일부 변수를 초기화하면이 변수를 변수 외부에서 사용할 수 없습니다. 예 :
def f(): a = 1 f() print(a)
NameError: name 'a' is not defined
오류 NameError: name 'a' is not defined
. 함수 내에서 선언 된 변수는 local 이라고합니다. 이 기능을 종료하면 사용할 수 없게됩니다.
여기서 정말 매력적 인 부분은 함수 내부에서 전역 변수의 값을 변경하면 어떻게 될까요?
def f(): a = 1 print(a) a = 0 f() print(a)
이 프로그램은 숫자 1과 숫자 0을 출력합니다. 변수 a
의 값이 함수 내부에서 변경되었지만 함수 외부에서는 변경되지 않습니다. 이는 의도하지 않은 함수 변경으로부터 전역 변수를 보호하기 위해 수행됩니다. 따라서 함수 내에서 일부 변수가 수정되면 변수는 지역 변수가되고 수정하면 동일한 이름을 가진 전역 변수가 변경되지 않습니다.
보다 공식적으로 : 파이썬 인터프리터는이 함수의 코드에서 변수의 값을 수정하는 명령어가 하나 이상있는 경우 함수의 로컬 변수를 고려합니다. 그런 다음 해당 변수도 초기화 전에 사용할 수 없습니다. 변수의 값을 수정하는 명령어 - =
, +=
연산자 및 변수의 사용법 for
매개 변수의 루프로 사용합니다. 그러나 변수 변경 문이 실행되지 않더라도 인터프리터는이를 확인할 수 없으며 변수는 여전히 로컬입니다. 예 :
def f(): print(a) if False: a = 0 a = 1 f()
UnboundLocalError: local variable 'a' referenced before assignment
. 즉, 함수 f()
에서 변수 a
를 수정하는 명령이 함수에 포함되어 있으므로 식별자 a
는 지역 변수가됩니다. 변경 명령은 절대로 실행되지 않지만 인터프리터는이를 확인하지 않습니다. 따라서 변수 a
를 인쇄하려고하면 초기화되지 않은 지역 변수에 호소합니다.
함수가 일부 변수를 변경할 수있게하려면 global
변수 키워드를 사용하여 함수 내에서이 변수를 선언해야합니다.
def f(): global a a = 1 print(a) a = 0 f() print(a)
이 예에서는 변수 때문에, 1 (1)의 출력을 인쇄 할 수 a
글로벌로 선언하고, 내부 기능을 변경하는 것이 세계적으로의 변화를 야기한다.
그러나 함수 내부의 전역 변수 값을 수정하지 않는 것이 좋습니다. 함수가 일부 변수를 변경해야하는 경우이 값을 리턴 하게하고 함수를 호출 할 때이 값에 변수를 명시 적으로 지정할 때 선택하십시오. 이 규칙을 따르면 기능 로직이 코드 로직과 독립적으로 작동하므로 이러한 기능을 한 프로그램에서 다른 프로그램으로 쉽게 복사 할 수있어 시간을 절약 할 수 있습니다.
예를 들어, 변수 f에 저장하려는 주어진 숫자의 계승을 계산해야한다고 가정하십시오. 다음과 같이하면 안됩니다 .
def factorial(n): global f res = 1 for i in range(2, n + 1): res *= i f = res n = int(input()) factorial(n) print(f) # 변수 f를 사용하여 다른 작업하기
이것은 다른 코드를 사용하기가 어렵 기 때문에 나쁜 코드의 예입니다. 내일 "팩토리얼"기능을 사용하는 또 다른 프로그램이 필요하다면 여기에서이 기능을 복사하여 새 프로그램에 붙여 넣기 할 수 없습니다. 해당 프로그램에 변수 f
포함되지 않도록해야합니다.
이 예제를 다음과 같이 다시 작성하는 것이 훨씬 더 좋습니다.
# 프로그램간에 복사 할 수있는 코드 덩어리의 시작 def factorial(n): res = 1 for i in range(2, n + 1): res *= i return res # 코드 조각 끝 n = int(input()) f = factorial(n) print(f) # 변수 f를 사용하여 다른 작업하기
함수가 둘 이상의 값을 반환 할 수 있다고 말하는 것이 유용합니다. 다음은 두 개 이상의 값 목록 을 반환하는 예제입니다.
return [a, b]
그러한 목록의 기능을 호출하여 다중 할당에 사용할 수 있습니다.
n, m = f(a, b)
3. 재귀
위에서 보았 듯이 함수는 다른 함수를 호출 할 수 있습니다. 그러나 함수 자체를 호출 할 수도 있습니다! 이것을 설명하기 위해 계승 계산 기능의 예를 고려하십시오. 0! = 1, 1! = 1이라는 것은 잘 알려져있다. n의 가치를 계산하는 방법! 큰 n? (n-1)!의 값을 계산할 수 있다면, n! = n⋅ (n-1)!이므로 n!을 쉽게 계산할 수 있습니다. 그러나 (n-1)을 계산하는 방법!? 우리가 (n-2)를 계산했다면, (n-1)! = (n-1) · (n-2) !. (n-2)를 계산하는 방법!? If ... 결국, 우리는 0이된다. 이것은 1과 같다. 따라서, 계승을 계산하기 위해서 계승의 값을 더 작은 정수에 사용할 수있다. 이 계산은 파이썬을 사용하여 수행 할 수 있습니다.
def factorial(n): if n == 0: return 1 else: return n * factorial(n - 1) print(factorial(5))
함수 자체를 호출하는 상황을 재귀 라고하며, 이러한 함수를 재귀라고합니다.
재귀 함수는 프로그래밍의 강력한 메커니즘입니다. 불행히도, 그들은 항상 효과적인 것은 아니며 종종 실수로 이어집니다. 가장 일반적인 오류는 함수 호출 체인이 끝나지 않는 무한 재귀입니다 (실제로 컴퓨터에서 여유 메모리가 부족할 때 종료됩니다). 무한 재귀의 예 :
def f(): return f()무한 재귀를 일으키는 가장 일반적인 두 가지 이유는 다음과 같습니다.
- 정지 조건이 잘못되었습니다. 예를 들어 계승 계산 프로그램에서
if n == 0
인if n == 0
수표를 잊어 버린if n == 0
factorial(0)
은factorial(-1)
을 호출하고factorial(-2)
등을 호출합니다. - 잘못된 매개 변수가있는 재귀 호출 기능 예를 들어,
factorial(n)
함수 호출factorial(n)
, 또한 통화 무한 체인을 얻을 것이다.
따라서 재귀 함수를 코딩 할 때 먼저 중단 조건에 도달하는지 확인해야합니다. 즉 재귀가 끝나는 이유를 생각할 필요가 있습니다.