김동형수 개발기

Grokking Functional Programming - 1부 3장 본문

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

Grokking Functional Programming - 1부 3장

김동형수 2022. 11. 23. 22:57

3장 불변 변수

 

이 장에서 배울 것입니다

  • 가변성이 위험한 이유
  • 사본으로 작업하여 가변성과 싸우는 방법은 무엇입니까?
  • 공유 가변 상태는 무엇입니까?
  • 불변 값으로 작업하여 가변성과 싸우는 방법
  • 문자열  목록 의 불변 API를 사용하는 방법

3.1 엔진 연료

순수함수가 가변 상태를 사용할 수 없는 이유를 설명한다.

순수 함수와 불변 값 사이의 관계는 매우 강해서 다음 두 가지 개념만 사용하여 함수형 프로그래밍을 정의할 수 있습니다.

 

 

Q          절대 변경될 수 없는 순수한 함수와 값만을 사용하여 완전히 작동하는 애플리케이션을 작성하는 것이 어떻게 가능합니까?

A          짧은 대답은 순수 함수가 데이터의 복사본을 만들어 전달하는 것입니다. 사본을 사용하여 쉽게 프로그래밍할 수 있으려면 언어의 특정 구조가 필요합니다. 이 장과 다음 장에서 더 긴 답변을 읽으면 더 많은 것을 알 수 있습니다.

 

3.2 불변성의 또 다른 경우

유럽여행

List<String> planA = new ArrayList<>();
planA.add("Paris");
planA.add("Berlin");
planA.add("Kraków");
System.out.println("Plan A: " + planA);
console output: Plan A: [Paris, Berlin, Kraków]

 

 

우리의 임무는 업데이트된 계획을 반환하는 재계획 기능 을 작성하는 것입니다. 세 가지 매개변수가 필요합니다.

  • 변경하려는 계획(예: [Paris, Berlin, Kraków] ),
  • 추가하려는 새 도시(예: Vienna ) 및
  • 새로운 도시가 추가되어야 하는 도시(예: Kraków ).

아래처럼 시그니처가 작성되어야 한다.

3.4 가변성은 위험하다

변경 가능한 값을 사용하는 것은 매우 위험 합니다.
 

3.5 거짓말을 하는 함수... 다시

순수 함수

· 단일 값만 반환합니다.

· 인수에 따라서만 반환 값을 계산합니다.

· 기존 값을 변경하지 않습니다.

static List<String> replan(List<String> plan,
                           String newCity,
                           String beforeCity) {
  int newCityIndex = plan.indexOf(beforeCity);
  plan.add(newCityIndex, newCity);
  return plan;
}

 위의 replan은 순수함수가 아니다.

이유 - 매개변수로 받은 plan을 변경했기 때문이다.

 

3.6 복사본으로 작업하여 가변성에 맞서기

static List<String> replan(List<String> plan,
                           String newCity,
                           String beforeCity) {
    int newCityIndex = plan.indexOf(beforeCity);
    List<String> replanned = new ArrayList<>(plan);
    replanned.add(newCityIndex, newCity);
    return replanned;
}

순수 함수는 기존 값을 변경하지 않습니다. 인수 목록이나 전역 범위에서 아무 것도 수정할 수 없습니다. 그러나 로컬로 생성된 값을 변경할 수 있습니다. 

 

3.7 쉬는 시간: 가변성으로 인한 화상

static double totalTime(List<Double> lapTimes) {
  lapTimes.remove(0);
  double sum = 0;
  for (double x : lapTimes) {
    sum += x;
  }
  return sum;
}
static double avgTime(List<Double> lapTimes) {
  double time = totalTime(lapTimes);
  int laps = lapTimes.size();
  return time / laps;
}
평균 시간 요구 사항

· avgTime 은 워밍업 랩을 제외한 평균 랩 시간을 반환해야 합니다.

·   최소 2개의 랩이 있는 목록만 전달됩니다.

 

내 풀이

static double totalTime(List<Double> lapTimes) {
  var copiedLapTimes = new ArrayList<Double>(lapTimes)
  copiedLapTimes.remove(0)
  double sum = 0;
  for (double x : lapTimes) {
    sum += x;
  }
  return sum;
}
static double avgTime(List<Double> lapTimes) {
  var copiedLapTimes = new ArrayList<Double>(lapTimes)
  copiedLapTimes.remove(0)
  return totalTime(lapTimes) / copiedLapTimes.size();
}

3.9 공유 가변 상태 소개

상태 는 한 곳에 저장되고 코드에서 액세스할 수 있는 값의 인스턴스입니다. 
전역변수 or 전역으로 선언 된 싱글톤 인스턴스?

 

3.10 프로그래밍 능력에 대한 상태의 영향

첫째, 프로그래밍 문제를 해결하기 위해 많은 것을 염두에 두어야 하고 일반적으로 그렇게 하는 경우 이러한 사항이 함수 호출 사이 또는 심지어 두 줄의 코드 사이(스레드를 사용하여 프로그래밍할 때) 언제든지 변경될 수 있다면 문제는 훨씬 더 어려워집니다.

 

둘째, 이렇게 시시각각 변하는 것들이 추가로 공유된다면 그에 대한 소유권과 책임의 문제가 생긴다. "이 값을 안전하게 변경할 수 있습니까?", "프로그램의 다른 부분에서 이 값을 사용합니까?", "이 값을 변경하면 이 변경 사항에 대해 어떤 엔터티에 알려야 합니까?"라는 질문을 끊임없이 스스로에게 해야 합니다.

 

셋째, 많은 개체가 주어진 상태를 변경할 수 있는 경우 이 상태의 모든 가능한 값을 식별하는 데 문제가 있을 수 있습니다. 이 상태가 당면한 코드에 의해서만 생성될 수 있는 값을 가지고 있다고 가정하는 것은 매우 유혹적입니다. 그러나이 상태가 공유된다면 그것은 잘못된 가정입니다! 재계획 기능이 새 계획을 반환 하기 때문에 계획 이 생성되면 변경할 수 없다고 가정한 것을 기억 하십니까?

 

3.11 움직이는 부품 다루기

마지막으로 "움직이는 부분" 또는 공유된 가변 상태를 직접 다루는 기술에 대해 이야기할 수 있습니다. 

세 가지 접근 방식을 소개합니다.

  • 재계획 기능을 고쳤 을 때 사용한 방식
  • 객체 지향 방식
    • OOP에서는 변경되는 데이터를 보호하기 위해 캡슐화 를 사용합니다.
  • 기능적 방식

3.12 FP를 이용한 가동부 처리

불변의 값

값이 생성되면 절대 변경할 수 없도록 보장하는 기술입니다. 프로그래머가 값의 아주 작은 부분이라도 변경해야 하는 경우(예: 목록에 문자열 추가) 새 값을 만들고 이전 값을 그대로 유지해야 합니다.

Scala의 목록 은 변경할 수 없습니다. 모든 작업은 항상 새로운 List 를 반환합니다.

3.15 쉬는 시간: Immutable String API

def function(stringParameter: String): String = {
  stringParameter.split(" ").zipWithIndex.map(string => {
    if(string._2 == 0) string._1.substring(0, 1) + "."
    else string._1
  }).reduce((a, b) => a + " " + b)
}

3.18 공유 가변 상태에 대한 순수 함수적 접근

가치 사이의 관계에 집중

항상 들어오는 값과 생성하려는 값 사이의 모든 알려진 관계를 나열하는 것으로 시작하는 것입니다 .

 

3.20 요약

가변성은 위험하다

우리는 변경 가능한 컬렉션을 사용할 때 특별한 주의를 기울여야 한다는 것을 발견했습니다. 
서명을 보는 것만으로는 충분하지 않습니다.

복사본을 사용한 가변성과의 싸움

공유 가변 상태란 무엇입니까?

코드베이스의 서로 다른 엔터티 간에 공유되고 변경될 수 있는 변수인 공유 변경 가능 상태를 도입했습니다.

불변 값을 사용하여 가변성과 싸우기

마지막으로 공유된 가변 상태를 처리하기 위해 더 강력한 기술인 불변 값을 도입했습니다. 함수가 변경 불가능한 값만 취하고 변경 불가능한 값을 반환한다면 예기치 않게 변경된 것이 없다고 확신할 수 있습니다.

문자열  목록 의 불변 API 사용

결국 우리는 Java의 String 이 이미 이 접근 방식을 사용하고 있음을 알게 되었습니다. 많은 메서드가 String 의 새 값을 반환하기 때문 입니다. 우리는 Scala가 불변 콜렉션을 기본적으로 지원한다는 것을 배웠고 List의 slice , append  appendAll 함수를 약간 사용해 보았습니다. 우리는 모든 종류의 불변 값에 대해 학습하는 데 책의 나머지 부분을 할애할 것입니다.

 

Comments