티스토리 뷰

반응형
SMALL

함수형 프로그래밍에 대해 알아보도록 하겠습니다.

프로그래밍 패러다임으로서의 함수형 프로그래밍

절차지향 프로그래밍

알고리즘과 로직 중심으로 문제 해결이 주목적

객체지향 프로그래밍

클래스 선언이 최우선, 데이터와 데이터를 처리할 메서드를 하나로 묶어 객체를 만들고, 객체를 조합해서 프로그래밍 작성, 객체를 만들기 위한 추상화, 캡슐화, 상속, 다형성 등의 개념을 제공

함수형 프로그래밍

함수 선언이 최우선, 데이터의 흐름이 아니라 함수의 선언과 선언된 함수의 유기적인 흐름이 주목적

 

함수형 프로그래밍의 주요 원칙

함수형 프로그래밍에는 여러 원칙들이 있는데 가장 큰 핵심은 크게 두 가지입니다.

일급 객체(First Class Citizen)로서의 함수
순수 함수(Pure Function)으로 정의되는 함수

일급 객체란 함수가 프로그램의 최상위 구성요소라는 것을 의미합니다. 객체 지향 프로그래밍에서는 Class가 일급 객체가 되겠습니다.

Class가 최상위 단위이고 Method가 그 안에 포함되는 개념입니다.

함수형 프로그래밍에서는 일급 객체로써 함수를 정의할 수 있어야 합니다.

즉, 클래스를 선언하지 않고도 함수를 정의할 수 있고, 함수 내에 다른 함수, 클래스를 포함할 수 있어야 합니다.

 

함수를 변수처럼 사용할 수 있어야 합니다. 어떤 함수의 Parameter로 변수를 전달하는 것 처럼 함수의 Parameter로 함수를 전달할 수 있어야 하며 함수에서 변수를  Return 하듯이 함수를 Return 할 수 있어야 합니다.

 

순수 함수로 정의되는 함수는 Side Effect가 발생하지 않는 함수라는 뜻입니다. 이는 같은 Parameter를 전달해서 함수를 호출하면 항상 같은 결과를 Return 한다는 것을 의미합니다.

순수 함수가 되려면 함수 내에서 함수 밖의 데이터를 변경하는 작업이 없어야 하고 별도의 입출력이 없어야 합니다.

 

함수형 프로그래밍에서의 데이터

함수형 프로그래밍에서 데이터는 다음과 같은 2가지 특징을 갖고 있습니다.

데이터는 변경되지 않으며 프로그램의 상태만 표현한다. (데이터 불변성)
함수에서 데이터는 변경하지 않고 새로운 데이터를 만들어 Return 한다.

위 특징들을 정리하면 함수형 프로그래밍에서는 아예 변수라는 것이 없다는 개념입니다. 하나의 데이터가 변수에 저장되어 계속 변경되면서 데이터의 변경 흐름에 따라 프로그래밍 하는 것이 아니라, 변경할 수 없는 상수 데이터만 이용하고 함수의 흐름에 따라 프로그래밍하자는 개념입니다.

 

함수형 프로그래밍의 이점(장점)

코드가 간결하여 개발 생산성과 유지 보수성이 증대된다.
동시성 작업을 좀 더 쉽고 안전하게 구현할 수 있다.

데이터의 흐름에 따라 프로그래밍하면 코드를 분석할 때 알고리즘에 따라 분석할 수밖에 없습니다.

Break Point를 거록 한 줄 한 줄 데이터를 추적하면서 분석하면서 개발을 하곤 합니다.

 

함수형 프로그래밍으로 한다면 이런 일들이 필요가 없습니다. 함수 내에서 데이터를 변경하지도 않고 부수효과가 발생하지 않기 때문에 그냥 어떤 함수가 선언되었는지만 파악하면서 코드를 분석하고 개발할 수 있습니다.

 

이 때문에 코드가 간결하여 개발 생산성과 유지 보수성이 좋아집니다.

스레드를 구현할 때에도 안전성을 확보할 수 있고 병렬 처리에 도움을 줍니다.

 

함수형 프로그래밍의 원칙

앞에서 말한 원칙들 중에서 몇가지를 조금 더 자세하게 알아보도록 하겠습니다.

 

일급 객체로서의 함수

함수형 프로그래밍에서 일급 객체는 클래스가 아닌 함수입니다.  따라서 함수 안에 Variable, Function, Class 등이 올 수 있다는 것입니다.

함수가 단순히 클래스 밖에 선언해 파일의 최상의 레벨에 있다고 해서 일급 객체로 이용한다고 하지는 않습니다.

함수 내에 프로그램의 모든 구성요소를 포함해서 작성할 수 있어야 합니다.

함수 내에 변수, 다른 함수 심지어 다른 클래스를 작성할 수 있어야 일급 객체로 이용된다고 말할 수 있습니다.

 

함수가 일급 객체로 이용된다는 것은 함수의 선언 위치와 구성 요소 뿐 아니라 함수가 변수처럼 이용될 수 있는지도 중요합니다.

 

순수 함수로 정의되는 함수

fun some(a: Int): Int {
    return (Math.random() * a).toInt()
}

위 코드는 랜덤 숫자를 Return 하는 함수입니다.

이 함수는 순수 함수일까요? 아닙니다. 같은 값을 넣더라도 random()의 반환값에 따라 결과값이 달라집니다.

fun some(a: String): Boolean {
    try {
        val file = File("a.txt")
        val out = FileWriter(file)
        out.write(a)
        out.flush()
        return true
        
    } catch(e: Exception) {
        return false
    }
}

위 함수는 매개 변수 값으로 문자열을 전달 받고 파일로 저장합니다.

이 함수는 순수 함수 일까요? 아닙니다. 함수 내에서 파일 입출력이 발생합니다.

 

그렇다면 순수 함수란 어떤 함수를 말하는 것일까요? 다음 소스코드를 살펴봅시다.

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

두 개의 매개변수가 있고 결과값을 반환합니다. 이 함수는 동일 입력에 대하여 동일 결과를 반환합니다.

그리고 함수 내에서 별도의 입출력 등 부수 효과가 발생하지 않습니다. 따라서 순수 함수입니다.

 

매개 변수가 없는 함수는 순수 함수로써 의미가 없습니다.

순수 함수는 맞지만 의미가 없는 순수 함수라는 것입니다. (상수 변수로 충분히 정의 가능)

fun returnFun(a: Int, b: Int) {
    val result = a + b
}

위 함수는 매개 변수가 있습니다. 부수 효과도 없고 외부의 데이터를 변경하지도 않으므로 순수함수입니다.

그런데 함수의 반환 값이 없습니다. 이는 실행 결과값이 없다는 의미입니다.

이 역시 결국 의미 없는 순수 함수가 되게 됩니다.

 

정리하자면, 순수 함수는 매개 변수와 반환 값을 가지는 함수로써 같은 인수를 전달하여 함수를 호출하면 항상 같은 결과값이 나와야 하며 함수 내에서 부수 효과가 발생하지 않아야 합니다.

 

코틀린에서 일급 객체로서의 함수

다양한 구성요소를 포함하는 함수

fun superFun() {
    val superData = "hello"
    fun subFun1() {
        println("subFun1() .. superData : ${superData}")
    }
    fun subFun2(a: Int, b: Int): Int {
        subFun1()
        return a + b
    }
    
    class SubClass {
        fun classFun() {
            println("classFun() . superData : ${superData}")
        }
    }
    subFun1()
    SubClass().classFun()
}

코틀린에서는 위의 코드 처럼 함수 내에 함수나 클래스를 모두 선언할 수 있습니다.

 

변수처럼 이용되는 함수

val dataVal = 10
val funVal = fun someFun() { // ERROR
   // ..
}

일반 적인 방법을 생각하여 함수를 위와 같이 변수에 대입하려 한다면 컴파일 에러가 납니다.

이 경우 람다 함수(Lambda Function)를 이용히거나 함수 참조(Function Reference)를 이용해야 합니다.

 

val funVal = { x1: Int ->
    println("hello world")
    x1 * 10
}

funVal1(10)

위 코드를 보면 람다 함수를 사용하여 함수를 변수에 대입한 모습을 보이고 있습니다.

코틀린에서는 람다 함수를 많이 이용하지만, 함수 참조를 이용할 수도 있습니다.

 

함수 참조(Method Reference)를 이용한다는 것은 :: 이라는 연산자를 사용한다는 것입니다.

JAVA에서도 있는 것이며 간단한 코드를 살펴보도록 하겠습니다.

 

fun someFun() {
    println("i am some Function")
}

val funVal2 = ::someFun

funVal2()

함수 참조는 Reflection 기법으로 추후 포스트에서 알아보도록 하겠습니다.

이번 포스트에서는 함수형 프로그래밍에 대한 간략한 소개, 원칙 등에 대해 알아보았습니다.

다음 포스트에서는 람다 표현식, 고차함수에 대해 알아보도록 하겠습니다.

 

 

 

 

 

 

 

 

 

 

 

 

반응형
LIST
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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 31
글 보관함