이글을 쓴 이유
객체 지향과 함수형 프로그래밍을 대비하는 글이 많은데 그건 제 생각에는 잘못 된 견해 같습니다. 그래서 어떻게 잘못 된 지에 대한 저의 의식의 흐름을 글로 옮기고 싶어서 글을 썼습니다.
일단! 실제로 함수형 프로그래밍의 대비 되는 것은 명령형 프로그래밍이고, 객체지향 프로그래밍과 대비 되는것은 절차 지향 프로그래밍입니다.
함수형 프로그래밍이란?
상태값을 지니지 않는 함수값들의 연속
자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 하나이다.
쉽게 말해 one-to-one Correspondence했으면 하는게 함수형 프로그래밍이다. 함수형 프로그램과 상반 대는 개념이 명령형 프로그래밍인데 같은 input값에도 다른 output값이 나오는 그런 프로그래밍 이다.
가장 쉽게 일어날 수 있는 법(?)은 함수 안에 random함수를 넣으면 일어난다.
여기에는 세가지 함수를 쓰는게 목표인데
-
순수한 함수 즉, 함수의 실행이 외부에 영향을 끼치지 않는 함수를 뜻한다. 따라서 순수한 함수는 스레드 안전하고, 병렬적인 계산이 가능
- 익명 함수
- 고계 함수
함수를 다루는 함수를 뜻한다. 사실 함수형 언어에서는 함수도 ‘값(value)’으로 취급
Map, filter,reduce 같은 것
함수형 프로그래밍은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변적인 데이터를 배제하는 프로그래밍 기법
함수형 프로그래밍으로 코딩하는 법
함수형 프로그래밍은 좀 더 추상화된 레벨에서 코딩하는 것
일반적 처리
import java.util.List;
public class TheCompanyProcess {
public String cleanNames(List<String> names) {
StringBuilder result = new StringBuilder();
for (String name : names) {
if (name.length() > 1)
result.append(capitalizeString(name)).append(" ,");
}
return result.substring(0, result.length() - 1);
}
private String capitalizeString(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
}
작성한 로직을 다음 세 개의 그룹
- 한 글자 이름을 걸러내고(filter)
- 이름 첫 글자를 대문자로 변형하고(transform)
- 쉼표로 구분한 하나의 문자열로 변환한다(convert)
함수형 처리
해당 작업을 루프 안에서 직접 기술하는 것이 아니라 추상화된 메소드(filter, transform, convert)를 이용해 작업할 수 있고, 세부 사항에 대한 내용은 함수를 인자로 넘겨줘서 처리할 수 있습니다.
의사코드
listOfNames
-> filter(x.length > 1)
-> transform(x.capitalize)
-> convert(x + "," + y)
java8로 구현을 하면
public String cleanNamesWithJava8(List<String> names) {
if (names == null) return "";
return names.stream()
.filter(name -> name.length() > 1)
.map(this::capitalize)
.collect(Collectors.joining(","));
}
private String capitalize(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
이렇게 된다… 근데 역시 .. 좀 그렇다(나만인건가)
val employees = List("tony", "a", "steve", "captain", "b", "thor", "hulk", "c");
val result = employees
.filter(_.length > 1)
.map(_.capitalize)
.reduce(_ + "," + _)
의사코드와 거의 똑같습니다. 아주 간결하고 쉽게 읽히죠. 물론 함수의 이름은 map 이나 reduce 로 바뀌었지만 역할은 같다. 그리고 인자로 넘어가는 함수 모두 사용하는 변수의 이름은 크게 상관이 없기 때문에 스칼라에서는 이름을 생략하고 언더바 (_)
를 사용합니다.
함수형으로 작성하면 더 추상적인 레벨에서 코드를 작성할 수 있습니다. 이렇게 추상적으로 작업을 하면 코드가 간결할 뿐만 아니라 런타임에서 최적화를 해줘서 성능을 높여주고 엔진 레벨에서 처리해야 하는 코드에 신경쓰지 않게 도와줍니다. 예를 들어 쓰레드를 이용해 병렬 처리를 해야 할 경우엔 par 를 붙여서 병렬 스트림을 만들기만 하면 됩니다.
참고 : https://futurecreator.github.io/2018/10/07/functional-programming-filter-map-fold-reduce/