티스토리 뷰
- 함수형 코드를 작성하기 위해서는, 함수형 언어인 스칼라나 클로저로의 전환이 필요한 것이 아니라 문제에 접근하는 방식의 전환이 필요하다.
명령형 처리와 함수형 처리
- 명령형 처리
- 전통적인 프로그래밍 방식
- 문제를 명령형 루프내에서 수행
- 예제 : 전형적인 회사 프로세스
- 어떤 이름 목록에서 한 글자로 된 이름을 제외한 모든 이름을 대문자화해서 쉼표로 연결한 문자열을 구한다.
- 한 글짜짜리 이름을 필터
- 목록에 남아있는 이름을 대문자로 변형
- 이 목록의 하나의 문자열로 변환
package com.promiseflower.functionalthinking.ch2; import android.os.Build; import java.util.List; import java.util.stream.Collectors; import androidx.annotation.RequiresApi; public class TheCompanyProcess { public String cleanNames(List listOfNames) { StringBuilder result = new StringBuilder(); for (int i = 0; i < listOfNames.size(); i++) { 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()); } } |
- 함수형 처리
- 함수형 처리는 언어에서 제공하는 함수를 이용하여 서술형으로 문제를 처리
- 필터, 변형, 변환등의 논리적인 분류를 저수준의 작업이라 칭하여 문제를 처리하는 방식보다는 세부적인 로직에 집중할 수 있다.
- 개발자는 고계함수에 매개변수로 주어지는 함수를 이용하여 저수준의 작업을 커스터마이징할 수 있다.
- 예제 : 전형적인 회사 프로세스
// 예제 : 전형적인 회사 프로세스에 대한 의사코드 listOfEmps -> filter(x.length > 1) -> transform(x.capitalize) -> convert(x + "," + y) // 예제 : 전형적인 회사 프로세스에 대한 코틀린으로 작성한 함수형 처리 class TheCompanyProcessWithKotlin { fun cleanNames(listOfNames: List): String { return listOfNames .filter { it.length > 1 } .map { it.capitalize() } .reduce { acc, s -> "$acc,$s" } } } // capitalize() : 최초문자가 대문자인 카피형을 반환 |
-
filter() 함수를 통해 이름목록에서 한 글자 이름을 필터해서 제거
- 목록 내의 null이 있을 경우를 대비에서 filter() 함수에서 null 체크를 할 수 있다.
-
map() 함수를 통해 주어진 코드 블록에서 최초문자가 대문자인 카피형을 반환하는 capitalize()를 통해 값을 반환
-
reduce() 함수를 통해 각 요소를 주어진 코드 블록에 규칙에 따라 결합한다.
-
함수형 사고로의 전환은 어떤 경우에 세부적인 구현에 뛰어들지 않고 이런 고수준 추상 개념을 적용할지를 배우는 것
- 고수준의 추상적 사고로 얻는 이점
- 문제의 공통점을 고려하여 다른 방식으로 분류하기를 권장
- 런타임이 최적화를 잘할 수 있도록 해줌
- 어떤 경우에는 결과가 변하지 않는 한, 작업 순서를 바꾸면 더 능률적이 된다.
- 개발자가 엔진 세부사항에 깊이 파묻힐 경우 불가능한 해답을 가능하게 한다.
- 자바 코드를 여러 스레드에 나누어 처리하게끔 할 때, 개발자가 저수준의 반복과정을 제어해야 하기 때문에, 스레드 관련 코드가 문제해결 코드로 섞일 수 밖에 없다.
- 이러한 내용을 간단한 병렬처리로 대체할 수 있다.
- 고수준의 추상적 사고로 얻는 이점
// 예제 : 코틀린에서의 병렬 처리 fun cleanNames(listOfNames: List): String { return listOfNames .parallelStream() .filter { it.length > 1 } .map { it.capitalize() } .reduce { acc, s -> "$acc,$s" } .toString() } // 예제 : 자바8 버전에서의 분산 처리 public String cleanNames(List listOfNames) { if (listOfNames == null) return ""; return listOfNames .parallelStream() .filter(n -> n.length() > 1) .map(e -> capitalizeString(e)) .collect(Collectors.joining(",")) .toString() } |
사례 연구 : 자연수의 분류
- 자연수 분류법
- 완전수 : 자신의 모든 양의 약수의 합
- 양의 약수 : 곱해서 대상의 값이 나오는 두 수 중 자신을 제외한 수
- 예) 6은 약수가 1,2,3,6 이고, 6 = 1+2+3이므로 완전수
- 완전수 : 진약수의 합 = 수
- 과잉수 : 진약수의 합 > 수
- 부족수 : 진약수의 합 < 수
// 예제 : 자바를 사용한 자연수 분류 import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; 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 Set getFactors() { Set 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); } public boolean isPerfact() { // 완전수 return aliquotSum() == _number; } public boolean isAbundant() { // 과잉수 return aliquotSum() > _number; } public boolean isDeficient() { // 부족수 return aliquotSum() < _number; } } |
// 예제 : 자바8의 람다, 고계함수와 스트림을 통한 함수형 자연수 분류기 에제 import java.util.stream.IntStream; import static java.util.stream.IntStream.range; public class NumberClassifier { public static IntStream factorsOf(int number) { return range(1, number + 1).filter(potential -> number % potential == 0); } 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; } } |
// 예제 : 코틀린을 사용한 자연수 분류기 object NumberClassifier { private fun factorsOf(number : Int) : List { return (1 .. number + 1).filter { potential -> number % potential == 0 } } fun aliquotSum(number: Int): Int { return factorsOf(number).sum() - number } fun isPerfect(number: Int): Boolean { // 완전수 return aliquotSum(number) == number } fun isAbundant(number: Int): Boolean { // 과잉수 return aliquotSum(number) > number } fun isDeficient(number: Int): Boolean { // 부족수 return aliquotSum(number) < number } } |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- constraintlayout
- kotlin
- 조세퍼스 문제
- 1237
- javacv
- 10827
- #kotlin
- algorihtm
- 단어 길이 재기
- 피보나치 수 4
- 영상처리
- 10757
- mssql
- 자동타입
- algorithm
- #android #motionlayout
- 10826
- 10828
- 함수형사고 Kotlin Java
- 1158
- a^b
- 2743
- 최대공약수와 최소공배수
- 1260
- Eclipse
- OpenCV
- 문자열
- GCD 합
- 큰 수 A+B
- 알고리즘
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
글 보관함