본문 바로가기
Software Engineering

함수형 프로그래밍

by 태크민 2024. 6. 16.

명령형 프로그래밍 VS 선언형 프로그래밍

명령형 프로그래밍

프로그래밍의 상태와 상태를 변경시키는 구문의 관점으로 접근하는 프로그래밍 방식.

명령형 프로그래밍은 컴퓨터가 실행할 명령들을 실행 순서대로 구현해야 한다.

대부분의 객체 지향 프로그래밍 언어가 명령형 프로그래밍 언어이다. 알고리즘 처리 작업에 적합한 언어이다.

절차지향 프로그래밍, 객체지향 프로그래밍이 이에 속한다.

 

예시) point를 얻기 위해 실행할 명령들을 순서대로 구현

fun getPoint(customer: Customer): Int {
    for (i in 0..customers.size) {
        val c = customers[i]
        if (customer == c) {
            return c.point
        }
    }
    return NO_DATA
}

 

선언형 프로그래밍

선언으로만 프로그램을 동작시키는 것을 의미한다.

프로그램을 실행하기 위해 구체적인 작동 순서를 나열하지 않아도 된다.

완전하지 않지만 함수형 프로그래밍을 활용해 일정 수준의 선언형 프로그래밍을 할 수 있다.

함수형 프로그래밍은 선언형 프로그래밍의 한 종류로 볼 수 있다.

 

예시) 구체적인 로직을 직접 작성하지 않고 ~게 할 것이라고 선언

fun getPoint(customer: Customer): Int {
    if (isRegisteredCustomer(customer)) {
        return findCustomer(customer).point
    }
    return NO_DATA
}

 

함수형 프로그래밍 이란?

함수형 프로그래밍은 함수의 입력만을 의존하여 출력을 만드는 구조로 외부 상태를 변경하는 패러다임의 부작용 발생을 최소화하는 방법론을 의미한다.

함수형 프로그래밍 언어로 설계된 클로저,스칼라,하스켈 등의 언어가 있고, 자바스크립트,코틀린,파이썬 등에도 최근 버전에 함수형 프로그래밍 문법이 추가 되었다.

 

 


 

함수형 프로그래밍의 특징

 

1. 순수함수 (Pure function)

  • 동일한 입력에는 항상 같은 값을 반환해야 하는 함수
  • 함수의 실행이 프로그램의 실행에 영향을 미치지 않아야 하는 함수
  • 함수 내부에서 인자의 값을 변경하거나 프로그램 상태를 변경하는 Side Effect가 없는 것
var num = 1

fun add(a: Int): Int {
    return a + num
}

위와 같은 예제에서는 add라는 함수 안에서 전역으로 선언된 변수인 num을 참조하기 때문에 순수함수라고 볼 수 없다.

fun add(a: Int, b: Int): Int {
    return a + b
}

위와 같이 add의 함수가 프로그램 실행에 영향을 미치지 않고 입력 값에 대해서만 값의 변환이 있으므로 순수함수이다.

순수 함수는 프로그램의 변화 없이 입력 값에 대한 결과를 예상 할 수 있어 테스트가 용이하다.

부수 효과(Side-Effect)

부수 효과(Side Effect)란 아래와 같은 변화가 발생하는 작업을 의미한다.

  • 콘솔 또는 파일 I/O
  • 예외가 발생하여 프로그램 실행 중단
  • 데이터가 변경됨
val items = mutableListOf("Apple", "Banana", "Orange")

fun add(item: String) {
    items.add(item)
}

위 코드에서 add() 함수를 호출하면서 items의 요소가 변경되었다.

따라서 부수 효과가 발생한다고 할 수 있다.

 

 

2. 비상태, 불변성 (Stateless, Immutability)

  • 함수형 프로그래밍에서의 데이터는 변하지 않는 불변성을 유지해야 한다.
  • 데이터의 변경이 필요한 경우, 원본 데이터 구조를 변경하지 않고 그 데이터의 복사본을 만들어서 그 일부를 변경하고, 변경한 복사본을 사용해 작업을 진행한다.
val person = Person("jongmin", 26)
fun increaseAge(person: Person): Person {
    person.age = person.age + 1
    return person
}

위의 예제에서는 increaseAge 함수에서 전역으로 선언된 person의 age 속성을 변경하므로 불변성 유지를 만족하지 못한다.

data class Person(val name: String, val age: Int)

fun increaseAge(person: Person): Person {
    return person.copy(age = person.age + 1)
}

위처럼 객체의 값을 바꾸기 위해서는 데이터의 복사본을 만들어, 그 복사본을 사용해 작업을 진행하고 반환한다.

 

 

3. 선언형 함수 (Expressions)

명령형 프로그래밍은 무엇을 어떻게 할 것인가에 주목하고, 선언헌 프로그래밍은 무엇을 할 것인가에 주목한다.

val numbers = mutableListOf(1, 2, 3)
fun multiply(numbers: MutableList<Int>, multiplier: Int) {
    for (i in numbers.indices) {
        numbers[i] = numbers[i] * multiplier
    }
}

위의 예시에서는 for문을 사용해서 배열의 각 요소에 multiplier 곱해주는 명령형 프로그래밍이다.

함수형 프로그래밍에서는 마찬가지로 if,switch,for 등 명령문을 사용하지 않고 함수형 코드로 사용해야한다.

 

val numbers = mutableListOf(1, 2, 3)
fun multiply(numbers: List<Int>, multiplier: Int): List<Int> {
    return numbers.map { it * multiplier }
}

위의 예시는 for문을 map으로 대치했고, kotlin 에서는 에서는 filter,map 등의 함수형 코드를 사용한다. 명령형에서 함수형으로 변환하는 과정은 OPEN 투게더님 블로그을 참조해도 좋을 것 같다.

 

 

 

4. 1급 객체와 고차함수 (Fist-class, Higher-order functions)

함수형 프로그래밍에서는 함수가 1급 객체가 된다. 1급 객체의 특징은 다음과 같다.

  • 변수나 데이터 구조안에 담을 수 있다.
  • 파라미터로 전달 할 수 있다.
  • 반환값(return value)으로 사용할 수 있다.
  • 할당에 사용된 이름과 관계없이 고유한 구별이 가능하다.
  • 동적으로 프로퍼티 할당이 가능하다.
fun addTwo(num: Int): Int = num + 2
fun multiplyTwo(num: Int): Int = num * 2

fun transform(numbers: List<Int>): List<Int> {
    return numbers.map(::addTwo).map(::multiplyTwo)
}

fun main() {
    val result = transform(listOf(1, 2, 3, 4))
    println(result) // 출력: [6, 8, 10, 12]
}

위의 예시에서는 함수를 변수에 할당하거나 반환하는 1급 객체로서의 특징을 보여준다.

 

그리고 고차 함수의 속성을 가져야 하는데, 고차 함수의 특징은 다음과 같다.

  • 함수를 인자로써 전달 할 수 있어야 한다.
  • 함수의 반환 값으로 또 다른 함수를 사용 할 수 있다
fun addInform(name: String): (String) -> String {
    return { age: String -> age + name }
}

fun main() {
    val jongmin = addInform("종민")
    println(jongmin("96")) // 96종민
}

위의 예제처럼 함수의 반환 값으로 다른 함수를 사용하거나, 함수의 반환 값으로 또 다른 함수를 사용 할 수 있어야 한다.

 


 

함수형 프로그래밍의 장단점


장점

  1. 높은 수준의 추상화를 제공한다
  2. 함수 단위의 코드 재사용이 수월하다
  3. 불변성을 지향하기 때문에 프로그램의 동작을 예측하기 쉬워진다


단점

  1. 함수형 프로그래밍은 높은 수준의 추상화를 제공한다. 이로 인해 코드가 간결하고 모듈화되긴 하지만, 때로는 이해하기 어렵고 복잡해질 수도 있다. 특히 함수형 프로그래밍에 익숙하지 않은 개발자들에게는 코드 해석이 어려울 수 있다.
  2.  함수형 프로그래밍에서는 불변성과 순수 함수를 사용하여 상태 변경을 최소화한다. 그러나 이로 인해 메모리 사용량이 증가하거나, 가비지 컬렉션 비용이 높아질 수 있다. 또한, 재귀를 사용한 경우 스택 오버플로우가 발생할 수도 있다.
  3. 순수함수를 사용하는 것은 쉬울 수 있지만 조합하는 것은 쉽지 않다.

 


출처

https://jongminfire.dev/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%B4%EB%9E%80

 

함수형 프로그래밍이란?

jongminfire.dev

https://thecho7.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EA%BF%80%ED%8C%81-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DFunctional-Programming%EC%9D%B4%EB%9E%80

 

[면접 꿀팁] 함수형 프로그래밍(Functional Programming)이란?

함수형 프로그래밍(Functional Programming)은 백엔드/프론트엔드를 막론하고 자주 질문 받으며 실제로 현업에서도 자주 다루는 개념입니다. 함수형 프로그래밍에 대해 설명해보도록 하겠습니다. 함

thecho7.tistory.com

https://itstory1592.tistory.com/120

 

[우아한테크코스] 함수형 프로그래밍(Functional Programming)이란?

프로그래밍 패러다임은 프로그래머에게 프로그램을 어떻게 바라볼지에 대한 관점을 제공합니다. 명령형 프로그래밍 프로그래밍의 상태와 상태를 변경시키는 구문의 관점으로 접근하는 프로그

itstory1592.tistory.com