김동형수 개발기

Grokking Functional Programming - 1부 4장 본문

책 스터디/[완료] FP - Grokking Funtional Programming

Grokking Functional Programming - 1부 4장

김동형수 2022. 11. 30. 23:23

4부 가치로서의 기능

이 장에서는 다음을 수행하는 방법을 배웁니다.

  • 함수를 인수로 전달
  • 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 함수 구문을 사용하여 코드 중복 처리

Java의 화살표 구문( -> ) 을 사용하여 함수를 생성할 때 위에 표시된 함수가 어떻게 보이는지 봅시다 . 이러한 함수는 동일하지만 값으로 저장됩니다 .

 

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 를 사용하는 것 입니다.

읽을 코드가 너무 많습니다.

Java로 완전히 기능적으로 작성하는 것을 반대하는 사람들은 이 솔루션이 다소 부풀려지고 많은 "노이즈"를 포함한다고 말합니다.

코드는 작성된 것보다 훨씬 더 자주 읽힙니다. 따라서 우리는 항상 읽기를 위해 코드를 최적화해야 합니다.

우리는 여전히 변경 가능한 List 를 사용 합니다 .

 

4.18 선언적 프로그래밍 수용

해키 방식으로 단어 순위 지정

def rankedWords(wordScore: String => Int,
                words: List[String]): List[String] = {
  def negativeScore(word: String): Int = -wordScore(word)
  words.sortBy(negativeScore)
}​

꼼수를 부려서 정상동작하게 만들었기 때문에 가독성 저하를 시킨다.

 

선언적으로 단어 순위 지정

sortBy * -1 보다는 reverse를 이용하자.
 

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 필터 를 사용하여 목록의 일부 반환

filter 등장
 
필터 는 목록의 각 요소를 가져와 제공된 조건을 적용하여 이 조건을 만족 하는 요소만 반환합니다
 
 

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에서 케이스 클래스 로 인코딩합니다 .

 

 

Comments