일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- web
- 코틀린
- DDD
- 개발방법론
- 만들면서배우는클린아키텍처
- 유지보수
- 클린아키텍처
- Thymeleaf
- 책스터디
- Kotlin
- 함수형프로그래밍
- 아키텍처
- 스터디
- 이펙티브코틀린
- 테스트주도개발
- GrokkingFunctionalProgramming
- 조영호
- 개발서적
- template
- Spring
- TDD
- 추상화 설계
- 테스트
- 객체지향의사실과오해
- Boot Legacy 차이점
- FP
- 헥사고날아키텍처
- 도메인 주도 개발 시작하기
- 계층형아키텍처
- Java
- Today
- Total
김동형수 개발기
Grokking Functional Programming - 1부 4장 본문
Grokking Functional Programming - 1부 4장
김동형수 2022. 11. 30. 23:234부 가치로서의 기능
이 장에서는 다음을 수행하는 방법을 배웁니다.
- 함수를 인수로 전달
- sortBy 함수 사용
- 맵 및 필터 기능 사용
- 함수에서 함수 반환
- 함수를 값으로 취급
- foldLeft 기능 사용
- 제품 유형을 사용하여 불변 데이터 모델링
4.1 요구 사항을 기능으로 구현
기능 측면에서 비즈니스 요구 사항을 생각하고 순수 기능을 값으로 취급하는 방법이 얼마나 유용한지 알아봅니다.
단어 순위
· 주어진 단어의 점수는 'a' 가 아닌 각 문자에 대해 1점을 주어 계산됩니다.
· 주어진 단어 목록에 대해 가장 높은 점수를 받은 단어로 시작하는 정렬된 목록을 반환합니다.
4.2 불순한 함수와 가변 값 반격
주어진 단어에 대한 점수를 반환하는 함수가 이미 있습니다.
static int score(String word) {
return word.replaceAll("a", "").length();
}
버전 #1: Comparator 및 정렬 사용
static Comparator<String> scoreComparator =
new Comparator<String>() {
public int compare(String w1, String w2) {
return Integer.compare(score(w2), score(w1));
}
};
static List<String> rankedWords(List<String> words) {
words.sort(scoreComparator);
return words;
}
4.3 Java 스트림을 사용하여 목록 정렬
Java의 Stream을 사용하면, 순수함수의 규칙을 어기지 않으면서 Comparator를 사용할 수 있다.
· 단일 값만 반환합니다.
· 인수에 따라서만 반환 값을 계산합니다.
· 기존 값을 변경하지 않습니다.
버전 #2: Streams를 사용하여 단어 순위 지정
static List<String> rankedWords ( List<String> words ) {
return words.stream()
.sorted(scoreComparator)
.collect (Collectors.toList());
}
가장 중요한 것은 이 스니펫에서 변경된 것이 없다는 것입니다
4.4 함수 서명은 모든 것을 말해야 합니다.
시그니처를 볼 때 이 함수가 주어진 단어를 정렬하는 방법을 어떻게 알 수 있습니까?
함수의 두 번째 규칙으로 표현되는 매개변수 목록에 대한 설명이 있어야 합니다. 함수는 인수에 따라서만 반환 값을 계산합니다.
버전 #3: 알고리즘을 인수로 전달
4.5 요구 사항 변경
버전 #4: 순위 알고리즘 변경
· 주어진 단어의 점수는 'a' 가 아닌 각 문자에 대해 1점을 주어 계산됩니다.
· 주어진 단어 목록에 대해 가장 높은 점수를 받은 단어로 시작하는 정렬된 목록을 반환합니다.
· 단어에 'c' 가 포함된 경우 보너스 점수 5 점을 점수에 추가해야 합니다 .
· 이전 점수 방식(보너스 없음)은 코드에서 계속 지원되어야 합니다.
기능적 접근 방식을 따르면서 우리는 많은 작은 기능을 갖게 되었고 그 중 일부는 전혀 변경할 필요가 없습니다
4.6 코드를 전달합니다!
Comparator 인스턴스를 전달 함으로써 일부 데이터의 인스턴스가 아닌 특정 동작을 전달합니다.
List<String> rankedWords(Comparator<String> comparator,
List<String> words) {
return words
.stream()
.sorted(comparator)
.collect(Collectors.toList());
}
rankWords 함수는 실제로 정렬을 담당하는 코드 조각인 Comparator 를 사용합니다. 이 아이디어를 Java로 표현하고 함수를 Comparator 로 전달할 수 있는 멋진 점은 다음과 같습니다.
Comparator<String> scoreComparator =
(w1, w2) -> Integer.compare(score(w2), score(w1));
4.7 Java의 함수 값 사용
functional interface?
Function<String, Integer> scoreFunction =
w -> w.replaceAll("a", "").length();
4.8 함수 구문을 사용하여 코드 중복 처리
4.9 사용자 정의 함수를 인수로 전달
버전 #5: 채점 기능 전달
4.10 커피 브레이크: 매개변수로 기능
· 주어진 단어의 점수는 'a' 가 아닌 각 문자에 대해 1점을 주어 계산됩니다.
· 주어진 단어 목록에 대해 가장 높은 점수를 받은 단어로 시작하는 정렬된 목록을 반환합니다.
· 단어에 'c' 가 포함된 경우 보너스 점수 5 점을 점수에 추가해야 합니다 .
· 이전 점수 방식(보너스 없음)은 코드에서 계속 지원되어야 합니다.
연습: 새 요구 사항 구현
새로운 요건: 페널티 가능성
· 단어에 's' 가 포함된 경우 벌점 7 을 점수에서 빼야 합니다 .· 이전 점수 방식(보너스가 있거나 없는 경우)은 코드에서 계속 지원되어야 합니다. |
static scoreWithPenalty(Function<String, Integer> wordScore,
String word) {
return score.apply(word) + word.contains("s") ? -7 : 0
}
Function<String, Integer> scoreWithPenaltyFunction =
w -> scoreWithPenalty(w);
4.12 기능적 Java 읽기 문제
읽을 코드가 너무 많습니다.
Java로 완전히 기능적으로 작성하는 것을 반대하는 사람들은 이 솔루션이 다소 부풀려지고 많은 "노이즈"를 포함한다고 말합니다.
코드는 작성된 것보다 훨씬 더 자주 읽힙니다. 따라서 우리는 항상 읽기를 위해 코드를 최적화해야 합니다.
우리는 여전히 변경 가능한 List 를 사용 합니다 .
4.18 선언적 프로그래밍 수용
해키 방식으로 단어 순위 지정
def rankedWords(wordScore: String => Int,
words: List[String]): List[String] = {
def negativeScore(word: String): Int = -wordScore(word)
words.sortBy(negativeScore)
}
꼼수를 부려서 정상동작하게 만들었기 때문에 가독성 저하를 시킨다.
선언적으로 단어 순위 지정
4.20 작은 기능과 책임
가독성 최적화
매우 작은 함수(간단하고 직접적인 한 줄)만 익명 함수로 전달해야 합니다. 점수 와 보너스 를 추가 하는 것이 완벽한 예입니다. 당신은 그것을 잘못 읽을 수 없습니다. 그리고 인수로 전달하면 코드가 더 간결하고 읽기 쉬워집니다.
4.22 쉬는 시간: Scala에서 함수 전달
def rankedWords(wordScore: String => Int,
words: List[String]): List[String] = {
words.sortBy(wordScore).reverse
}
def score(word: String): Int = word.replaceAll("a", "").length
def bonus(word: String): Int = if (word.contains("c")) 5 else 0
def penalty(word: String): Int = if (word.contains("s")) 7 else 0
def words = List("haskell", "rust", "scala", "java", "ada")
결과
scala> rankedWords(w => score(w) + bonus(w) - penalty(w), words)
res3: List[String] = List(java, ada, scala, haskell, rust)
책과 다름..; 책이 잘못된 것 같다.
rankedWords(w => score(w) + bonus(w) - penalty(w), words)
List("java", "scala", "ada", "haskell", "rust"))
4.24 함수를 전달하는 것만으로 또 무엇을 얻을 수 있습니까?
함수를 인수로 전달하는 기술은 함수형 프로그래밍에서 어디에나 있습니다.
4.25 목록의 각 요소에 함수 적용
· 단어 목록에서 각 단어의 점수를 알아야 합니다.
· 랭킹을 담당하는 기능은 여전히 동일하게 작동해야 합니다(기존 기능을 변경할 수 없음).
'map' 등장.
4.26 맵 을 사용하여 목록의 각 요소에 함수 적용
맵 이 하는 일은 목록 의 각 요소를 가져와서 함수에 적용하여 반환되는 새 목록에 결과를 저장하는 것입니다 .
4.27 맵 알아보기
map 은 하나의 매개변수만 사용하며 이 매개변수는 함수입니다. List 에 Strings 가 포함된 경우 이 함수는 하나 의 String 을 가져와 같거나 다른 유형의 값을 반환해야 합니다. 이 함수가 반환하는 값의 유형은 반환된 List 에 포함된 요소의 유형과 정확히 일치합니다.
map 은 순서를 유지합니다. 즉, 요소가 원래 목록의 다른 요소 앞에 있으면 매핑된 버전도 앞에 있게 됩니다.
4.30 조건에 따라 목록의 일부 반환
· 점수가 1보다 높은 단어 목록을 반환해야 합니다(예: high score ).
· 지금까지 구현된 기능은 여전히 동일하게 작동해야 합니다(기존 기능을 변경할 수 없음).
4.31 필터 를 사용하여 목록의 일부 반환
4.38 함수는 함수를 반환할 수 있습니다
서명부터 시작합니다. 여기에서 다른 함수를 반환하는 첫 번째 함수를 정의합니다!
본문에서 어떻게 함수를 반환할 수 있습니까? 함수를 sortBy , 맵 및 필터 함수 에 전달하는 데 사용한 것과 동일한 구문을 사용할 수 있습니다 .
4.40 함수는 값이다
1급객체에 대한 내용이 필요할 것 같다.
인수로 전달된 함수는 값입니다.
highScoringWords 의 매개변수 목록을 보면 두 개의 매개변수가 있습니다. 둘 다 동등하게 취급되며 둘 다 불변 값입니다.
다른 함수에서 반환되는 함수는 값입니다.
우리는 단지 값를 전달하고 있습니다
동일한 직관이 FP에서 작동합니다. 함수는 값일 뿐이며 인수로 사용할 수 있고 생성할 수 있으며 다른 함수에서 반환할 수 있습니다. Strings 와 같이 그냥 전달할 수 있습니다 .
def fun1(compareNumber: Integer) : Int => Boolean = fun => fun > compareNumber
scala> List(5,1,2,4,0).filter(fun1(4))
res5: List[Int] = List(5)
scala> List(5,1,2,4,0).filter(fun1(1))
res6: List[Int] = List(5, 2, 4)
def fun2(divideNumber: Integer) : Int => Boolean = fun => fun % divideNumber == 0
scala> List(5,1,2,4,15).filter(fun2(5))
res8: List[Int] = List(5, 15)
scala> List(5,1,2,4,15).filter(fun2(2))
res9: List[Int] = List(2, 4)
def fun3(stringLength: Integer) : String => Boolean = fun => fun.length < stringLength
scala> List("scala", "ada").filter(fun3(4))
res10: List[String] = List(ada)
scala> List("scala", "ada").filter(fun3(7))
res11: List[String] = List(scala, ada)
def fun4(sLength: Integer) : String => Boolean = fun => fun.count(c => c == 's') > sLength
scala> List("scala", "ada").filter(fun4(2))
res12: List[String] = List()
scala> List("scala", "ada").filter(fun4(0))
res13: List[String] = List(scala)
4.43 기능적 API 설계
우리는 매우 중요한 기술을 도입했습니다. 함수는 값일 뿐이며 다른 함수에서 반환될 수 있습니다. 이 예에서는 3개의 매개변수 함수에 문제가 있었고 이를 1개의 매개변수 함수를 반환하는 2개의 매개변수 함수로 변환했습니다. 함수 내부의 논리는 변경되지 않았습니다.
4.44 기능적 API의 반복 설계
4.46 반환된 함수에서 함수를 반환하는 방법은 무엇입니까?
더블 포인터가 생각이 나는건 왜일까..ㅎ
4.48 함수에서 여러 매개변수 목록 사용
이를 두고 currying이라고 한다.
4.51 함수 값을 전달하여 프로그래밍
고차 함수
다른 for 루프를 하나의 라이너로 교체하기 전에 매우 중요한 사항을 강조하겠습니다. 함수를 매개 변수로 다른 함수에 전달하고 함수에서 함수를 반환하는 기술은 기능 코드베이스에서 보편적입니다. 너무 보편적이어서 자체 이름이 있습니다. 다른 함수를 받거나 반환하는 함수를 고차 함수라고 합니다 . sortBy , map & filter 는 표준 라이브러리의 예제일 뿐이지만 더 많은 것을 만나보고 직접 작성할 수도 있습니다!
두 번째로 매우 중요한 점은 컬렉션뿐만 아니라 다른 유형에서도 map , filter 및 기타 고차 함수를 사용할 것이라는 점입니다. 우리는 그것들을 한 번 배웠고 어디에서나 사용할 것입니다.
4.52 많은 값을 단일 값으로 줄이기
· 입력 목록으로 제공된 단어의 누적 점수를 반환해야 합니다.
· 지금까지 구현된 기능은 여전히 동일하게 작동해야 합니다(기존 기능을 변경할 수 없음).
4.54 foldLeft 알아보기
foldLeft 는 목록의 모든 요소를 살펴보고 제공된 함수를 호출하여 값을 누적합니다.
제공된 함수를 기반으로 합계 를 누적
Java Streams reduce 와 매우 유사합니다 !
4.57 불변 데이터 모델링
map , filter 및 foldLeft 는 함수를 매개변수로 받는 함수임을 기억하십시오 . 함수를 반환하는 이들과 함수를 고차 함수라고 합니다.
기능적 언어는 서로 다른 유형의 여러 값을 보유할 수 있는 기본이 아닌 불변 값을 정의하는 특수 언어 구조를 제공합니다. FP에서는 이를 제품 유형이라고 하며 Scala에서 케이스 클래스 로 인코딩합니다 .
'책 스터디 > [완료] FP - Grokking Funtional Programming' 카테고리의 다른 글
Grokking Functional Programming - 2부 6장 (0) | 2022.12.21 |
---|---|
Grokking Functional Programming - 2부 5장 (0) | 2022.12.14 |
Grokking Functional Programming - 1부 3장 (0) | 2022.11.23 |
Grokking Functional Programming - 1부 2장 (0) | 2022.11.16 |
Grokking Functional Programming - 1부 1장 (0) | 2022.11.09 |