1. Go로 순수 함수형 프로그래밍하기

"Go로 배우는 함수형 프로그래밍" 교재를 읽고 정리한 내용입니다.


  • 함수형 프로그래밍의 이론적 기초 다지기
  • 함수형 해법의 구현 방법 배우기
  • 어떤 함수형 프로그래밍 타입이 업무 요구 사항에 가장 잘 부합하는지 결정하기.
  • Go는 여러 플랫폼에서 실행 가능하며 병행 프로그래밍, 많은 개발 지원도구 및 커뮤니티를 제공한다 => "단순하며 강력한 언어"

병행 프로그래밍❓

병행 프로세스로 프로그램을 작성하는 것을 의미.

병행 프로세스❓

두 개 이상의 프로세스들이 동시에 존재하여 실행 상태에 있는 것을 의미

참조: https://velog.io/@ha0kim/%EB%B3%91%ED%96%89-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%98-%EA%B0%9C%EB%85%90


1. 함수형 프로그래밍의 사용 동기

  • 함수형 프로그래밍 스타일은 간결하고, 높은 표현력, 줄여지는 코드량, 낮은 오류발생률을 제공한다.
  • 소프트웨어 개발은 쉽지 않으며, 그 이유로 여러 비함수적 요구사항(non-functional requirements, NFRs)를 고려해야하기 때문이다.
    • 복잡성(complexity)
    • 기능 확장성(extensibility)
    • 유지 보수정(maintainability)
    • 신뢰성(reliability)
    • 병행성(concurrency)
    • 규모 확장성(scalability)

2. 명령행 프로그래밍과 선언적 프로그래밍

  • 기계어는 컴퓨터의 네이티브 언어이자 명령행 프로그래밍 언어이다.
  • 명령행 프로그래밍은 문장을 사용해 프로그램의 상태를 변경해가는 패러다임.
// 명령형 프로그래밍
var found bool;
cars := []string{"Accored", "IS250", "Blazer"}
for _, car := range cars {
    if car == carToLookFor {
        found = true;
    }
}
fmt.Printf("Found" ? %v, found)

// 함수형 프로그래밍
cars := []string{"Accord", "IS250", "Blazer"}
fmt.Printf("Found ? %v", cars.contains("Blazer"))
  • 코드의 양이 현저히 줄어는 것을 볼 수 있다.
  • 함수형 프로그래밍은 for 반복문보다 작성자의 의도를 명확히 표현 가능하며, 데이터 집합의 요소를 필터링하거나 통합할 때 유용하다.

3. 순수 함수들

  • 동일한 입력 값으로 동일한 함수를 실행해도 실행할 때마다 다른 결과를 얻을 수 있다. => 이는 어리석은 짓이다.
  • 순수 함수란?
    • 함수를 일급 객체로 취급한다.
    • 같은 입력에 대해 항상 같은 결과를 반환한다.
    • 실행 환경에 대한 부수 효과를 야기하지 않는다.
    • 외부 상태가 결과에 영향을 주지 않는다.
    • 변수 값이 시간이 지나도 변하지 않는다.
  • 순수 함수의 두 가지 특징은 참조 투명성멱등성 이다.
    • 참조 투명성 이란, 프로그램 내 함수 호출 부분을 이와 동등한 값들로 치환해도 프로그램의 동작이 달라지지 않는 성질
    • 멱등성 이란, 함수 호출 결과가 언제나 같은 성질

4. 메모이제이션

  • Go는 성능 향상을 위한 메모이제이션 옵션을 제공한다.
type Memoized func(int) int	// 함수 타입 선언
var fibMem Memoized	// 피보나치 변수에게 Memoized 타입 선언
// Memoize() 함수 구현
func Memoize(mf Memoized) Memoized {
    cache := make(map[int]int)
    return func(key int) int {
        if val, found := cache[key]; found {
            return val
        }
        temp := mf(key)
        cache[key] =temp
        return temp
    }
}

5. 익명 함수와 클로저의 차이

// 기명 함수
func nameGreeting(name string) {
    fmt.Printf("hey %s!", name)
}

// 익명 함수
func anonymousGreeting() func(string) {
    return func(name string) {
        fmt.Printf("hey %s!", name)
    }
}
  • 익명함수는 현재상태를 가진다. 그러나 클로저는 기존의 상태를 보존한다.
  • 또한 클로저는 외부 환경의 상태를 변경하는 대신에 새로운 상태를 생성해서 전달되거나 참조될 수 있도록 한다.

6. Go의 병행성 요소를 이용해 함수형 프로그래밍하기

result := function1() + function2()
  • 위의 식에서, 병렬화는 각 함수를 다른 CPU 코어에서 실행하는 것을 뜻한다.
  • 이때, 총 수행 시간은 둘 중 비싼 함수가 결과를 반환하는 데 드는 시간이다.
  • 병렬화와 병행성의 차이
    • 병렬화 (parallelization): 여러 함수를 동시에(다른 CPU 코어에서) 실행
    • 병행성 (concurrency): 프로그램을 독립 실행 가능한 조각으로 나눔
  • Go는 채널을 이용한 메시징과 동기화를 바탕으로 고루틴의 병행 실행을 지원한다.
    • 고루틴: Go 런타임이 관리하는 경량 스레드
    • Go명령문: 함수 호출을 실행하는 go 명령어로서, 호출하는 코드와 동일한 주소공간 내에서 제어 가능한 독립적 병행 스레드로 함수를 실행
    • 채널: 타입을 가진 통로로서 채널 연산자(<-)를 이용해 값을 송수신

참고

  • 단위 테스트 는 다음과 같은 목적을 위해 작성해야 한다.
    • 구현하는 바가 기능 요구 사항에 부합하는지 확인
    • 해법 구현 시 최선책 고안을 위한 테스팅 활용
    • 지속적 통합 프로세스에 활용하기 위한 품질 테스트 생성
    • 구현한 바가 애플리케이션의 인터페이스 요구 사항에 부합하는지 검증
    • 쉬운 통합 테스트 개발
    • 내가 개발 중인 코드가 다른 개발자의 코드로 인해 오작동할 수 있으므로, 개발 중인 코드를 안전하게 보호
  • 느긋한 계산 (Lazy Evaluation)
    • 수식의 계산 값을 필요로 하는 시점까지 계산을 미뤄서 불필요한 계산을 배제함으로써 성능을 높이는 계산법
  • 불변 함수
    • 실행 시간에 값을 배정하고 나면, 값을 변경할 수 없는 변수
  • 꼬리 호출 최적화 (TCO)
    • 함수의 마지막 문장이 함수 호출인 경우를 꼬리 호출(Tail call)이라 한다. 꼬리 호출 최적화는 함수 호출을 GoTo문으로 치환하는 작업이다. 이렇게 함으로써 함수 호출로 인한 스택 설정/복원 부담을 줄일 수 있다.
  • 함수 리터럴
    • 언어의 일급 객체로 취급 되는 함수
    • 일급 객체란, int나 string처럼 변수의 타입이 되는 요소
    • Go의 함수는 타입으로 선언될 수 있고, 변수 또한 구조체 필드에 배정될 수 있으며, 다른 함수의 전달 인자나 반환값이 될 수도 있다.
    • 함수 리터럴은 클로저로서, 선언된 환경의 스코프에 접근할 수 있다.

좋은 웹페이지 즐겨찾기