1. 함수형 프로그래밍 정의
참조 투명성 기반의 수학적 함수만으로 자료를 처리하는 프로그래밍 패러다임
가. 함수형 프로그래밍을 사용하는 이유
•
Side Effect가 없음
→ 참조 투명성, 함수 외부의 변화에 영향을 받지 않음
→ 무상태, 객체 내 상태(필드)를 제거함으로써 객체의 상태에 영향을 받지 않음
→ 불변성, 가변적인 변수나 데이터를 사용하지 않음
•
코드가 간결하고 명확해짐
→ 고차함수, 함수를 인자로 받아 처리하기 때문에 코드의 간결성이 향상됨 ex) map, filter, forEach
→ 조건문, 반복문을 사용을 최소화하여 코드의 목적이 명확해짐
•
테스트가 편함
→ 테스트 인자로 전달한 값에 따라 테스트 결과가 쉽게 예측되므로 테스트 코드 작성이 수월함
2. 핵심 개념
가. 1급 객체
•
일급 객체 조건(by 로빈 포플스톤)
1. All items can be the actual parameters of functions
2. All items can be returned as results of functions
3. All items can be the subject of assignment statements
4. All items can be tested for equality.
•
All items can be the actual parameters of functions
→ java는 함수의 매개변수로 함수를 전달할 수 없음
// 반면 kotlin은 function의 매개변수로 () -> Unit을 전달함
object Main {
@JvmStatic
fun main(args: Array<String>) {
function(test)
}
fun function(f: () -> Unit) {
f.invoke()
}
val test: () -> Unit = { println("kotlin") }
}
Kotlin
복사
•
All items can be returned as results of functions
→ java는 함수를 반환할 수 없음
// 반면 kotlin은 함수 println("kotlin") 반환 가능
object Main {
@JvmStatic
fun main(args: Array<String>) {
function()
}
fun function(): () -> Unit {
return { println("kotlin") }
}
}
Kotlin
복사
•
All items can be the subject of assignment statements
→ java는 변수/데이터에 함수를 할당할 수 없음
// test() 메소드는 Object a에 할당이 불가함
public class java {
public static void test(){
System.out.println("java");
}
public static void main(String[] args) {
System.out.println("java");
// Object a = test;
}
}
Java
복사
// 반면 kotlin은 변수 test에 () -> Unit 할당 가능
object Main {
@JvmStatic
fun main(args: Array<String>) {
val a = test
}
val test: () -> Unit = { println("kotlin") }
}
Kotlin
복사
나. Pure Function, 순수 함수
•
정의: 함수 외부로부터 영향을 받지 않고 실행하는 함수
→ 외부의 상태나 가변 변수에 영향을 받지 않음
→ 함수의 결과값은 오직 함수의 입력값에 따라 달라진다
•
장점: 스레드 안전이 보장되고 병렬 계산이 가능함
다. 고차함수(Higher Order Functions)
•
정의: 함수를 다루는 함수
→ 함수를 인자로 받을 수 있고, 결과로 반환할 수 있음
라. Lambda Calculus, 람다 계산
3. Java의 함수형 프로그래밍 단계별 구현 과정
가. 함수형 인터페이스 선언
public class FPexample {
public static void main(String[] args){
}
}
@FunctionalInterface
interface CalculateMin {
public abstract int min(int a, int b);
}
Java
복사
나. 함수형 인터페이스의 인스턴스를 참조변수로 설정
public class FPexample {
public static void main(String[] args){
CalculateMin calculateMin = new CalculateMin() {
@Override
public int min(int a, int b) {
return a < b ? a : b;
}
};
}
}
@FunctionalInterface
interface CalculateMin {
public abstract int min(int a, int b);
}
Java
복사
다. 참조변수에 익명 객체 선언 및 함수형 인터페이스의 추상체 구현
public class FPexample {
public static void main(String[] args){
CalculateMin calculateMin = new CalculateMin() {
@Override
public int min(int a, int b) {
return a < b ? a : b;
}
};
int minNum = calculateMin.min(3, 4);
System.out.println(minNum);
}
}
@FunctionalInterface
interface CalculateMin {
public abstract int min(int a, int b);
}
Java
복사
라. 함수형 인터페이스의 참조변수에 람다식으로 인터페이스의 추상체 구현
public class FPexample {
public static void main(String[] args){
CalculateMin calculateMin = (int a, int b) -> a < b ? a : b;
int minNum = calculateMin.min(3, 4);
System.out.println(minNum);
}
}
@FunctionalInterface
interface CalculateMin {
public abstract int min(int a, int b);
}
Java
복사
마. FP 구현 유의사항
•
람다식(익명 객체)을 할당하는 참조변수의 타입은 함수형 인터페이스(@FunctionalInterface)로 설정
•
익명 객체로 구현한 부분은 이름이 없기 때문에 이에 대한 명칭을 부여하기 위해 함수형 인터페이스를 사용함
•
본문에 구현된 람다식은 함수형 인터페이스에 정의한 함수의 시그니처를 따라야 함
4. 함수형 인터페이스
가. 함수형 인터페이스를 사용하는 이유
•
1급 객체화, java의 함수를 1급 객체로 사용하기 위해
•
코드의 간결성 향상, 객체 선언 및 오버라이딩의 절차를 생략할 수 있음
나. 익명 객체를 사용할 경우
•
Stream 사용 중에 new가 사용되어 전체적인 가독성을 떨어뜨림
class ModifyString{
public String add(Integer number) {
String result = "";
if (isPerfect.test(number)) result += number + " : perfect, ";
else if (isDeficient(number)) result += number + " : deficient, ";
else if (isAbundant(number)) result += number + " : abundant, ";
if (isPrime(number)) result += "prime";
return result;
}
}
public class Classifier {
public static void main(String[] args) {
final int START_NUM = 2;
final int END_NUM = 100;
IntStream.rangeClosed(START_NUM, END_NUM)
.boxed() // Int -> Integer
.map(num -> new ModifyString().add(num))
.forEach(System.out::println);
}
}
Java
복사
다. 함수형 인터페이스를 직접 선언하여 사용하는 경우
•
인터페이스 정의부분이 포함되어 코드가 길어짐
// 직접 함수형 인터페이스 선언
@FunctionalInterface
interface IModifyString {
public abstract String add(Integer number);
}
public class Classifier {
public static void main(String[] args) {
final int START_NUM = 2;
final int END_NUM = 100;
IModifyString modifyString = (Integer number) -> {
String result = "";
if (isPerfect.test(number)) result += number + " : perfect, ";
else if (isDeficient(number)) result += number + " : deficient, ";
else if (isAbundant(number)) result += number + " : abundant, ";
if (isPrime(number)) result += "prime";
return result;
};
IntStream.rangeClosed(START_NUM, END_NUM)
.boxed() // Int -> Integer
.map(modifyString::add) // 각 Integer를 완전수, 부족수 등으로 분류
.forEach(System.out::println);
}
}
Java
복사
라. java.util의 공통 함수형 인터페이스를 사용하는 경우
•
Stream 부분도 간략해지고 interface 정의부분이 생략되어 코드의 가독성면에서 제일 낫다
public class Classifier {
public static void main(String[] args) {
final int START_NUM = 2;
final int END_NUM = 100;
Function<Integer, String> modifyString = (number) -> {
String result = "";
if (isPerfect.test(number)) result += number + " : perfect, ";
else if (isDeficient(number)) result += number + " : deficient, ";
else if (isAbundant(number)) result += number + " : abundant, ";
if (isPrime(number)) result += "prime";
return result;
};
IntStream.rangeClosed(START_NUM, END_NUM)
.boxed() // Int -> Integer
.map(modifyString::apply) // 각 Integer를 완전수, 부족수 등으로 분류
.forEach(System.out::println);
}
}
Java
복사
마. 함수형 인터페이스를 매개변수 및 반환형으로 사용하는 경우
•
공통 함수형 인터페이스 Function<T, R>로 정의됨
•
매개변수로 함수형 인터페이스(<BiFunction<Integer, Integer, Boolean>>)를 전달
•
반환형으로 함수형 인터페이스(IntFunction<Set<Integer>>)를 받음
→ bold 처리한 부분이 반환됨(as 함수형 인터페이스)
// 코드스쿼드 K의 코드를 참고함
private static final Function<BiFunction<Integer, Integer, Boolean>, IntFunction<Set<Integer>>> factors
= (isFactor) -> (number) -> {
HashSet<Integer> factors = new HashSet<>();
for (int pod = 1; pod <= Math.sqrt(number); pod++) {
if (isFactor.apply(number, pod)) {
factors.add(pod);
factors.add(number / pod);
}
}
return factors;
};
Java
복사
Reference
•
FP 개념 다지기, https://ko.wikipedia.org/wiki/함수형_프로그래밍