# 함수란? ![](https://hackmd.io/_uploads/rJpFC-gY6.png) - 함수는 특정한 기능을 하는 코드의 집합이다 - 반복적으로 사용되는 코드를 함수로 만들어 놓으면 코드의 길이를 줄일 수 있고, 가독성을 높일 수 있다 ## 자바 함수의 구조 - 자바의 함수는 **메서드(Method)** 라고 부른다 - 함수와 메서드는 동일한 의미이다 - 함수는 선언 후 호출하여 사용한다 - 함수 선언 : `접근제어자 반환타입 함수이름(매개변수){ /* 실행 코드 */ }` - 접근제어자 : 함수에 접근할 수 있는 범위를 의미 - 반환타입 : 함수가 반환하는 데이터의 타입을 의미 (반환될 데이터가 없을 경우 `void` 를 사용한다) - 함수이름 : 함수의 이름을 의미 - 매개변수 : 함수를 호출할 때 전달하는 데이터를 의미 (전달될 데이터가 없을 경우 생략 가능) - 실행 코드 : 함수가 호출되었을 때 실행되는 코드를 의미 - 함수 호출 : `함수이름(매개변수);` ### 예시 샘플 - 매개변수가 없으며, 반환값이 없는 함수 ```java public class FunctionExample { public static void main(String[] args) { sayHello(); } public static void sayHello(){ System.out.println("Hello"); } } ``` > `void` 는 반환 타입이 없음을 의미한다 - 매개변수가 있으며, 반환값이 있는 함수 ```java public class FunctionExample { public static void main(String[] args) { int result = add(10, 20); System.out.println(result); } public static int add(int num1, int num2){ int result = num1 + num2; return result; } } ``` ## 함수를 사용하는 이유 - 코드 길이도 줄일 수 있고, 가독성도 높일 수 있으며, 코드의 구조를 파악하기 쉽게 만들 수 있다 - 함수명을 보고 어떤 기능을 하는지 파악할 수 있게끔 만들어야 하는 이유 - 코드의 재사용성을 높일 수 있고, 중복 코드를 줄일 수 있다 - ... => **생산성을 높일 수 있다** --- 예시 : 함수를 사용하지 않은 복잡한 코드 ```java public class NoFunctionExample { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("높이를 입력하세요: "); int height = scanner.nextInt(); System.out.println("\n정사각형:"); for (int i = 0; i < height; i++) { for (int j = 0; j < height; j++) { System.out.print("*"); } System.out.println(); } System.out.println("\n왼쪽 정렬된 직각삼각형:"); for (int i = 0; i < height; i++) { for (int j = 0; j <= i; j++) { System.out.print("*"); } System.out.println(); } System.out.println("\n오른쪽 정렬된 직각삼각형:"); for (int i = 0; i < height; i++) { for (int j = 0; j < height; j++) { if (j < height - 1 - i) { System.out.print(" "); } else { System.out.print("*"); } } System.out.println(); } System.out.println("\n정삼각형:"); for (int i = 0; i < height; i++) { for (int j = 0; j < height * 2 - 1; j++) { if (height - 1 - i <= j && j <= height - 1 + i) { System.out.print("*"); } else { System.out.print(" "); } } System.out.println(); } scanner.close(); } } } ``` 예시 : 함수를 사용한 간단한 코드 ```java public class FunctionExample { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("높이를 입력하세요: "); int height = scanner.nextInt(); System.out.println("\n정사각형:"); printSquare(height); System.out.println("\n왼쪽 정렬된 직각삼각형:"); printLeftTriangle(height); System.out.println("\n오른쪽 정렬된 직각삼각형:"); printRightTriangle(height); System.out.println("\n정삼각형:"); printTriangle(height); scanner.close(); } /** * 정사각형을 출력하는 메소드 * * @param height 높이 */ public static void printSquare(int height) { for (int i = 0; i < height; i++) { for (int j = 0; j < height; j++) { System.out.print("*"); } System.out.println(); } } /** * 왼쪽 정렬된 직각삼각형을 출력하는 메소드 * @param height 높이 */ public static void printLeftTriangle(int height) { for (int i = 0; i < height; i++) { for (int j = 0; j <= i; j++) { System.out.print("*"); } System.out.println(); } } /** * 오른쪽 정렬된 직각삼각형을 출력하는 메소드 * @param height 높이 */ public static void printRightTriangle(int height) { for (int i = 0; i < height; i++) { for (int j = 0; j < height; j++) { if (j < height - 1 - i) { System.out.print(" "); } else { System.out.print("*"); } } System.out.println(); } } /** * 정삼각형을 출력하는 메소드 * @param height 높이 */ public static void printTriangle(int height) { for (int i = 0; i < height; i++) { for (int j = 0; j < height * 2 - 1; j++) { if (height - 1 - i <= j && j <= height - 1 + i) { System.out.print("*"); } else { System.out.print(" "); } } System.out.println(); } } } ``` > **Javadoc 주석** > > `/** */` 로 작성한 주석을 Javadoc 주석이라고 한다 > > Javadoc 주석은 함수, 클래스, 변수 등에 대한 설명을 작성할 때 사용한다. > > Javadoc 주석을 작성하면 `javadoc.exe` 를 사용하여 HTML 형식의 문서를 생성할 수 있다 > > Javadoc 주석은 다음과 같은 태그를 사용하여 작성한다 > > - `@param` : 매개변수에 대한 설명을 작성한다 > - `@return` : 반환값에 대한 설명을 작성한다 > - `@throws` : 예외에 대한 설명을 작성한다 > - ... 등등 > > Javadoc을 통해 생성된 HTML 예시 사이트 : [https://docs.oracle.com/javase/8/docs/api/](https://docs.oracle.com/javase/8/docs/api/) ## 스택 ![](https://hackmd.io/_uploads/B1B4A7SKa.png) - 스택(stack)은 데이터를 제한적으로 접근할 수 있는 자료구조다 - 후입선출(LIFO, Last-In First-Out) 구조로 되어 있다 - 가장 나중에 쌓은 데이터를 가장 먼저 빼낼 수 있는 구조다 > 자료구조는 데이터를 표현하고 관리하고 처리하기 위한 구조를 의미한다 ### 스택 메모리 ![](https://hackmd.io/_uploads/SkicAGhuT.png) - 스택 메모리(stack memory)는 함수의 호출과 관계되는 지역변수와 매개변수가 저장되는 메모리 영역이다 - 이름 그대로 스택(stack) 자료구조의 특성을 따른다 - 함수가 호출될 때마다 함수의 실행에 필요한 메모리가 스택 메모리에 할당된다 ![](https://hackmd.io/_uploads/ryrU0QSYa.png) - 함수의 실행이 종료되면 할당된 메모리가 반환된다 ![](https://hackmd.io/_uploads/rJCIAQSYp.png) - 함수의 호출이 너무 깊어지면 스택 메모리가 가득 차서 오버플로우(overflow)가 발생할 수 있다 ![](https://hackmd.io/_uploads/SkgVW4HYp.png) ## 재귀 함수 (Recursive function) - 재귀 함수란 프로그래밍에서 문제를 해결하는 방법 중 하나로, **함수가 자기 자신을 다시 호출**하여 문제를 작은 부분 문제로 나누어 해결하는 방식이다 - 재귀 기법은 간단한 코드로 복잡한 문제를 해결할 수 있지만, 시간 및 공간 복잡도 면에서 비효율적일 수 있다 ### 재귀함수 예시 : 재귀 함수로 구현한 팩토리얼 - 팩토리얼 : 1부터 n까지의 정수를 모두 곱한 값 - 재귀 함수로 팩토리얼을 구현하면 다음과 같다 - 팩토리얼의 점화식 - `n! = n * (n - 1)!` - `n! = n * (n - 1) * (n - 2) * ... * 2 * 1` - `factorial(n) = n * factorial(n - 1)` - `factorial(n) = n * factorial(n - 1) = n * (n - 1) * factorial(n - 2) = ... = n * (n - 1) * ... * 2 * 1` ```java public class FactorialExample { public static void main(String[] args) { int result = factorial(4); System.out.println(result); } public static int factorial(int num){ if(num == 1 || num == 0){ return 1; } else { return num * factorial(num - 1); } } } ``` ### 재귀함수 예시 : 문자열 뒤집기 - 문자열을 뒤집는 함수를 재귀 함수로 구현하면 다음과 같다 - 문자열 뒤집기의 점화식 - `reverse(str) = reverse(str[1...n]) + str[0]` - `reverse(str) = reverse(str[1...n]) + str[0] = reverse(str[2...n]) + str[1] + str[0] = ... = str[n] + str[n-1] + ... + str[1] + str[0]` ```java public class ReverseStringExample { public static void main(String[] args) { String str = "Hello"; String result = reverseString(str); System.out.println(result); } public static String reverseString(String str){ if(str.length() == 1){ return str; } else { return reverseString(str.substring(1)) + str.charAt(0); } } } ``` ### 재귀 함수의 장단점 - 장점 - 코드가 더 간결해진다 - 반복문 대신 재귀 함수를 사용하면 프로그램의 성능이 떨어지지만, 알고리즘을 더 직관적으로 이해할 수 있다 - 단점 - 재귀 함수는 함수를 계속 호출하기 때문에 메모리 공간의 낭비가 발생할 수 있다 - 재귀 함수가 반복문보다 느리다 - 스택에 함수 호출이 쌓이기 때문에 스택 오버플로우가 발생할 수 있다 > 장점에 비해 단점이 더 크기 때문에, **많은 경우에 재귀 함수 대신 반복문을 사용한다** --- 반복문을 이용한 팩토리얼 구현 ```java public class FactorialExample { public static void main(String[] args) { int result = factorial(4); System.out.println(result); } public static int factorial(int num){ int result = 1; for(int i = 1; i <= num; i++){ result *= i; } return result; } } ``` --- 반복문을 이용한 문자열 뒤집기 구현 ```java public class ReverseStringExample { public static void main(String[] args) { String str = "Hello"; String result = reverseString(str); System.out.println(result); } public static String reverseString(String str){ String result = ""; for(int i = str.length() - 1; i >= 0; i--){ result += str.charAt(i); } return result; } } ```