-
챕터[18] 함수형 관점으로 생각하기모던자바인액션 2020. 10. 22. 00:44
서론
함수형 프로그래밍을 이용하지 않으면 메소드의 호출로 인하여 다른 객체가 변경될 경우 부작용이 발생할 수 있다. 이러한 부작용은 결국 개발자 입장에서 유지보수를 하기 힘들게 만들며, 병렬로 처리하는 과정에서는 언제 어떻게 값이 변경되는지 추적할 수가 없어진다.
공유된 가변 데이터
어떠한 자료구조도 바꾸지 않으며(자신을 포함하는 클래스의 상태 및 다른 객체) return 문을 통해서 자신의 결과를 반환하는 메서드를 순수 메서드 혹은 부작용 없는 메서드라고 불린다.
부작용 없는 메서드의 부작용이란?
-
자료구조를 고치거나 필드에 값을 할당
-
예외 발생
-
파일에 쓰기 및 I/O 동작
선언형 프로그래밍
어떻게로 접근하는 방식을 선언형프로그래밍이라고 부르기도 한다. 선언형 프로그래밍에서는 우리가 원하는 것이 무엇이고 시스템이 어떻게 그 목표를 달성할 것인지 등의 규칙을 정한다. 문제 자체가 코드로 명확하게 드러나는 점이 선언형 프로그래밍의 장점이다.
도대체 함수형 프로그래밍은 무엇인가?
-
함수는 0개 이상의 인수를 가지며, 한 개 이상의 결과를 반환하지만 부작용이 없어야 한다.
-
자바에서는 순수 함수형이 아니라 함수형 프로그램을 구현할 것이라 실제로는 부작용이 있지만 아무도 보지 못하게 하여 함수형을 당성할 수 있다.
함수형 조건
-
지역 변수만 변경한다.
-
객체를 참조한다면 그 객체는 불변성 객체여야 한다.
-
함수나 메서드가 어떤 예외도 하면 안된다.
참조 투명성
-
부작용을 감추어야 한다는 제약은 참조 투명성 개념으로 귀결된다.
-
같은 인수로 함수를 호출 시 결과 값은 항상 같아야 한다. 그렇기에 Scanner로 값을 입력받는 다면 참조 투명성을 위배하는 것이다.
-
동일한 값을 가진 객체를 생성 후 반환 하는 함수를 n번 호출할 경우 값은 동일하지만 서로 다른 객체가 생성되어 다른 메모리에 할당이 된다. 하지만 이러한 문제도 참조 투명성으로 간주된다.
함수형 실전 연습 예제는 머리가 딸려서 스터디날 물어보겠습니다.
재귀와 반복
-
순수 함수형 프로그래밍 언어에서는 while, for 같은 반복문을 사용하지 않는다.
-
반복이 이루어 지면서 루프를 탈출하는 조건을 충족하기 위하여 다른 객체를 참조하여 변경이 될 수 있기에 사용하지 않는다.
-
그렇기에 반복문 대신 변화가 없는 재귀를 사용한다.
@Slf4j public class ch18FunctionalProgramming { // 반복 팩토리얼 public static int factorialIterator(int n) { int r = 1; for (int i = 1; i <= n; i++) { r *= i; log.info("r : {}, i : {}", r, i); } return r; } // 재귀 팩토리얼 public static long factorialRecursive(long n) { log.info("n : {}", n); return n == 1 ? 1 : n * factorialRecursive(n-1); } // 스트림 팩토리얼 public static long factorialStreams(long n) { log.info("n : {}", n); return LongStream.rangeClosed(1, n) .reduce(1, (long a, long b) -> a * b); } // 꼬리 재귀 팩토리얼 public static long factorialTailRecursive(long n) { log.info("n : {}", n); return factorialHelper(1, n); } // 꼬리 재귀 팩토리얼 헬퍼 public static long factorialHelper(long acc, long n) { log.info("acc : {}, n : {}", acc, n); return n == 1 ? acc : factorialHelper(acc * n, n-1); } }
재귀
- 재귀 메서드는 일반적 반복 메소드에 비하여 코드가 더 비싸다.
- factorialRecursive 함수를 호출할 때마다 호출리얼의 입력값에 비례해서 메모리 사용량이 증가한다. 그렇기에 큰 값을 입력할 경우 StackOverFlow가 발생한다.
꼬리재귀
재귀에 문제점을 해결한 재귀방법이 꼬리 재귀이다.
-
중간 결과를 각각 스택 프레임에 저장해야 하는 일반 재귀와 달리 꼬리 재귀에서는 컴파일러가 하나의 스택 프레임을 재활용할 가능성이 생긴다.
-
사실 factorilHelper의 정의에서는 중간 결과를 함수의 인수로 직접 전달한다.
하지만 자바 8에서는 스트림으로 대체하여 간결하며 부작용 없이 제작할 수 있다.
git :
'모던자바인액션' 카테고리의 다른 글
챕터[21] 결론 그리고 자바의 미래 (0) 2020.10.29 챕터[20] OOP와 FP의 조화 : 자바와 스칼라 비교 (0) 2020.10.22 챕터[17] 리엑티브 프로그래밍 (0) 2020.10.22 챕터[16] 안정적 비동기 프로그래밍 (0) 2020.10.21 챕터[15] CompletableFuture와 리액티브 프로그래밍 컨셉의 기초 (0) 2020.10.21 -