본문 바로가기
java

함수형 프로그래밍

by 오우지 2021. 12. 15.

 

프로그래밍 패러다임은 크게 아래와 같다.

 

1. 명령형 프로그래밍: 무엇을 할 것인지 나타내기보다 어떻게 할 건지를 설명하는 방식, 원하는 결과를 얻기 위해 특정 단계를 설명하는 코드 라인을 사용

    - 절차지향 프로그래밍: 수행되어야 할 순차적인 처리 과정을 포함하는 방식(C)

    - 객체지향 프로그래밍: 객체들의 집합으로 프로그램의 상호작용을 표현(C++, JAVA)

 

2. 선언형 프로그래밍: 어떻게 할건지 나타내기보다 무엇을 할 건지 설명하는 방식, 흐름 제어를 추상화하고 데이터 흐름을 설명하는 코드 라인을 사용

    - 함수형 프로그래밍: 순수 함수를 조합하고 소프트웨어를 만드는 방식(하스켈, 리스프)

 

 

코드로 이해하는게 제일 쉽다고 생각한다. 아래 코드를 보면

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WordCount {
    private static List<String> WORDS = Arrays.asList("TONY", "a", "hULK", "B", "america", "X", "nebula", "Korea");

    private static Map<String, Integer> wordPrefixFreq() {
        Map<String, Integer> wordCountMap = new HashMap<>();
        String prefix;
        Integer count;
        for (String word : WORDS) {
            prefix = word.substring(0, 1);
            count = wordCountMap.get(prefix);
            if (count == null) {
                wordCountMap.put(prefix, 1);
            } else {
                wordCountMap.put(prefix, count + 1);
            }
        }
        return wordCountMap;
    }

    public static void main(String[] args) {
        final Map<String, Integer> map = wordPrefixFreq();
        map.keySet().forEach(k -> System.out.println(k + ": " + map.get(k)));
    }
}

출처: https://mangkyu.tistory.com/111 [MangKyu's Diary]

우리가 하던 평범한 어떻게 할 건지를 적은 코드라고 볼 수 있다.

 

함수형 프로그래밍은

 

1. 생성하기

2. 가공하기

3. 결과 만들기

 

순서대로 진행한다고 생각하면 된다. 아래 코드를 보면

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WordCount {
    private static List<String> WORDS = Arrays.asList("TONY", "a", "hULK", "B", "america", "X", "nebula", "Korea");

    private static Map<String, Integer> wordPrefixFreq() {
        Map<String, Integer> wordCountMap = new HashMap<>();
        WORDS.stream()
                .map(w -> w.substring(0, 1))
                .forEach(prefix -> wordCountMap.merge(prefix, 1, (oldValue, newValue) -> (newValue += oldValue)));
        return wordCountMap;
    }

    public static void main(String[] args) {
        final Map<String, Integer> map = wordPrefixFreq();
        map.keySet().forEach(k -> System.out.println(k + ": " + map.get(k)));
    }
}

출처:https://mangkyu.tistory.com/111 [MangKyu's Diary]

어떻게 할 건지보다 무엇을 할지에 대한 명시가 더 많은 것을 알 수 있다.

 

자바8 이후의 문법에서 지원하기 시작한 Stream의 특성으로는

  • 원본의 데이터를 변경하지 않는다.
  • 일회용이다.
  • 내부 반복으로 작업을 처리한다.

자바에서 map, filter, sorted 등등이 존재하고 마지막에 collect로 결과를 만들어 줄 수 있다. 궁금하다면 저기 아래 링크에서 확인 가능하다. 그래서 함수형 프로그래밍이 무엇인지 다시 얘기해보자면

 

함수형 프로그래밍: 부수 효과가 없는 순수 함수를 1급 객체로 간주하여 파라미터로 넘기거나 반환값으로 사용할 수 있으며, 참조 투명성을 지킬 수 있다.

 

부수 효과: 

1. 변수의 값이 변경됨

2. 자료 구조를 제자리에서 수정함

3. 객체의 필드값을 설정함

4. 예외나 오류가 발생하며 실행이 중단됨

5. 콘솔 또는 파일 I/O가 발생함

 

순수 함수: 같은 입력에 대해 항상 같은 출력을 반환하는 함수로 멀티쓰레드에서 안전하고 병렬처리 및 연산이 가능하다.

   - 동일한 입력에 대해 항상 같은 값을 반환

   - 부작용이 없는 결과를 생성 -> 함수에서 인자를 변경하거나 프로그램의 상태를 변경하지 않는다.

 

 

1급 객체(1급 함수) : 보통 자바스크립트에서 많이 나오는 개념으로 함수형 프로그래밍의 전제. 조건으로는

1. 변수나 데이터 구조 안에 넣을 수 있다.

2. 파라미터로 전달할 수 있다.

3. 동적으로 프로퍼티 할당이 가능하다.

4. 리턴값으로 활용할 수 있다.

 

자바의 경우 함수형 인터페이스를 통해 구현이 가능하다.

 

참조 투명성: 

    - 동일한 인자에 대해 항상 동일한 결과를 반환

    - 기존의 값이 변경되지 않고 유지된다.

 

 

 

명령형 프로그래밍과 함수형 프로그래밍의 함수는 부수효과에서 차이가 있다. 부수효과에 따라서 함수가 참조에 투명한지 안한지 나뉘어지는데, 참조에 투명한 것은 함수를 실행해도 상태의 변화 없이 항상 동일한 결과를 반환해서 항상 동일하게(투명하게) 실행결과를 예측(참조)할 수 있는것을 의미한다.

 

함수형 프로그래밍은 부작용을 제거해 프로그램의 동작을 이해하고 예측을 용이하게 하고 다수의 스레드를 이용한 병렬 처리 환경에서 race condition에 대한 비용을 줄여준다.

 

그래서 함수형 프로그래밍을 왜 써야 하는가?

 

우리가 Collection에서 데이터를 가공하기 위해 반복문을 사용하는 것과 Stream을 사용하는 것의 차이는 외부 반복과 내부 반복의 차이인데 Stream은 내부 반복을 사용한다.

 

Stream은 람다식을 사용해 별도의 스레드에서 병렬 처리를 하는데 내부에서 외부지역 변수를 참조하려 할 때 final한 변수를 복사해서 가져가는데 이 복사한 값을 람다식 내부에서 변경하려 하면 컴파일 에러가 발생한다. 

 

간단하게 JVM 스택은 스레드 별로 가지는 영역으로 스레드 간에는 공유되지 않는다. 그리고 지역변수는 JVM 스택 영역에 저장된다. 람다식은 별도의 스레드를 가지기 때문에 다른 스레드의 JVM 스택 안의 지역변수를 참조할 수 없다. 그래서 final한 변수를 복사해서 가져가는 것이다.

 

복사한 값을 람다식 내부에서 변경하면 컴파일 에러가 발생한다. 기존의 반복문을 사용하면 반복문 내에서 지역변수의 값이 변경 가능하기 때문에 final로 변수를 제한함으로써 병렬처리에서 발생할 수 있는 동시성 문제를 막는다.

 

하지만 이런 함수형 프로그래밍에도 단점이 있는데. 딱 봐도 리소스가 많이 든다. 본인의 선택이라고 할 수 있다.

 

결론적으로 함수형 프로그래밍은 프로그래밍 언어나 방식을 배우는 것이 아닌, 함수로 프로그래밍 하는 사고를 배우는 것이다.

 

 

 

출처: https://mangkyu.tistory.com/112 [MangKyu's Diary]

https://dinfree.com/lecture/language/112_java_9.html#m3

https://bbbicb.tistory.com/50

'java' 카테고리의 다른 글

1. 생성자 대신 정적 팩터리 메서드를 고려하라  (0) 2023.02.19
또다시 Optional  (0) 2021.12.01