개발 기록
>함수형 프로그래밍은 무엇인가? 본문

■
1. 개요
● 프로그래밍은 컴퓨터에게 수행할 작업을 지시하는 과정을 말하며, 여러가지 패러다임 또는 방식으로 분류할 수 있다.
① 선언형 프로그래밍: '무엇(What)'을 표현하는 방식에 중점을 둔다. 수행해야 할 작업을 선언하고, 어떻게 처리할지 명시한다.
ex. SQL, Prolog (논리 프로그래밍 언어), 함수형 프로그래밍 언어
② 명령형 프로그래밍: '어떻게(How)'를 표현하는 방식에 중점을 둔다. 수행해야 할 작업을 세부적인 명령어로 순차적으로 기술
ex. 프로시저기반 언어 (예: C, Pascal), 객체지향 프로그래밍 언어의 일부 (예: Java, C++), 절차지향 프로그래밍
③ 객체지향 프로그래밍: 객체(object)를 기반으로 프로그램을 구성하고 작성하는 방식.
ex. Java, C++, Python, Ruby
④ 함수형 프로그래밍: 수학적 함수의 개념을 기반으로 프로그램을 작성.
ex : Haskell, Lisp, Scala, JavaScript (일부 기능)
⑤ 절차지향 프로그래밍: 프로시저나 함수의 연속적인 호출에 중점을 둔다. 구조를 절차적으로 기능 단위로 분해하여 작성한다.
ex. C, Pascal
⑥ 로직형 프로그래밍: 사실과 규칙을 기반으로 프로그램을 작성하고 문제를 해결하는 방식에 중점을 둔다
ex. Prolog
⑦ 반응형 프로그래밍: 데이터의 흐름에 따라 시스템을 구성하고, 데이터의 변화에 반응하여 동작하는 프로그래밍 방식을 강조한다.
ex. RxJava, Reactive Extensions (ReactiveX)
이 중 함수형 프로그래밍에 대해서 알아보자!!!!
■
2. 함수형 프로그래밍
프로그램을 수학적인 함수의 평가로 간주하고, 함수의 조합을 통해 프로그램을 구성하는 프로그래밍 패러다임
(1) 특징
① 불변성 데이터: 데이터를 변경할 수 없는 불변 객체로 다룬다. 데이터의 상태를 변경하는 대신, 새로운 데이터를 생성하거나 변형한다. (ex. final 키워드)
② 함수의 일급 객체 : 함수를 자료구조에 저장하고, 함수를 다른 함수의 인자로 전달하거나 다른 함수의 반환 값으로 사용
③ 순수 함수: 순수 함수는 동일한 입력에 대해 항상 동일한 출력을 반환하며, 외부의 상태를 변경하지 않는다.
④ 고차 함수: 함수를 다루는 함수. 고차 함수는 다른 함수를 인자로 받거나 함수를 반환할 수 있다.
⑤ 재귀: 재귀를 통해 반복을 대체하고, 재귀적으로 문제를 분해하여 해결한다.
⑥ 모나드: 부수 효과(side effect)를 다루는 데 사용되는 개념적인 구조. (ex. List, Future, IO..등)
⑦ 참조 투명성: 동일한 인자에 대해 항상 동일한 결과를 반환한다.
함수형 프로그래밍은 초반에는 큰 호응을 받지 못했지만, 병렬 처리와 이벤트 지향 프로그래밍에 적합하기 때문에 다시 부각 되고 있다. 그래서 더욱 효율적인 프로그래밍을 위해 객체 지향 프로그래밍과 함수적 프로그래밍을 혼합해서 사용하고 있다.
■
2. JAVA 에서 함수형 프로그래밍
(1) 함수의 일급 객체(First-Class Functions)
함수의 일급 객체란 프로그래밍 언어에서 함수를 다루는데 있어서 일반적인 값과 동일하게 취급되는 개체를 의미.
☞ 함수를 변수에 할당할 수 있다.=> 함수를 값으로 취급
☞ 함수를 다른 함수의 인자로 전달할 수 있다. => 고차함수를 사용할 수있는 기반이 된다.
☞ 함수를 다른 함수의 반환 값으로 사용할 수 있다. => 함수가 함수를 생성하거나 반환할 수 있다.
☞ 함수를 자료구조에 저아할 수 있다. => 함수를 배열, 리스트, 맵 등의 자료 구조에 저장할 수 있다.
* Java에서 함수형 인터페이스를 사용하여 함수를 일급 객체로 다룰 수 있다.
★ 함수형 인터페이스 (Functional Interface):
java.util.function 패키지에 다양한 함수형 인터페이스가 포함되어 있다.(Function, Predicate, Supplier, Consumer 등)
| @FunctionalInterface | 함수형 인터페이스를 선언하기 위한 어노테이션 |
| interface Function<T,R> | 하나의 인수를 받아들이고 결과를 생성하는 함수를 나타냄 |
| @param <T> | 함수에 대한 입력 유형 |
| @param <R> | 함수 결과의 유형 타입 |
| apply 메서드 | 특정 함수에 대해 주어진 인자(입력값)를 사용하여 연산 또는 처리를 수행 |

function 인터페이스를 통해 함수형 프로그래밍을 구현해보자!
import java.util.function.Function;
public class FunctionExample {
public static void main(String[] args) {
// Function 인터페이스를 구현한 두 수의 합을 계산하는 함수
Function<Integer, Integer> addTwo = new Function<Integer, Integer>() {
@Override
public Integer apply(Integer x) {
return x + 2;
}
};
// Function을 사용하여 값을 계산
int result = addTwo.apply(3); // 입력값 3을 넣어서 계산
System.out.println("Result: " + result); // 출력: 5
}
}
위의 예제를 람다 표현식으로 바꾸면 더 간단하게 구현할 수 있다.
import java.util.function.Function;
public class LambdaFunctionExample {
public static void main(String[] args) {
// 람다 표현식을 사용하여 Function 인터페이스 구현
Function<Integer, Integer> addTwo = x -> x + 2;
// Function을 사용하여 값을 계산
int result = addTwo.apply(3); // 입력값 3을 넣어서 계산
System.out.println("Result: " + result); // 출력: 5
}
}
※ 모든 함수형 프로그래밍 코드가 함수형 인터페이스를 구현해야 하는 것은 아니며, 람다 표현식과 스트림 API 등의 기능만으로도 함수형 프로그래밍의 장점을 활용할 수 있다.
(2) 고차 함수
고차 함수는 다른 함수를 인자로 받거나 함수를 반환하는 함수를 의미한다.(ex. 함수 합성) 즉 함수를 다루는 함수로 함수형 프로그래밍에서 함수를 조합하고 추상화하기 위해 사용된다.
import java.util.function.Function;
public class HigherOrderFunctionExample {
// 고차 함수: 다른 함수를 인자로 받아서 실행하는 함수
// Function<Integer, Integer> func: Integer 타입을 입력으로 받아 Integer 타입을 반환
static void applyFunction(int x, Function<Integer, Integer> func) {
int result = func.apply(x); // func에 전달된 함수를 x에 적용하여 결과 계산
System.out.println("Result: " + result);
}
public static void main(String[] args) {
// 고차 함수를 호출하여 다양한 함수를 전달해 보기
applyFunction(5, y -> y * 2); // 입력값에 2를 곱하는 함수 전달 => 결과 5 * 2 = 10 출력
applyFunction(8, z -> z + 10); // 입력값에 10을 더하는 함수 전달 => 결과 8 + 10 = 18이 출력
}
}
(3) 메서드 레퍼런스
메서드 레퍼런스는 Java에서 메서드를 가리키는 표현식으로, 람다 표현식을 간결하게 표현할 수 있는 방법을 제공한다.
' :: ' 기호를 사용하여 특정 메서드를 참조한다.
예를 들어, 아래 예제는 MethodReferenceExample::convertToUpper 구문을 사용하여 정적 메서드 convertToUpper를 참조하고, 이를 Function<String, String> 인터페이스의 구현체로 사용하고 있다.
import java.util.function.Function;
public class MethodReferenceExample {
// 정적 메서드
static String convertToUpper(String str) {
return str.toUpperCase();
}
public static void main(String[] args) {
// 메서드 레퍼런스를 사용하여 정적 메서드를 Function 인터페이스에 전달
Function<String, String> toUpperCase = MethodReferenceExample::convertToUpper;
// Function을 사용하여 값을 변환
String result = toUpperCase.apply("hello");
System.out.println("Result: " + result); // 출력: "HELLO"
}
}
(4) 모나드
모나드는 값을 래핑하고, 일련의 변환을 적용하고, 모든 변환이 적용된 값을 다시 가져올 수 있도록 해준다.
Java에는 Optional과 CompletableFuture 등 모나드적인 특성을 가진 클래스가 있다.
Optional.of(2).flatMap(f -> Optional.of(3).flatMap(s -> Optional.of(f + s)))
참고
'DIVE' 카테고리의 다른 글
| > 캐싱에 대해 알아보자! 캐시는 언제 써야 할까? (0) | 2024.01.05 |
|---|---|
| > [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (5) HttpMessageConvert (0) | 2023.12.12 |
| > [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (4) Convert (0) | 2023.12.08 |
| > [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (3) Request, Response Log (0) | 2023.12.08 |
| > [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (2) HandlerMethodArgumentResolver : Request Value Binding (0) | 2023.12.07 |