재귀 함수: 자기 자신의 함수를 함수 내 연속적으로 호출하여 처리하는 함수
합성 함수: 다른 함수를 매개 변수로 받거나 반환하여 처리하는 함수
I. 재귀 함수
- 재귀 함수 사용 시 별도의 순환문을 사용하지 않아도 반복 처리 효과
- 파이썬에서는 재귀 함수를 무한정 사용할 수 없도록 제한
- [예제] 함수 재귀 호출
import timeit def func1(list1) : if len(list1) == 1 : # 마지막 처리 로직으로 무한 수행 방지 return list1[0] head, *tail = list1 # 처리값 감소 위해 첫 번째 원소는 head, 나머지 원소는 tail에 삽입 return head + func1(tail) # tail을 인자로 전달해서 자신을 호출 time1 = timeit.timeit('func1([1,2,3])', setup = "from __main__ import func1", number = 1000000) print("재귀함수 처리 시간:", time1) def func2(list2) : # 재귀 함수 처리 시간과 비교하기 위해 순환문 사용 result = 0 for i in list2 : result += i return result time2 = timeit.timeit('func2([1,2,3])', setup = "from __main__ import func2", number = 1000000) print("순환문 처리 시간:", time2) [결과] 귀함수 처리 시간: 0.8320854999999999 순환문 처리 시간: 0.26898349999999993
- 재귀함수와 순환문 처리 성능 비교 시 재귀 함수 호출 시 함수를 계속적으로 생성하여 처리해야 하므로 순환문 처리 성능이 높음
II. 합성 함수
- 함수도 객체이므로 다른 객체처럼 인자로 전달하여 사용 가능
- [예제] 합성 함수 처리
import math def add(a, b) : # 인자로 전달할 함수 정의 return a + b def power(func, *args, z=None) : # 전달받는 함수와 인자를 매개변수로 정의 result = func(*args) # 인자를 전달 받아 수행 if z : result = math.pow(result, z) # 키워드 인자가 들어오면 합한 값을 제곱 return result print(power(add, 1, 3, z = 2)) [결과] 16.0
III. 재귀 함수 실행 시 객체 이름공간 사용
- 함수도 객체이므로 함수 자체 이름공간과 함수 내부 지역 이름공간이 생성
- 함수 정의 시 생성된 이름공간은 함수 실행 시 공유하여 사용 가능
- [예제] 순환문 없이 순환 수행
def recursion(iterable) : # 하나의 매개변수를 가지는 재귀 함수 if not isinstance(iterable, list) : # 매개변수 자료형이 리스트 클래스가 아니면 종료 return "처리 불가" if not hasattr(recursion, "result") : # 함수 객체에 속성이 없으면 빈 리스트 할당 recursion.result = [] if len(iterable) == 1 : # 반복 원소의 길이가 1이면 recursion.result.append(iterable.pop()) # 마지막 원소를 함수 객체의 속성에 추가 후 종료 return None recursion.result.append(iterable.pop(0)) # 원소를 하나씩 객체 속성에 추가 return recursion(iterable) # 자기 함수 호출 recursion([1,2,3]) print(recursion.result) print(recursion("문자열 처리")) [결과] [1, 2, 3] 처리 불가
[참고]
- 잇플, “한 권으로 개발자가 원하던 파이썬 심화 A to Z”, 2019.11