4. 명령형 처리(Java)
public class TheCompanyProcess {
public String cleanNames(List<String> listOfNames) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < listOfNames.size(); i++) { // loop 사용이 일반적
if (listOfNames.get(i).length() > 1) {
// 한 문자 이상인 문자열을 연결
result.append(capitalizeString(listOfNames.get(i))).append(",");
}
}
return result.substring(0, result.length() - 1).toString();
}
public String capitalizeString(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1, s.length());
}
}
5. 함수형 처리(scala)
val employees = List("neal", "s", "stu", "j", "rich", "bob", "aiden",
"j", "ethan", "liam", "mason", "noah", "lucas",
"jacob", "jayden", “jack")
val result = employees
.filter(_.length() > 1) // 필터
.map(_.capitalize) // 변형(Transform)
.reduce(_ + "," + _) // 변환(convert)
수학 공식을 모델링하는 표현과 변형으로 기술하며, 가변 상태를 지양
6. 무엇이 다른가?
명령형 처리: How를 기술
함수형 처리: What을 기술
문자열 예제의 What?
1. len(x) > 1인 큰 문자열을 찾음
2. 대문자로 변환
3. 문자열을 연결
7. Java 8
public class Process {
public String cleanNames(List<String> names) {
if (names == null) return "";
return names.stream() // stream을 생성
.filter(name -> name != null)
.filter(name -> name.length() > 1)
.map(name -> capitalize(name))
.collect(Collectors.joining(",")); // reduce의 특별한 경우
}
private String capitalize(String e) {
return e.substring(0, 1).toUpperCase() + e.substring(1, e.length());
}
}
8. Groovy
public class TheCompanyProcess {
public static String cleanUpNames(listOfNames) {
listOfNames
.findAll { it.length() > 1 } // 필터
.collect { it.capitalize() } // 변형
.join ',' // 변환
}
}
9. Clojure
; 기본적인 clojure 버전
; 리스프 계열 언어는 안에서 밖으로 실행됨.
(defn process [list-of-emps]
(reduce str (interpose ","
(map s/capitalize
(filter #(< 1 (count %)) list-of-emps)
))))
; 매크로를 사용한 가독성 향상 버전
(defn process2 [list-of-emps]
(->> list-of-emps
(filter #(< 1 (count %)))
(map s/capitalize)
(interpose ",")
(reduce str)
))
11. 고수준 추상적 사고의 이점
문제를 다른 시각에서 분류할 수 있게 함
런타임에 최적화가 가능(작업 순서 조정)
개발자가 엔진 세부사항을 고려하지 않으면 할 수 없던 일을
가능하게 함(ex: 병렬 처리)
12. 고수준 추상화의 스레드 적용
// Scala
val parallelResult = employees
.par // 스레드로 처리
.filter(_.length() > 1)
.map(_.capitalize)
.reduce(_ + "," + _)
// java 8
public String cleanNamesP(List<String> names) {
if (names == null) return "";
return names
.parallelStream() // 스레드로 처리
.filter(n -> n.length() > 1)
.map(e -> capitalize(e))
.collect(Collectors.joining(","));
}
13. 사례 연구
예제: 자연수의 분류
6(완전수) = 1 + 2 +3
분류 조건
완전수 N의 진약수의 합 = N
과잉수 N의 진약수의 합 > N
부족수 N의 진약수의 합 < N
14. 명령형 자연수 분류
public class ImpNumberClassifierSimple {
private int _number;
private Map<Integer, Integer> _cache; // 내부 캐시
public ImpNumberClassifierSimple(int targetNumber) {
_number = targetNumber;
_cache = new HashMap<>();
}
// 약수인가?
public boolean isFactor(int potential) {
return _number % potential == 0;
}
public boolean isPerfect() {
return aliquotSum() == _number;
}
public boolean isAbundant() {
return aliquotSum() > _number;
}
public boolean isDeficient() {
return aliquotSum() < _number;
}
// 진약수 구하기
public Set<Integer> getFactors() {
Set<Integer> factors = new HashSet<>();
factors.add(1);
factors.add(_number);
for (int i = 2; i < _number; i++)
if (isFactor(i)) factors.add(i);
return factors;
}
// 진약수의 합
public int aliquotSum() {
if (_cache.get(_number) == null) {
int sum = 0;
for (int i : getFactors())
sum += i;
_cache.put(_number, sum - _number);
}
return _cache.get(_number);
}
}
15. Java 8을 사용한 자연수 분류
public class NumberClassifier {
// 함수 호출시 실제 작업이 수행되는 것이 아님. stream을 반환
public static IntStream factorsOf(int number) {
return range(1, number + 1)
.filter(potential -> number % potential == 0);
}
// sum 내부에서 stream을 해석
public static int aliquotSum(int number) {
return factorsOf(number).sum() - number;
}
public static boolean isPerfect(int number) {
return aliquotSum(number) == number;
}
public static boolean isAbundant(int number) {
return aliquotSum(number) > number;
}
public static boolean isDeficient(int number) {
return aliquotSum(number) < number;
}
}
17. 필터
Figure 2-1. Filtering a list of numbers from a larger list
When filtering, you produce another list (or collection) potentially smaller than the
original, depending on the filtering criteria. In the number-classifier example, I use
filtering to determine the factors of a number, as shown in Example 2-14.
Example 2-14. Filtering in Java 8
public static IntStream factorsOf(int number) {
return range(1, number + 1)
.filter(potential -> number % potential == 0);
}
The code in Example 2-14 creates a range of numbers from 1 to the target number, then
사용자가 지정한 조건으로 목록을 더 작은 목록으로 만드는 작업
public static IntStream factorsOf(int number) {
return range(1, number + 1)
.filter(potential -> number % potential == 0);
}
18. 맵
리스트의 각 요소에 같은 함수를 적용하여 새로운 리스트를 생성
// 약수 구하기 최적화 버전.
static def factors(number) {
def factors = (1..round(sqrt(number)+1)).findAll({number % it == 0})
(factors + factors.collect {number / it}).unique()
}
Figure 2-2. Mapping a function onto a collection
To illustrate map() and related transformations, I create an optimized version of my
number classifier. First, I create an imperative version, shown in Example 2-16.
Example 2-16. Number classifier optimized
import java.util.HashMap;
import java.util.HashSet;
19. 폴드/리듀스
Figure 2-3. Fold operation
Because addition is commutative, it doesn’t matter if you do a foldLeft() or fold
Right(). But some operations (including subtraction and division) care about order,
so the foldRight() method exists to handle those cases. In purely functional languages,
leftandrightfoldshaveimplementationdifferences.Forexample,rightfoldscanoperate
on infinite lists whereas left folds cannot.
Example 2-13 uses the Functional Java-supplied add enumeration; the framework in‐
cludes the most common mathematical operations for you. But what about cases in
누산기(Accumulator)를 사용해서 값을 모으는 작업
reduce, foldLeft => (f (f (f (f 0,N1) N2) N3) N4)
foldRight => (f N1 (f N2 (f N3 (f N4 0))))
foldLeft