함수

TIL·2022년 12월 12일
0

Java (최은빈)

목록 보기
12/27

  • 클래스 로더, 스택(맨 밑에 main()), GC
  • main 내부에서 호출하는 함수는 static (static 위한 메모리 따로 있으므로 main()과 같은 공간에 있어야 호출 가능)
  • 함수의 한계 : 반환값 두개 이상 불가능
    • 함수 내부에서 배열로 만들어 반환 (동일한 데이터 타입)
    • 객체로 만들어 반환
    • 함수 외부에서 배열로 만들어 인자로 전달, 함수 내부에서 리턴X
  • 함수의 인자
    • = 변수 -> 함수 내부에서 외부로 반환하지 않으면 외부 호출에 영향이 없다. (함수 외부에서 값을 복사하여 인자로 들여옴) : call by value(data) (원본 수정 X)
    • = 배열 -> 함수 외부로 반환하지 않아도 함수 내부의 변화가 외부에서 반영된다. (배열의 주소값이 복사되어 인자로 들어옴)
      파라미터와 인자의 변수명이 달라도 주소값은 같다 : call by value(address) (원본 수정 O)
    • call by reference (참조자는 C++ 개념)

package me.day06.function;

public class MainFunctionExample { // 컴파일 실행 후 바이트 코드로 실행될때 클래스로더가 모든 클래스 로드 후 main 함수 가장 먼저 호출

    // 함수: 공통적인 기능을 분리/모듈화해서 작성하기 위함
    // 메서드: 객체지향에서 함수를 메서드 (함수의 작은 범위)

    public static void main(String[] args) {

        // 스택에서 맨 밑에 클래스 로더, 그위에 main(), 그위에 sigma()
        // sigma() 호출 끝나면 리턴 값을 main()에 전달 후 스택에서 빠짐
        int res1 = sigma(1);
        int res2 = sigma(10); // 코드 중복 하지 말고 모듈화 // 실무에서 함수/클래스 단위로 협업
        System.out.println("res1 = " + res1);
        System.out.println("res2 = " + res2);

        // main 내부에 존재하는 지역변수 !== swap()의 지역변수 x, y (scope)
        int x = 1;
        int y = 2;

        // swap : 다른 변수 scope으로 값만 복사
        System.out.printf("swap 함수 외부 (전): [%d %d]\n", x, y); // [1 2]
        swap(x, y);
        System.out.printf("swap 함수 외부 (후): [%d %d]\n", x, y); // [1 2]

        // swap2 : 함수 외부에 배열로 반환
        int[] res = swap2(x, y);
        x = res[0];
        y = res[1];
        System.out.printf("swap2 함수 외부 (후): [%d %d]\n", x, y); // [2 1]

        // swap3 : 인자를 배열로 전달 -> 배열의 값이 아닌 주소로 전달됨
        int[] vars = new int[]{x, y}; // [2 1]
        System.out.println(vars); // args, vars 이름은 달라도 주소값 같다

        swap3(vars); // 리턴값 없어도 순서 바뀜
        System.out.printf("swap3 함수 외부 (후): [%d %d]\n", vars[0], vars[1]); // [1 2]


    }


    // sigma(함수이름), 사용자로부터 들어온 입력 n(매개변수), int 리턴값의 타입(반환형), res1에 들어감 // 1 = 인자, 파라미터
    // main 내부에서 호출하는 함수는 static // static 위한 메모리 따로 있으므로 main()과 같은 공간에 있어야 호출 가능 // 나중에 다시
    // 접근제어자 public
    // int n, i, sum 은 sigma scpoe
    public static int sigma(int n) {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return sum;
    }

    // 자바 함수의 한계 : 반환값이 두 개 이상이 불가능 -> 해결방법 : 배열(동일한 데이터 타입만 가능)이나 객체로 반환
    public static Aggregation sumAndAverage(int n) { // int n = 1, int n = 10 // main에서 값 복사해서 가져옴
        int sum = 0;
        double average = 0.0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        average = (double) sum / n;
        return new Aggregation(sum, average);
    }

    // 같은 x, y여도 다른 scope이며 값만 복사해오므로 함수 내부에서 바뀐 값은 리턴해줘야 main(함수 외부) 에서 알 수 있다.
    public static void swap(int x, int y) { // swqp 함수 내부에만 존재하는 지역변수 !== main의 x, y
        System.out.printf("swap1 함수 내부 (전): [%d %d]\n", x, y); // [1 2]
        int temp; // 새로운 컵
        temp = x;
        x = y;
        y = temp;
        System.out.printf("swap1 함수 내부 (후): [%d %d]\n", x, y); // [2 1]

//        return x, y; // 자바는 두개 이상 반환 불가
    }

    // 배열에 담아 외부에 전달하기 : call by value(data) (x, y가 값만 복사되어 함수로 들어옴)
    public static int[] swap2(int x, int y) {
        System.out.printf("swap2 함수 내부 (전): [%d %d]\n", x, y);
        int temp;
        temp = x;
        x = y;
        y = temp;
        System.out.printf("swap2 함수 내부 (후): [%d %d]\n", x, y);

        return new int[]{x, y};
    }


    // call by refrence 아님 (참조자에는 C++ 개념)
    // call by value(address value)
    // 외부에 리턴하지 않아도 바뀐값을 외부에서 알게 하는 방법 : 인자를 배열로 받기
    public static void swap3(int[] args) {
        System.out.println(args); // args, vars 이름은 달라도 주소값 같다 // 실제 메모리 값의 주소 아닌 해시코드 값임

        System.out.printf("swap3 함수 내부 (전): [%d %d]\n", args[0], args[1]);
        int temp;
        temp = args[0];
        args[0] = args[1];
        args[1] = temp;
        System.out.printf("swap3 함수 내부 (후): [%d %d]\n", args[0], args[1]);
    }
}

변수와 배열의 참조

  • call by reference
    call by value (data)
    call by value (address)
  • 리턴 값 없는데 왜 순서 바뀌었을까
    변수 : 다른 scope에 변수 새로 할당되고 복사된 값이 새로운 변수에 들어감 // 서로 다른 4개의 변수
    배열 : 새로운 변수를 참조하지 않고 배열의 주소를 그대로 참조
  • 배열
    • 배열과 객체는 생성하자마자 배열의 이름에 주소값이 들어가있다. 인덱스로 모든 데이터에 접근 가능
    • 배열의 전달 = 원본 배열 주소의 전달
    • 값 복사 = 원본 수정X, 주소값 복사 = 원본 수정O (원본 참조)
    • 일반 변수는 실제 물리적 주소값을 알기 어렵다. (C++ 에서는 포인터로 가능)
    • 배열 인덱스의 시작이 0인 이유 = 주솟값 추정
      (첫번째 인덱스의 주소값) + i * (int의 byte 수) = 200 + 1 * 4 = 204 = 배열 두번째 원소의 주소값

중복정의(overload)

  • 함수 이름이 동일하면 매개변수의 개수, 데이터 타입, 순서로 구분
  • 매개변수의 개수, 데이터 타입, 순서 까지 동일하면 컴파일 에러
  • 반환형만 다른 것은 다른 함수아님

재정의(override)

  • 상속에서 부모의 메소드를 다시 정의

함수명

  • 동사가 먼저 오는게 좋음 (calculateAverage)
  • 특수문자, 숫자, 키워드 X



가변인자

  • 함수 여러개를 중복정의 하는 대신 가변인자(Java8) 사용 : int ... vars
  • 컴파일러는 배열 인자로 인식 : int[] vars
package me.day06.function;

public class VarsArgExample { // 가변 인자 (Java8)
    public static void main(String[] args) {
        // 몇개의 인자 오든 상관없다.
        sum();
        sum(1);
        sum(1, 2);
        sum(1, 2, 3);
        sum(1, 2, 3, 4, 5, 6);
    }

    /*
    // 1. 중복 정의
    public static int sum(int x, int y) {
        return x + y;
    }
    public static int sum(int x, int y, int z) {
        return x + y + z;
    }
    public static int sum(int x, int y, int z, int l) {
        return x + y + z + l;
    }

    // 2. 배열
    public static int sum(int[] vars) {
        int sum = 0;
//        for (int i = 0; i < vars.length; i++) {
//            sum += i;
//        }
        // 배열은 for-each 많이 쓴다 (i 필요 없는 경우)
        for (int v: vars) { // vars의 값을 하나씩 가지고 와서 v에 넣음
            sum += v;
        }
        return sum;
    }
    */

    // 3. 가변인자
    public static int sum(int... vars) { // 내부적으로 배열로 구현됨
        int sum = 0;
        for (int v: vars) {
            sum += v;
        }
        return sum;
    }

}



재귀함수

  • 점화식, drive and conquer(분할 정복)
  • 트리로 종료구문까지 쭉 내려갔다가 계산하며 다시 올라옴. 중복되는 항의 계산이 많아 속도 느리다
  • vs. for : 계산에서 중복되는 항 없다
  • DFS/BFS : for문 횟수가 가능되지 않을때 고려. 결과를 보고 for문 멈춰야 할때
package me.day06.function;

public class RecursiveFunctionExample {

    // 재귀 함수 (recursive function) : 함수 내에서 자기 자신을 호출하는 함수
    // DFS/BFS 에서 많이 사용. 얼만큼 for문 돌아야 하는지 횟수가 가늠되지 않을때. 결과물 보고 for문 멈춰야 할때

    public static void main(String[] args) {
        long res1 = fibonacciFor(10);
        long res2 = fibonacciRecursive(10);
        System.out.println("res1 = " + res1);
        System.out.println("res2 = " + res2);
    }

    // 피보나치 수열 : 0 1 1 2 3 5 8 13 ....
    // f(n) = f(n-1) + f(n-2)

    // for : 차근차근 앞으로 나가며 계산하기 때문에 중복되는 항 없다.
    public static long fibonacciFor(int n) {
        // 이미 알고 있는 값
        if (n == 0) return 0;
        if (n == 1) return 1;

        int a = 0;
        int b = 1;
        int c = 0;
        for (int i = 2; i <= n; i++) { // i = 0, 1 -> 이미 알고 있는 값
            // f(n) = f(n-1) + f(n-2)
            c = a + b;
            // a b c
            //   a b c
            a = b;
            b = c;
        }
        return c; // n항의 값
    }


    // divide-and-conquer (분할 정복). 속도 느림 (중복되는 항의 계산이 많기 때문)
    public static long fibonacciRecursive(int n) {
        // 종료 구문
        if (n == 0) return 0;
        if (n == 1) return 1;

        // f(n) = f(n-1) + f(n-2) (점화식)
        return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);
        // n = 0, 1 일때까지 재귀적으로 계속 호출됨
        // 트리로 쭉 밑에 까지 내려간 다음에 종료구문에 도달하면 다시 위로 올라오면서 계산함
        // 단점 : 중복되는 항의 (이미 알고 있는 항의) 계산이 많음 (f(3), f(2), ... )
    }

}

0개의 댓글

관련 채용 정보