○ 함수는 과정을 가리키는 이름이다
이 블로그에서는 "프로그램 = 변수 + 함수"라는 등식을 계속 강조하고자 한다. 프로그램은 변수와 함수로 구성된다. 요즘의 정보기술이 기반하고 있는 객체(Object) 기술이라는 것도 결국은 변수와 함수를 하나로 묶는 것에 지나지 않는다. 아, 물론 그 단순한 "묶음"을 통해 어마어마한 장점을 얻기는 하지만 말이다.
함수는 영어로 function 또는 method 라고 부른다. 변수가 어떤 데이터의 이름이었다면, 함수는 어떤 연산(또는 연산의 과정)의 이름이다. 참고로, 컴퓨터에서 변수나 함수를 가리키는데 사용되는 이름을 identifier(식별자)라고 부른다.
사용자가 정의하는 모든 식별자는 변수에 대한 이름이거나 함수에 대한 이름이 된다. 이 2개 간에 형태상의 차이가 있는데, 이름 뒤에 괄호가 있으면 그 이름은 함수에 대한 이름이고, 괄호가 없으면 변수에 대한 이름이다. 예를 들어, a 라는 이름이 괄호와 함께 a( ) 처럼 사용되면, 이 이름 a 는 함수를 가리키는 이름이 된다.
아직 양치질을 하지 못하는 아기 컴퓨터가 있다고 하자. 이 아기 컴퓨터에게 양치질을 처음으로 가르치려고 한다. 양치질을 가르친다? 뭘 가르치게 되나? 양치질의 절차 (procedure) 를 가르치게 된다.
아기 컴퓨터에게 이렇게 가르친다. "양치질을 어떻게 하느냐 하면 말이지..."
먼저, 치약을 들고 뚜껑을 열어.
그 다음에 치솔을 들고
* (혹시 아기 컴퓨터가 잘못하면 조정해 주기도 한다.) 아니지, 치약은 왼손에, 치솔은 왼손에 드는게 편하지
그 다음에, 치약 튜브에 힘을 가해서 치약을 조금 나오게 한 다음에 그 치약을 치솔에 묻혀
그리고 나서 이제, 치솔을 오른손으로 쥐고 윗니도 닦고, 아랫니도 닦고
그 다음에 물을 머금고 헹군다음 물을 뱉어
마지막을 치솔을 씻고 세면대를 정리하면 돼
처음에 아기 컴퓨터가 양치질을 "어떻게 하는지"을 모를 때에는 일일이 하나하나 모두 가르쳐야 한다. 하지만, 아기 컴퓨터가 양치질을 하는 법을 알게 되면, 그 다음 부터는 우리가 너무 편해진다. 예전처럼 일일이 지시하고 조정할 필요가 없이 "양치질 해~" 라고만 하면 된다.
아기 컴퓨터에게 무엇을 가르친 것인가? 저 복잡한 과정의 이름이 "양치질"이라는 것을 가르친 것이다. 이제 "양치질"하라는 명령이 떨어지면 아기 컴퓨터는 "양치질 하는 절차" 에 따라 양치질을 하게 된다.
컴퓨터가 해주는 연산은 매우 단순하고 기본적인 것이다. 우리가 꼭 필요한 새로운 연산(또는 연산의 과정)이 있다면 우리가 직접 만들어야 한다. 그것이 함수이다. 여기서 과정이란 말의 의미는 "하나의 연산으로는 끝낼 수 없는 복잡한 연산"이라는 뜻이고, 더불어 "일정한 명령어들이 일정한 순서에 따라 이루어져야 한다"는 것을 나타낸다.
○ 간단하게, 함수를 하나 만들어 보자
다음은 키보드로 부터 input( ) 받은 3개의 정수를 더해서 출력해 주는 "과정" 이다. 아래의 코드를 보자.
이 과정은 전체 8개의 명령문으로 구성되어 있으며, 3개의 숫자를 input( ) 받아 더해주는 연산이다. 이 연산에, 이 과정에, 이 기능에, 이 절차에... 이름을 하나 부여해 보자. 어떤 과정을 "하나의" 함수로 지정하는 것은 def 라는 키워드를 사용한다. def 키워드는 define의 줄임말이다.
def 함수명 ( ) :
위의 코드를 add 라는 함수이름으로 정의해 보면 아래와 같다. 참고로, 어떤 이름(식별자) 뒤에 괄호가 있으면 그 이름은 함수의 이름이다. 참고로, 함수의 이름이다 보니 add 라는 이름 뒤에 괄호를 붙였다. 괄호 뒤에 콜론 ( : ) 이 보이고, 이 함수에 포함되는 명령어들은 1탭(TAB) 만큼 들여쓰기 되어 나타난다.
여기서 함수의 이름에 해당하는 부분을 함수의 signature 라고 부르고, 콜론 뒤에 정의된 명령어(들)을 함수의 body 라고 부른다.
자, 이제 우리 아기 컴퓨터에게 양치질하는 법을 알려줬다. 이제 양치질을 시켜 보자~
(def 키워드를 이용해) 이미 선언된 또는 정의된 함수를 실행할 때에는 그 함수의 이름 자체가 하나의 명령문이 된다. 예를 들어, 위에서 정의된 add( ) 함수를 실행시켜 보자. In[2]에서 보는 것 처럼, 셀에다 함수의 이름을 적으면 된다. add( ) 라고 씌어졌는데 무슨 뜻인가? 파이썬에게 "(add( )함수에서 정의된 절차에 따라) add 하라"는 뜻이다.
일단 이렇게 3개의 입력 숫자를 더하는 과정을 add( )함수로 정의했으면 필요할 때 마다 add( ), add( ), add( ) 하면 된다. 코드의 재사용(reuse)이다. 한번 정의하고 여러번 사용할 수 있다.
○ 함수는 서비스(service)의 일종이다
함수는 하나의 서비스 (service)로 이해할 수 있다. 하나의 서비스를 기준으로, 서비스를 제공하는 측이 있고, 서비스의 제공을 요청하는 쪽이 있다. 여기서 서비스를 제공하는 측을 서버(Server)라고 부르고, 서비스의 제공을 요청하는 측을 클라이언트(Client)라고 부른다.
클라이언트가 어떤 서비스가 필요하게 되면, 보통은 그 서비스를 제공하고 있는 (또는 제공할 수 있는) 서버를 검색하고, 그 서버에게 (정해진 양식에 맞추어) 원하는 서비스를 요청(request) 하게 된다. 프로그래밍 언어에서는 보통 호출(call, invoke) 라는 용어를 사용한다. 요청은 서버에게 전달되고, 사전에 정의된 절차에 따라 요청이 처리(processing) 된다. 처리가 완료되면 그 처리 결과를 반환(return) 하게 되는데 이를 응답(response)이라고 부른다.
우리 블로그에서는 함수를 호출한다(call), 처리후 결과를 반환한다(return) 라는 표현을 주로 사용하도록 하겠다. 나중에, 객체 또는 모듈을 공부하게 되면 일반적인 서비스와 함수는 똑같은 의미가 되는 것을 알게 될 것이다.
○ 함수의 정의(선언)
우리가 위에서 살펴봤던 add( ) 함수를 다시 한번 더 살펴보자. sum 값에 더해질 3개의 값, b, d, f 가 키보드로 부터 입력되고 있다. 사실, 우리가 파이썬에게 "더하시오" 라고 호출하면 add( ) 함수가 실행되게 된다.
이번에는 조금 다르게 파이썬에게 "2, 5, 3 값을 더하시오"라고 명령을 주는 경우를 생각해 보자. 위의 경우와는 다르게 "더하시오"라는 동사에 "2, 5, 3을"이라는 목적어가 추가되어 있다. 이 "무엇(들)을" 을 함수의 인자라고 부르고 이 값들은 함수의 이름 뒤에 붙은 괄호 안에 표시하게 된다. 2개 이상의 인자가 있으면 쉼표( , )를 이용해 구분한다. 예를 들어서, "2, 5, 3 값을 더하시오" 라는 명령어는 add(2, 5, 3)으로 표현한다는 뜻이다.
이렇게 3개의 값을 더하는 add( ) 함수는 어떤 형태로 정의될 수 있을까? Client가 보내주는 3개의 값이 순서를 가지고 전달되게 된다. 그래서 첫번째 값, 두번째 값, 세번째 값으로 구별할 수 있고, 첫번째 값을 a, 두번째 값을 b, 세번째 값을 c 라고 부르게 되면 이 함수의 signature는 add(a, b, c)와 같이 표현될 수 있다. 만약에 첫번째 값을 x, 두번째 값을 y, 세번째 값을 z라고 부르겠다면 이 함수의 signature는 add(x, y, z)가 되겠다.
아래는 그와 같은 의미로 정의된 add( ) 함수이다. 위에서 봤던 코드 보다는 많이 단순해 진 느낌이다. 당연하게도, 위의 add( ) 함수는 더해야 할 데이터를 input( ) 받는 부분이 코드에 포함되어 있었지만 (즉, 데이터를 스스로 만들었지만), 아래의 add(a, b, c) 함수는 더해야 할 데이터가 함수 밖에서 생성되어 전달되어 오는 차이가 있기 때문이다.
자, 다시 한번 정리해 보자. 함수 내에서 다루는 데이터들 중에 일부 데이터는 외부 (즉, 요청하는 클라이언트) 로 부터 전달되어오는 데이터가 있다. 그를 제외한 나머지 데이터는 함수 내부에서 (함수의 필요에 따라) 만들어지는 데이터이다. 이 중에서 외부로 부터 전달되어지는 데이터는 함수의 인자로 정의되어야 한다.
위의 add(a, b, c) 함수는 이렇게 해석할 수 있다. 우선 signature인 add(a, b, c)로 부터
- 함수의 이름은 add 이다
- 함수를 호출할 때 3개의 데이터가 호출과 함께 전달되어야 한다.
- 전달되는 3개 인자 중, 첫번째로 전달된 데이터를 함수 내부에서는 a 값이라고 부를 것이다. 두번째 데이터는 b값 이라고 부르고, 세번째 전달되는 데이터는 c값 이라고 부를 것이다.
함수 body 중, sum=a+b+c 명령어는
a값 (인자로 전달된 첫번째 값), b값(인자로 전달된 두번째 값), 그리고 c값(인자로 전달된 세번째 값)을 더한 값을 sum 변수에 저장하시오
라는 뜻이다.
○ 함수의 호출
add 함수를 실제 호출한 예를 한번 보자.
In[10]은 "add( ) 함수는 함수 호출과 함께 전달되는 3개의 데이터 a, b, c에 대하여 그 값으로 부터 a+b+c를 연산한 후 결과를 sum 변수에 저장하고 마지막으로 "Sum=" 이라는 문자열과 함께 sum 값을 출력" 하는 함수임을 정의, 또는 선언하고 있다. 이것은, add( ) 함수를 호출하기 위해서는 반드시 3개의 데이터가 함께 전달되어야 한다는 것을 선언한 것이라고 볼 수 있다.
In[11]과 In[12]에서 add( ) 함수를 호출하는 것을 보여 주고 있는데, 함수의 이름 자체가 하나의 명령어가 된다는 것을 알수 있다. 그리고, 3개의 값이 괄호 안에 나타나 있는 것을 확인할 수 있다. In[11]에서는 add(1, 2, 3)이라고 했는데, 이는 우리 말로 "1과 2와 3을 더하시오" 라는 호출이 되는 셈이다. 이 호출이 이루어질 때에는 아래 그림과 같은 매칭이 이루어진다.
즉, 이 호출이 이루어질 때에는 a값이 1이 되고, b 값은 2가 되고, c값은 3이 된다는 뜻이다. 그렇다면, 결국 add( ) 함수 내부에서 실행되는 sum=a+b+c 명령어는 (이 호출의 경우에는) sum=1+2+3 으로 실행된다는 뜻이다.
In[12]에서의 호출은 아래의 그림처럼 생각할 수 있겠다.
즉, 함수의 정의에서 add(a, b, c)와 같이 인자가 변수로 선언됨으로써 여러 경우의 호출에 모두 응답할 수 있게 된다. 즉, 1, 2, 3 만을 더하는 함수가 아니라, 20, 10, 50 만을 더하는 함수가 아니라 임의의 값 a, b, c에 대해 그 값의 합을 구하는 기능을 수행할 수 있게 되는 것이다. 이런 의미에서 함수의 정의에서 사용된 a, b, c와 같은 변수들을 특별히 매개변수(parameter) 라고 부르고 함수는 모든 경우를 처리할 수 있도록 일반화(parameterized) 되게 정의하는 것이다.
참고로, 함수 호출에 사용된 실제 값들은 인자(argument) 라고 부른다.
○ 우리는 이미 함수에 익숙하다
모든 연산에는 연산자(operator)와 피연산자(operand)가 있다. 우리가 잘 아는 두 수의 덧셈을 한번 살펴보자
a+b : "a 에다 더하시오, b를" (연산자가 중간에 있다. 중치방식이라고 부른다)
a b + : "a와 b를 더하시오" (연산자가 마지막에 있다. 후치방식이라고 부른다)
+ a b : "더하시오, a 와 b를" (연산자가 맨 앞에 있어 전치방식이라 부른다)
중치방식은 수학에서 흔하게 보던 방식이고, 후치방식은 우리나라 언어의 방식이다. 전치방식은 영어에서 사용하는 스타일인데 함수도 이런 형태를 가지게 된다
예를 들어서, 현재 우리가 + 라는 심볼로 나타내는 덧셈을 add 라는 심볼로 나타낸다고 해 보자. 그렇다면, 중치 방식은 a add b, 후치방식은 a b add, 전치방식은 add a b가 된다. 전치방식을 연산자와 피연산자를 구별하기 위하여 괄호라는 기호를 사용해서 add(a, b)라고 쓴다고 생각하면 된다.
사실 우리가 함수라는 말을 처음 듣는 것은 아니다. 중학교와 고등학교 수학수업에서 수도 없이 듣던 이름이 함수 아니던가? 함수란 어떤 값(x)을 다른 어떤 값(y)로 매핑(mapping) 시켜주는 장치 또는 메커니즘을 말한다. f : x→y 라고 나타내기도 하고 y=f(x) 라고 표현하기도 한다. x 라는 값이 f 라는 과정을 통해 y 라는 값으로 변환되는 것을 나타낸다.
만약 "a와 b 값을 add 하게 되면 결과적으로 c 값이 만들어진다" 라고 한다면 이를 c = add(a, b)로 표현할 수가 있다는 뜻이다. a와 b 값이 add 라는 과정을 통해 c 값으로 변환된다는 뜻으로 해석할 수 있다.
-------------------------------------
함수는 소프트웨어를 구성하는 가장 기본적인 단위(building block)이다. 따라서, 함수에 대한 이해는 매우 중요하다. 하나의 글에서 마무리하기 힘들어, 함수에 관한 내용은 [함수의 인자], [함수의 반환값], [변수의 범위] 에서 계속 이어집니다.
'파이썬 (python)' 카테고리의 다른 글
[python 17] 함수 - Part 3 : return (0) | 2020.03.25 |
---|---|
[python 16] 함수 - Part 2 : 인자 (0) | 2020.03.25 |
[python 14] 파이썬 문자열 String (0) | 2020.03.25 |
[python 13] 데이터 구조 - 튜플, 딕셔너리 (1) | 2020.03.16 |
[python 12] 리스트(list) 데이터 구조 (0) | 2020.03.16 |