SturdyCobble's Study Note

[프로그래밍] 4.4 함수와 메소드 본문

휴지통/['19.06-'20.07]프로그래밍101

[프로그래밍] 4.4 함수와 메소드

StudyingCobble 2019. 8. 24. 16:33

NOTICE : 독학인 만큼, 잘못된 내용이 있을 수 있습니다. 내용 지적은 언제나 환영합니다.

더 이상 이 블로그에는 글을 올리지는 않을 예정입니다. 그렇지만 댓글을 달아주시면 최대한 답변드리고자 노력하겠습니다.


※이 글은 프로그래밍 언어에 대한 기초적인 이해를 가정하고 있습니다. 최소 프로그래밍 언어 하나 정도를 약간이라도 접해보시는 것을 추천합니다. 또한, 이 글은 심화 내용은 되도록 피하여 서술했습니다.

(객체 지향과 절차 지향 언어를 동시에 다루기에 메소드를 보다 앞에서 설명하였습니다. 그러나 본 글의 메소드에 대한 내용은 상당히 생략되었습니다. 클래스와 메소드에 관해서는 다음 글에서 더 자세히 다루겠습니다.)

 

 

 함수와 메소드는 모두 특정 동작이나 연산을 진행하게 하는 코드의 일부분으로서, 커다란 프로그램의 부속품과 같은 역할을 합니다. 한번 만들면 반복적으로 불러내어 호출할 수도 있으며, 입력값을 넣어서 값을 얻어내는 것도 가능합니다.

 

 

그러나 메소드(또는 C++의 경우 멤버 함수)는 객체의 일부분으로서 동작하는 차이점이 있습니다. Java의 경우 메인 클래스없이 프로그램이 동작할 수 없으므로, 함수없이 메소드만 존재한다고 볼 수 있습니다. 

 

 함수를 부르는 이름은 다양한데, 프로그래밍 언어에 따라서 그 의미가 달라지기도 합니다. 예를 들어, 프로시저(Procedure)와 함수(Function)은 C/C++에서는 같은 의미이지만, 일부 언어에서는 다른 의미를 나타내어 값을 반환하지 않고 동작만 수행하는 경우 프로시저, 반환값이 존재할 때 함수라고 부릅니다. 비슷하게 서브루틴(Subroutine)이 프로시저를 대신한 언어도 존재합니다. 이 글에서는 이러한 언어를 다루지 않으므로, 함수를 포괄적인 개념으로 사용하겠습니다.

 

 아래는 4가지 언어에서 함수와 메소드를 선언하는 방식을 보여줍니다. 이때, C/C++, Java는 중첩되게 함수나 메소드를 선언하지 못하지만, Python의 경우 가능합니다.(Python의 경우 함수가 객체이므로 함수를 반환하는 것도 가능합니다.) 그렇기에, C/C++의 경우 main함수의 밖, Java의 경우 main메소드 밖, 클래스 안에 정의되어야 합니다.

/*
자료형 함수명(인수1자료형 인수1이름, ...){
  실행 내용;
  return 반환값;     (반환값이 존재하는 경우)
}
*/
//ex:
float avg2(int num1, int num2){ //int num1,num2로 쓸 수 없음
  int sum = num1 + num2
  float avg = sum/2;
  return avg;
}

'''
def 함수명(인수1, 인수2, ...):
    실행내용
    return 반환값 (존재하는 경우)
'''
# EX:
def returnSomething():
    def doNothing(num1, num2):
        pass
    return [10,20]

'''
def 함수명(인수1 : 자료형1, 인수2 : 자료형2, ...) -> 반환형:
    실행내용
    return 반환값
'''

#EX:
def returnInt(num1 : int, num2) -> int:
    return num1

(보시다시피 함수 내에도 내용이 없을 때 pass 키워드를 사용할 수 있습니다.)

 

이때, 함수의 이름은 중복이 가능하며, 이 경우 인수의 갯수나 자료형이 차이가 나서 컴파일러가 구분할 수 있어야 합니다.

 

만약 반환할 값이 없다면 void 자료형을 이용해줍니다.(C/C++, Java)

int num1;
void doSomething(int someNum){
  num1 = someNum;
}

 

 

 

함수는 다음과 같이 사용됩니다. 일반적인 수학 함수와 비슷한 느낌이지만, 반환값이 없을 수 있다는 점에서 약간 다릅니다.

함수명(인수1,인수2, ...)

(단, 인수가 없는 경우 함수명()과 같이 사용)

(선언 부분에 있는 인수는 형식 인수(Parameter), 사용시 사용하는 인수는 실 인수(Argument)라고 부릅니다.)

 

메소드의 사용법은 위와 비슷하나, 클래스 안에서 선언되기 때문에, 몇 가지 지정자가 필요합니다. 메인이 되는 클래스(파일명과 같은 이름의 클래스)에서 사용되는 메소드만을 다룬다면, 다음과 같이 선언하고 사용할 수 있습니다.

public class HelloWorld {
  static void printer(){ //static이란 지정자가 추가되었습니다.
    System.out.println("Hello");
  }
  public static void main(String[] args){
    printer();
    HelloWorld.printer(); //위와 같은 결과
}
}

---------실행 결과-----------
Hello
Hello

(자세한 내용은 이후 다시 다룹니다.)

 

 

아래 예시는 함수의 사용법을 보여줍니다.

def func1(num1,num2):
    print(num1)
    print(num2)
    return num1+num2
print(func1(10,12))
func1(10,12)
------------실행 결과--------------
10
12
22
10
12

이때, return은 값을 반환하는 동시에 함수를 종료하는 역할을 합니다. 따라서 C/C++,Java의 void형 자료형이나 리턴값이 없는 Python함수에서 함수를 종료하기 위해서 반환값 없이 return을 사용할 수 있습니다.

 

 

C/C++, Java에서는 중복된 이름을 가지는 함수(Java의 경우 메소드)가 여러개라면, 다음과 같이 사용할 수 있습니다. 즉, 컴파일러가 봐서 구분가능하면 중복이 가능합니다. (Java의 경우 이러한 방식을 메소드 오버로딩(Method Overloading)이라고 합니다.)

 

#include <iostream>

using namespace std;

int printer(int n1, float n2){
   cout << n1 << "Single";
   return n1+1;
}
int printer(int n1, int n2){
   cout << n1 << n2 << "Double";
   return n1+1;
}


int main() {
   cout<< printer(10,10.1f) << printer(10,12) << endl; 
   //10.2만 쓰는 경우 int로 묵시적 형변환 가능!!
   return 0;
}

-----실행 결과---------
10Single111012Double11

 

 

C/C++에서는 다음과 같이 원형과 본체를 분리하여 선언하는 것도 가능합니다.

float avg2(int num1, int num2); //float avg2(int,int);도 가능

avg2(int n1,int n2){
  int sum = n1 + n2;
  float avg = sum/2;
  return avg;
}

 이때 형식 인수의 명칭은 실 인수와 같을 필요가 없습니다. 따라서 코드의 앞부분에 함수의 역할을 설명하기 위해 긴 이름의 인수명을 써볼 수도 있습니다.(길다는 의미는 전달하고자 하는 의미를 담기 위해 충분히 긴 이름이라는 것이지 그렇다고 너무 길면 가독성만 떨어지겠죠?)

 

단, 함수 이름이 중복되는 경우 오류가 날 수 있습니다.

 

 

한편, 함수 안에서 선언된 변수는 그 안에서만 사용가능합니다. 정확히는 { }또는 들여쓰기(Python)으로 묶인 한 블럭 내에서 통용되며, 이를 지역 변수(Local Variable)이라고 합니다. 자세한 내용은 이후 다시 다룹니다.

 

한편 함수에 들어가는 인수의 값을 미리 설정할 수도 있습니다. 이 경우 인수 값을 초기화 한 인수 이후로는 초기화하지 않은 인수를 쓸 수 없습니다. 이는 일반적인 대입하듯이 하면 됩니다. 이 문법은 Java에는 없는 듯 합니다.

 

int func1(int num1, int num2 = 10, int num3 = 30){
	return num1 + num2 + num3;
}

def func1(num1, num2 = 10, num3 = 20):
    return num1 + num2 + num3

이 경우, 마지막의 초기값이 있는 인수를 생략할 수 있습니다.

 

def func1(num1, num2 = 10, num3 = 20):
    return num1 + num2 + num3
print(func1(10))
print(func1(10,20,30))
--------------실행 결과-------------
40
60

 

Python의 경우 인수를 전달할 때 인수의 이름을 통해 전달하는 다음의 문법도 존재합니다.

def add(num1,num2):
	return num1 + num2
    
print(add(num1 = 10, num2 = 20))

 

 

한편, 인수가 1개 이상인 경우도 분명 존재할 것입니다. 이 경우 배열이나 리스트 등을 이용하는 방법이 있을 수 있지만( 이 부분은 경우에 따라 포인터에 대한 내용을 배워야 하므로 생략합니다.), 다음과 같이 간단하게 가변 인수 함수를 만들 수 있습니다. (단, C/C++의 경우 1개 이상의 고정 인수가 필수적입니다.)

(헤더 파일이 추가된 것에 주의!)

//실행 환경에 따라 바이러스로 오진할 수 있습니다! 메모리를 접근하기 때문인 것 같습니다.
#include <stdio.h>
#include <stdarg.h>

int addAll(int num, ...){
  int sum = 0;
  int varg = num;
  va_list ap;

  va_start(ap,num); // num은 마지막 고정 인수!
  while(varg != -1){
    sum += varg;
    varg = va_arg(ap,int);
  }
  va_end(ap);
  return sum;
}

int main(){
  printf("%d",addAll(2,3,4,1,2,3,-1));  //-1을 끝을 나타내기 위해 사용.
  return 0;
}

 

(C언어와 입출력이나 헤더 파일 부분을 제외하곤 동일)

//실행 환경에 따라 바이러스로 오진할 수 있습니다! 메모리를 접근하기 때문인 것 같습니다.
#include <iostream>
#include <stdarg>

int addAll(int num, ...){
  int sum = 0;
  int varg = num;
  va_list ap;

  va_start(ap,num); // num은 마지막 고정 인수!
  while(varg != -1){
    sum += varg;
    varg = va_arg(ap,int);
  }
  va_end(ap);
  return sum;
}

int main(){
  cout << addAll(2,3,4,1,2,3,-1));  //-1을 끝을 나타내기 위해 사용.
  return 0;
}

public class HelloWorld {
    static int addAll(int... nums) {
      int sum = 0;
        for (int i : nums) {
            sum += i;
        }
        return sum;
    }

    public static void main(String[] args) {
        System.out.println(addAll(1,2,3,4,5,5,6,7,7));
    }
}

def addAll(*nums):
    sum = 0
    for i in nums:
        sum = sum + i
    return sum
print(addAll(1,2,3,1,1,2,3))

 

모두 주어진 양수들을 더하는 함수 또는 메소드를 나타냅니다.  C/C++은 복잡한 반면에(전체 길이를 구할 수 없어 종결 문자를 쓰거나(예시에서 종결 숫자로 -1을 사용), 길이를 따로 주어주어야 합니다.), Java와 Python은 단순합니다.

 

차이가 있다면, 위의 세 언어는 ...을 Python은 *변수명 을 사용하는데, 이는 C/C++에서 stdarg 헤더 파일을 사용하지 않는 경우 C언어의 포인터를 이용해 *args와 같이 가변 인자를 받았던 것과 상통하는 것 같습니다. 참고로, Java와 Python의 경우 리스트를 전달하여 가변 인수 함수를 대신할 수 있습니다.

 

 

 

한편, Python에서는 키워드 형태의 가변 인수를 전달받기 위해 **을 붙여서 **kwargs와 같이 사용합니다. 이는 키와 값이 대응된 형태의 딕셔너리 자료형으로 전달됩니다. 자세한 내용은 추후 설명하겠습니다.

def func1(**kwargs):
    print(kwargs)

func1(a = 1, b = 10) # 반드시 인수 이름을 주어줘야 합니다.

-----------실행 결과--------------
{'a': 1, 'b': 10}

 

 

Comments