함수형 프로그래밍 기법#1

2018, May 15    
함수형을 지향하기 위한 프로그래밍 기법을 알아보자.
  • 일급 시민, 고차원 함수, 커링, 부분 적용
  • 영속 자료구조
  • 자바 스트림을 일반화하는 게으른 평가와 게으른 리스트
  • 패턴 매칭, 자바에서 패턴 매칭을 흉내 내는 방법
  • 참조 투명성과 캐싱

#일급 시민

함수를 일급 객체 시민으로(fisrt-class object citizen)

이 단어는 크리스토퍼 스트래치(Christopher Strachey)라는 영국 컴퓨터 과학자가 60년대에 만들어 낸 단어로, 함수를 일반 객체처럼 사용해서 인수로 전달하거나, 결과로 반환받거나, 자료구조에 저장할 수 있음을 의미한다.

  • 변수나 데이터 구조안에 담을 수 있다.
  • 파라미터로 전달 할 수 있다.
  • 반환값(return value)으로 사용할 수 있다.
  • 할당에 사용된 이름과 관계없이 고유한 구별이 가능하다.
  • 동적으로 프로퍼티 할당이 가능하다.

고차원 함수(고계함수 higher-order functions)  :  이와 같이 함수를 일급 시민으로 사용하여 하나 이상의 동작을 수행하는 함수를 라고 한다.


#커링(currying)

함수를 일급 객체로 쓰기 위해 사용되는 커링이라는 기법에 대해 알아보자.

Currying은 수학자 하스켈 커리로 부터 유래된 이름으로 함수를 변형하는 과정을 말한다.
함수의 전달인자 몇개를 미리 채움으로써 더 간단한 함수를 만드는 방법이다.
참고 링크 : https://www.linkedin.com/pulse/functional-programming-applying-currying-java-figueras
예제를 통해 알아보자.

//아래와 같이 섭씨를 화씨로 바꿔주는 함수가 있다.
public int tempConverter(int x, int y, int z) {
  return x*y+z;
}
//--> 넘겨받는 인자중 x를 리턴해 주는 함수로 교체해본다면 어떨까?
public IntUnaryOperator curryConverter(int y, int z) {
  return (int x)->x*y+z;
}
//수정한 함수 curry를 이용해서 섭씨를 화씨로
//바꾸는 여러가지 기준의 함수를 정의 할수 있다.
IntUnaryOperator convertA = curryConverter(3,5);
IntUnaryOperator convertB = curryConverter(2,6);
int resulta = convertA.applyAsInt(10); //10*3+5
int resultb = convertB.applyAsInt(10); //10*2+6;
    

함수 curryConverter는 x, y, z를 넘기지 않고 y,z만을 넘겨 받고, 다시 x라는 값을 받아 연산하는 함수를 리턴한다.
하지만 원래 우리가 도달하고자 했던 x*y+z를 한번의 호출로 반환하는 함수는 아니다.
이와 같이 여러 과정이 끝까지 완료 되지 않은 상태를 가리켜 함수가 부분적으로partially적용 되었다라고 말한다.
이런 방식으로 변환 로직을 재활용할 수 있으며 다양한 변환 요소로 다양한 함수를 만들 수 있다.


#영속 자료구조

  • 자료의 불변성(Immutable)을 유지 하는 것을 말한다.
  • 오해하지 말하야 할것은 함수형 언어가 근본적인 문제를 해결해 주지는 않는다.
  • 다만 코딩하는 사람에게 제약을 조금 더 주어 변경을 난발하게 못하게 할 뿐이다.
  • 클로저에서 함수는 변경 불가능한 자료를 다루기 때문에 함수의 결과가 새로운 값을 리턴한다.
  • 내부적으로는 공유 데이터를 사용하기 때문에 중복 데이터에 대한 성능 문제는 발생하지 않는다.
  • 참고 링크 : [프로그래밍 클로저] 5장 함수형 프로그래밍
예) 다음과 같은 linkedList가 있다.
class TrainJourney {
  public int price;
  public TraiinJourney onward;
  public TrainJourney(int p, TrainJourney t) {
    price = p;
    onward = t;
  }
}
인자의 갱신이 일어나는 함수
public TrainJourney link(TrainJourney a, TrainJourney b) {
    if (a == null) {
        return b;
    }
    TrainJourney t = a;
    while (t.onward != null) {
      //반복적으로 인자 a의 값의 변형이 발생한다.
        t = t.onward;
    }
    t.onward = b;
    return a;
}      
      
함수형 프로그래밍
public TrainJourney append(TrainJourney a, TrainJourney b) {
    //기존의 자료구조를 갱신하지 않도록 중단 단계의 계산결과를 새로운 자료구조로 반환
    return a == null ? b : new TrainJourney(a.price, append(a.onward, b));
}