# 함수란?

- 함수는 특정한 기능을 하는 코드의 집합이다
- 반복적으로 사용되는 코드를 함수로 만들어 놓으면 코드의 길이를 줄일 수 있고, 가독성을 높일 수 있다
## 자바 함수의 구조
- 자바의 함수는 **메서드(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/)
## 스택

- 스택(stack)은 데이터를 제한적으로 접근할 수 있는 자료구조다
- 후입선출(LIFO, Last-In First-Out) 구조로 되어 있다
- 가장 나중에 쌓은 데이터를 가장 먼저 빼낼 수 있는 구조다
> 자료구조는 데이터를 표현하고 관리하고 처리하기 위한 구조를 의미한다
### 스택 메모리

- 스택 메모리(stack memory)는 함수의 호출과 관계되는 지역변수와 매개변수가 저장되는 메모리 영역이다
- 이름 그대로 스택(stack) 자료구조의 특성을 따른다
- 함수가 호출될 때마다 함수의 실행에 필요한 메모리가 스택 메모리에 할당된다

- 함수의 실행이 종료되면 할당된 메모리가 반환된다

- 함수의 호출이 너무 깊어지면 스택 메모리가 가득 차서 오버플로우(overflow)가 발생할 수 있다

## 재귀 함수 (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;
}
}
```