본문 바로가기

Study/Java

Java : 함수

함수에 대해 살펴보겠습니다.

이전 포스팅에서는 Wrapper Class 즉 일반적인 자료형을 편리하게 쓰기 위해 만들어놓은 객체의 형태와 정렬에 대해 학습해보았습니다.

그리고 배열안에 무작위로 들어있는 원소들을 오름차순 또는 내림차순으로 정렬하는 방법도 같이 봤었습니다.

오늘 포스팅에서는 함수에 대해 살펴볼 것입니다.

함수란?


개요

프로그래밍에서 말하는 함수는 수학에서 말하는 함수와 다를 것이 없습니다.

어떤 값x를 넣어주면 함수 f의 식에 맞게 처리된 후 f(x)를 반환해줍니다.

자바스크립트에서 함수를 어떻게 사용했는지 보겠습니다.

function plusOne (x) {
  x++
  return x;
}

let number = 1;

let fx = plusOne(number);

console.log(fx);

우선 함수 plusOne을 만들어 주었습니다. 이 함수는 어떤 값 x가 들어오면 이 x에 1을 더해줍니다. 그리고 x를 그대로 반환해줍니다.

만약 변수 number가 1일 때 number를 함수 plusOne에 넣어주면 어떻게 될까요?

결과값을 변수 fx에 저장해서 콘솔에 출력하면 2가 나올겁니다.

이렇게 함수는 들어오는 값을 받아서 처리한 후 나오는 값으로 뱉어줍니다.

이 때 들어오는 값의 이름을 매개변수, 즉 parameter라고 하고 나오는 값을 반환값 즉 return 값이라고 합니다.

Java에서의 함수

우리가 이제까지 살펴보았던 예제들에서도 함수는 사용되고 있었습니다.
예를 들어서 2진수를 10진수로 바꾸는 예시를 보겠습니다.

public class Main {
  public static void main(String[] args) {
    String bin = "1010101";
    int binToDec = Integer.parseInt(bin, 2);

    System.out.print(binToDec);        // 85
  }
}

"내가 10진수를 줄테니까 2진수를 돌려줘!"

라고 요청해주는 곳이 어디일까요?

바로 parseInt()입니다. 이 녀석의 매개변수 자리에 우리가 어떤 값을 넣어주었고, 변수에 저장해서 콘솔에 출력해보니 10진수가 나오는걸 알 수 있습니다.

이렇게 사용하는 형태를 우리는 method 메서드라고 부르고, 각각의 parameter 자리에 들어가는 값들을 argument라고 부릅니다.

이러한 method들은 누군가 편의상 만들어놓은 것이며, 필요에 따라 꺼내 쓰는 것입니다. 그렇지만 이렇게 규격화된 method만 사용한다면 프로그래밍을 하는데 자유도가 떨어지고 불편할 것입니다.

그래서 우리는 프로그래밍을 하면서 필요한 다양한 요건들을 그때그때 끄집어내서 쓸 수 있도록 custom된, 내 입맛에 맞는 함수를 만들 수 있습니다.

그것이 지금부터 살펴볼 함수라고 하는 매우 편리한 도구입니다.

함수와 메서드

우선 함수 객체는 매우 독립적입니다. 혼자 덩그러니 있다가 필요하면 호출해서 사용할 수 있는 녀석입니다.
중복된 작업을 할 때마다 불러와서 써주면 되니까 프로그래밍할 때 더욱 효율적이겠지요?

그리고 함수는 들어가는 값, 즉 매개변수(parameter)의 갯수에 제한이 없습니다. 그렇지만 나오는 값(return)은 딱 하나만 존재하게 됩니다.

이에 반해 Method는 클래스에 소속되어 있는 함수로 선언된 함수보다 독립성이 낮습니다.

함수의 형태

자바에서 기본적인 함수의 형태는 이렇습니다.

// 돌려줄 값이 있는 함수
static 돌아오는_값의_자료형 함수명 () {

  처리;
  return 돌려줄 값;

}

// 돌려줄 값이 없는 함수
static void 함수명 () {

  처리;

}

필요하다면 매개변수를 넣어줄 수 있습니다. 이 때 매개변수는 자료형 변수명의 순서로 씁니다.

주의할 사항으로 함수는 메인의 바깥에서 선언해준다는 것입니다.

public class MainClass {
  public static void main(String[] args) {
    // 다양한 코드
  }
  // 함수를 작성할 부분
}

왜냐하면 public static void main(String[] args) { }도 결국 함수이기 때문에 함수 안에서 함수를 선언해주지는 않기 때문입니다.


함수의 호출

개요

자 그러면 함수를 본격적으로 만들고 사용해보겠습니다.

우선 함수를 호출하는 연습을 해보겠습니다.

다음과 같이 코드를 작성해주세요.

public Main {
  public static void main(String[] args) {

  }

  public static int example(char c) {
    System.out.println("example 함수 출력!!");

    return 1;
  }

}

이 함수는 호출했을 때 "example 함수 출력!!"이라는 메시지를 보여주고, 1이라는 값을 return해줍니다.

호출하기

자 그러면 함수를 호출하기 위해 main에 다음과 같이 코드를 작성해보겠습니다.

public Main {
  public static void main(String[] args) {
    int i = example('A');
  }

  public static int example(char c) {
    System.out.println("example 함수 출력!!");

    return 1;
  }

}

우선 함수의 argument가 없으면 에러가 발생하기 때문에 char 자료형의 A를 매개변수로 전달해주겠습니다.

우선 변수에 선언하는 것 자체로 함수가 호출됩니다. 다만 return값은 보이지 않을것입니다.

저 상태에서 Run을 하게 되면 콘솔에 이렇게 표시됩니다.

example 함수 출력!!

return 값 출력하기

자 그러면 함수를 호출한 변수 iprint해보면 어떨까요?

public Main {
  public static void main(String[] args) {
    int i = example('A');
    System.out.println(i);
  }

  public static int example(char c) {
    System.out.println("example 함수 출력!!");

    return 1;
  }

}

콘솔에 메시지와 함께 return값이 출력될 것입니다.

example 함수 출력!!
1


함수의 활용


입력받은 문자열을 대문자로 바꿔주는 함수

문자열을 모두 대문자로 바꿔주는 String 클래스의 methodtoUpperCase()라는 것이 있음을 우리는 알고 있습니다.

그렇지만 여기에서 그치지 않고 직접 함수로 구현해보겠습니다.

// 문자열을 paremeter로 받는 함수
public static String toUpperCase(String text) {
  String txt = "";        // 빈 문자열

  // 반복문을 돌려 매개변수로 받은 문자의 길이만큼 반복
  for (int i = 0; i < text.length(); i++) {
    int n = text.charAt(i);    // i번째의 문자를 n에 저장
    if (n >= 97) {        // 아스키코드를 참조하여 97이상이면
      n -= 32;            // n에서 32만큼 빼줌
    }
    txt += (char)n;        // 빈 문자열에 n을 문자로 변환해서 저장
  }
  return txt;            // txt를 돌려줌
}

이렇게 작성을 해주고 함수를 호출해서 return을 출력해보겠습니다.

String p = "abcDEF";
String upStr = toUpperCase(p);
System.out.println(upStr);        // ABCDEF

입력한 문자에서 소문자들이 대문자로 바뀌어 출력됩니다.

return 없이 나눗셈의 결과를 출력해주는 함수

x 나누기 y를 해서 몫을 출력해주는 함수를 만들 수 있습니다.
보통 함수를 작성할 때 연산 결과를 return으로 반환해주고 함수를 호출해서 콘솔에 출력하면 값을 보여주는 구조가 일반적이지만, void형태의 그러니까 return이 없는 함수에서도 호출 시 값을 내뱉어줄 수 있습니다.

두개의 매개변수를 받아서 나눗셈을 출력하는 예제를 만들어보겠습니다.

// void 형태(return이 없는)로 작성한 나누기 연산 몫의 결과를 출력하는 함수
public static void division(double p, double q) {
  double result;
  result = p / q;
  System.out.println("결과값은 " + result + "입니다");
}

이 함수를 main에서 출력하면 다음과 같은 결과를 얻습니다.

int x = 15;
int y = 3;
division(x, y);

결과값은 5입니다.

그런데 우리가 계산기 예제에서 살펴봤듯이 0으로 나누려고 하면 Infinity가 나오면서 연산이 안되기 때문에 나눌 수 즉 이 함수의 q가 0일 때는 0으로 나눌 수 없다는 메시지도 띄워주겠습니다.

// void 형태(return이 없는)로 작성한 나누기 연산 몫의 결과를 출력하는 함수
public static void division(double p, double q) {
  double result;
  if (q == 0) {
    System.println("0으로 나눌 수 없음");
  }
  result = p / q;
  System.out.println("결과값은 " + result + "입니다");
}

이렇게 고쳐주고 함수를 호출할 때 q자리에 0을 넣어주면 조건문에 들어있는 메시지를 띄워줄 것입니다.

그런데 여기에서 더 재미있는 사실은 조건문에 return을 걸어주면 반복문에서의 break역할을 하게 되어 조건문이 참이되면, 즉 0을 입력받으면 아래 작업은 실행되지 않게됩니다.

다시 말해서 void 함수는 return이 없지만 작성해주어도 무방하며 특히 break와 같은 역할을 한다는 것입니다.

배열을 건들어주는 함수

예를 들어서 어떤 배열이 있다고 가정해봅시다.

int array[] = { 11, 22, 33 };

만약에 이 배열을 다른 배열에 통째로 복사하려면 어떻게 해야할까요?

int array[] = { 11, 22, 33 };
int arrayAlias[] = array;    // 임의의 빈 배열에 그대로 넣어줌

이렇게 하면 배열의 주소를 참조해서 빈 배열에 값을 넣어주게 됩니다.
그래서 두개의 배열을 출력해보면 같은 heap 주소를 반환해주게 됩니다.

System.out.println(array);        // [I@2a139a55
System.out.println(arrayAlias);    // [I@2a139a55

물론 이 heap 메모리주소는 컴퓨터마다 다르게 나옵니다.

그래서 두 개의 배열중에 하나의 배열의 일정 인덱스에 어떤 값을 넣어주게 되면 같은 주소를 참조하는 배열의 해당 인덱스를 조회했을 때 같은 값이 나오게 됩니다.

arrayAlias[1] = 27;
System.out.println(arrayAlias[1]);    // 27
System.out.println(array[1]);    // 27

이렇게 같은 값을 출력해주는 것을 볼 수 있습니다.

그러면 배열을 parameter로 넘기는 방법에 대해 살펴보겠습니다.

배열에 있는 값에 2씩 곱해서 저장해주는 함수를 만들려면 어떻게 해야할까요?

단순한 방법으로 우선 시도해보겠습니다.

5개의 원소를 갖는 배열을 만들어주고 배열안에 있는 숫자들에게 각각 2씩 곱해서 저장해주는 함수를 만들어보겠습니다.

// 단순한 방법
public static void arrayFunction(int a, int b, int c, int d, int e) {
  a *= 2;
  b *= 2;
  c *= 2;
  d *= 2;
  e *= 2;
}

이 함수는 배열의 값들을 parameter로 전달해서 각각에 2를 곱해주는 함수입니다. 그런데, 함수에는 return값을 하나만 쓸 수 있기 때문에 이렇게 쓸 경우에는 각각의 값에 2를 곱한 형태를 return하기는 어렵습니다..

그래서 배열의 주소 즉 배열 자체를 parameter로 넘겨서 반복문으로 처리해주는 작업을 해 주는것이 좋습니다.

예를 들어서 1부터 5까지의 숫자가 담긴 배열이 있다고 가정해봅시다.

int myArray[] = { 1, 2, 3, 4, 5 }

배열의 각 요소를 돌면서 i번째에 있는 요소마다 2를 곱해주는 형태의 함수를 구현해보겠습니다.

public static void arrayFunction(int array[]) {
  for (int i = 0; i < array.length; i++) {
    array[i] *= 2;
  }
}

이렇게 했을 때 배열은 return이 없더라도 반복문에 의해 원래의 배열의 각 원소에 2씩 곱해진 값이 저장됩니다.

arrayFunction(myArray);        // 함수의 parameter로 배열을 직접 지정
System.out.println(Arrays.toString(myArray));    // -> [2, 4, 6, 8, 10]

그렇다면 arrayFunction 함수에 parameter인 arrayreturn하게 해주면 해주지 않은것과 어떤 차이가 있을까요??

public static void arrayFunction(int array[]) {
  for (int i = 0; i < array.length; i++) {
    array[i] *= 2;
  }
  return array;        // return 추가
}

배열은 어차피 주소를 할당해서 값을 조회 및 변경하는 것이기 때문에 굳이 return을 받으려고 할 필요가 없을 수도 있습니다.

2차원 배열을 1차원 배열로 변경하는 함수

2차원 배열을 1차원 배열로 바꿔주는 함수를 작성해봅시다!

public class MainClass {

  public static void main(String[] args) {

    int from[][] = {
      { 1, 2, 3, 4 },
      { 5, 6, 7, 8 },
      { 9, 10, 11, 12}
    }

    int result[] = arrayFunction(from);

    System.out.println(Arrays.toString(result));

  }

  public static int[] arrayFunction(int array[][]) {

    int to = new int[array.length * array[0].length];

    for (int i = 0; i < array.length; i++) {
      for (int j = 0; j < array[i].length; j++) {
        to[array[i].length * i + j] = array[i][j]
      }
    }

    return to;

  }

}

오늘 다룰 내용은 여기까입니다~
그러면 연습문제를 통해서 함수를 더욱 활용해보도록 하겠습니다!


연습문제


문제 1

아스키 코드 값을 입력하면 문자를 확인할 수 있는 함수를 작성하라.

import java.util.*;

public class MainClass {
  public static void main(String[] args) {

    Scanner input = new Scanner(System.in);

    System.out.println("아스키 코드 값을 입력하시면 문자로 바꿔줍니다.");
    System.out.print("아스키 코드 값을 입력하세요 >> ");
    int asc = input.nextInt();

    char print = ascToChar(asc);
    System.out.println("아스키 코드 \"" + asc + "\"를 문자로 변환하면 \"" + print + "\"입니다.");

  }

  public static char ascToChar(int asc) {
    char character = (char) asc;
    return character;
  }

}

문제 2

import java.util.*;

public class MainClass {
  public static void main(String[] args) {

    Scanner input = new Scanner(System.in);

    System.out.println("입력 받은 두 수의 몫과 나머지를 구해드립니다!");
    System.out.print("나누려는 수를 입력하세요 >> ");
    int x = input.nextInt();
    System.out.print("몇으로 나눌까요? >> ");
    int y = input.nextInt();

    // division(x, y);
    int tag[] = new int[1];
    int ret = division(x, y, tag);
    System.out.println("몫은 \"" + ret + ", \"나머지는 \"" + tag[0] + "\" 입니다.");

  }

  // 몫과 나머지를 구하는 함수
  /*
  public static void division(int p, int q) {
    int quotient = p / q;
    int rest = p % q;
    System.out.println("몫은 \"" + quotient + ", \"나머지는 \"" + rest + "\" 입니다.");
  }
  */

  // public static int division(int p, int q, int* tag)
  public static int division(int p, int q, int[] tag) {
    int r = p / q;
    tag[0] = p % q;
    return r;
  }
}

문제 3

두점 사이의 거리를 구하는 함수를 작성하시오.

import java.util.*;

public class MainClass {
  public static void main(String[] args) {

    Scanner input = new Scanner(System.in);

    System.out.println("두 점사이의 거리를 구해드립니다!");
    System.out.print("첫번째 x좌표를 입력하세요. >> ");
    double x1 = input.nextInt();
    System.out.print("첫번째 y좌표를 입력하세요. >> ");
    double y1 = input.nextInt();
    System.out.print("두번째 x좌표를 입력하세요. >> ");
    double x2 = input.nextInt();
    System.out.print("두번째 y좌표를 입력하세요. >> ");
    double y2 = input.nextInt();

    double result = distance(x1, x2, y1, y2);

    System.out.println("두 점 사이의 거리는 " + result + "입니다.");

  }


  // 두 점사이의 거리를 구하는 함수
  public static double distance(double x1, double x2, double y1, double y2) {
    double refX = Math.pow(x2 - x1, 2);
    double refY = Math.pow(y2 - y1, 2);
    double result = Math.sqrt(refY + refX);

    return result;

  }

}

문제 4

입력받은 숫자가 정수인지 실수인지 확인해주는 함수를 작성하세요.

import java.util.*;

public class MainClass {
  public static void main(String[] args) {

    Scanner input = new Scanner(System.in);
    System.out.println("입력한 숫자가 정수인지 실수인지 판단합니다.");
    System.out.print("숫자를 입력해주세요. >> ");
    String number = input.next();

    boolean b = isDouble(number);

    if (b == true) {
      System.out.println("입력하신 숫자는 실수형(float)입니다.");
    } else {
      System.out.println("입력하신 숫자는 정수형(integer)입니다.");
    }

  }

  public static boolean isDouble(String sNumber) {

    boolean numCheck = false;
    for (int i = 0; i < sNumber.length(); i++) {
      int n = sNumber.charAt(i);
      if (n == '.') {
        numCheck = true;
        break;
        //return true;
      }
    }

    return numCheck;
    //return false;
}

끝!!