함수형 프로그래밍(FP)이란?

o_jooon_·2024년 2월 26일
1

CS

목록 보기
5/6
post-thumbnail

지난 번에 포스팅 했던 객체 지향 프로그래밍(OOP)에 이어, 이번 포스팅은 함수형 프로그래밍(FP)에 관한 글입니다.
iOS 개발을 위해 Swift를 사용하다보니, OOP뿐만 아니라 FP에 대해 관심이 생겨 공부 해보았습니다.
아마 다음은 프로토콜 지향에 대한 포스팅을 작성할 것 같습니다.

CS에서 항상 나오는 객체 지향이 아닌 새로운 패러다임에 대해 공부하니 자료도 훨씬 적고,
객체 지향과 완전히 다른 방식의 패러다임이라 이해하는 데에 시간이 조금 걸렸네요.


함수형 프로그래밍(Functional Programming)이란?

함수형 프로그래밍(Functional Programming, FP)은 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임.
죽, 데이터를 함수로 연결하는 것을 중심으로 사고하는 프로그래밍 방법.

  • 함수형 프로그래밍은 거의 모든 것을 순수 함수로 나누어 문제를 해결한다.
    가독성을 높이고 유지보수를 용이하게 한다.
  • 값이나 상태 변화보다는 함수 자체의 응용을 중요시 한다.
  • 프로그램의 동작 과정에서 상태가 변하지 않으면 상호 간섭 없이 함수가 실행된다.
    → 병렬 처리를 할 때 부작용이 거의 없다.

함수형 프로그래밍 언어

  • Haskell, Clojure, Scala, F#, Scheme 등
  • Swift, Python, JavaScript → 함수형 프로그래밍의 특성 가지고 있다.

함수형 프로그래밍의 특징

  • 함수형 프로그래밍의 특징으로는 순수 함수, 1급 객체, 고차 함수, 불변성 등이 있다.

1. 순수 함수(Pure functions)

  • 동일한 입력에 대해 동일한 출력을 생성한다.
  • 외부 상태에 의존하거나 범위 밖의 상태를 수정하지 않는다.
  • 함수의 실행이 프로그램의 실행에 영향을 미치지 않는다.
  • 부수 효과(Side effects)가 없다.
  • 함수가 독립적이며 부수 효과가 없기 때문에 쓰레드 안전성을 보장받을 수 있다.(메모리, I/O의 관점에서 부수 효과가 없기 때문)
    테스트 및 병렬화가 쉽다.

부수 효과(Side effects)란?

  • 변수의 값이 변경된다.
  • 자료구조를 제자리에서 수정한다.
  • 콘솔 또는 I/O가 발생한다.

순수 함수의 예시(Swift)

// 순수 함수가 아닌 경우(부수 효과를 가진다)
// result가 var로 선언되었기 때문에 변수의 값이 변경(부수 효과)된다.
var result = 0

func addImpure(a: Int, b: Int) {
		result = a + b
}

addImpure(a: 3, b: 4)
print(result)  // Output: 7

// 순수 함수의 경우(부수 효과를 가지지 않는다)
func addPure(a: Int, b: Int) -> Int {
		return a + b
}

print(addPure(a: 3, b: 4))  // Output: 7

2. 1급 객체(First-class objects)

  • 함수를 1급 객체로서 사용할 수 있다.
  • 변수나 데이터 구조 안에 담을 수 있다.
  • 파라미터로 전달할 수 있다.
  • 반환값으로 사용할 수 있다.
  • 할당에 사용된 이름과 무관하게 고유한 구별이 가능하다.

1급 객체의 예시(Swift)

// 1급 객체로서의 함수(변수 안에 함수가 담겨있다)
let add = { (a: Int, b: Int) -> Int in
		return a + b
}

// 함수를 파라미터로 전달
func applyOperation(a: Int, b: Int, operation: (Int, Int) -> Int) -> Int {
		return operation(a, b)
}

// 위의 코드 사용
let result = applyOperation(a: 3, b: 4, operation: add)
print(result)  // Output: 7

// 1급 객체인 함수를 반환하는 함수
func makeMultiplier(_ multiplier: Int) -> (Int) -> Int {
		return { number in
				return number * multiplier
		}
}

let multiplyByThree = makeMultiplier(3)
print(multiplyByThree(4))  // Output: 12

// 코드 진행 과정
// makeMultiplier 함수는 (Int) -> Int 형태의 함수를 반환하는 함수이다.
// multiplyByThree는 가장 위의 함수 객체(add) 처럼
// number * 3을 반환하는 함수 객체가 된다.
// 따라서, multiplyByThree(4)는 4 * 3을 반환하여 값이 12가 된다.

3. 고차 함수(Higher-order functions)

  • 함수를 1급 객체로서 사용하는 것을 말한다.
  • 함수를 인자로서 전달할 수 있어야 한다.
  • 함수의 반환 값으로 또 다른 함수를 사용할 수 있다.
  • 추상화와 코드 재사용을 통해 강력한 메커니즘을 제공하며, 현대 프로그래밍 언어의 주요 특징이다.
  • Swift의 경우, map, filter, reduce, compactMap 등과 같은 표준 라이브러리에 있는 함수를 통해 데이터에 대한 복잡한 변환을 간결하게 수행할 수 있다.

고차 함수의 예시(Swift)

let numbers = [1, 2, 3, 4, 5]

// map
let squaredNumbers = numbers.map { $0 * $0 }
print(squaredNumbers)  // Output: [1, 4, 9, 16, 25]

// filter
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers)  // Output: [2, 4]

// reduce
let sum = numbers.reduce(0) { $0 + $1 }
print(sum)  // Output: 15

// forEach
numbers.forEach { print("Number: \($0)") }
// Output:
// Number: 1
// Number: 2
// Number: 3
// Number: 4
// Number: 5

// compactMap
let optionalNumbers: [Int?] = [1, nil, 3, nil, 5]
let unwrappedNumbers = optionalNumbers.compactMap { $0 }
print(unwrappedNumbers)  // Output: [1, 3, 5]

// flatMap
let nestedArray = [[1, 2], [3, 4], [5, 6]]
let flattenedArray = nestedArray.flatMap { $0 }
print(flattenedArray)  // Output: [1, 2, 3, 4, 5, 6]
  • 각 고차 함수에 대한 설명은 추후에 다시 정리하여 따로 포스팅 할 예정

4. 불변성(Immutability)

  • 함수형 프로그래밍에서의 데이터는 변하지 않는 불변성을 유지해야 한다.
  • 데이터의 변경이 필요한 경우, 기존의 데이터는 수정하지 않고 해당 데이터를 통해 새로운 데이터를 만든다.
  • 예상치 못한 버그의 가능성을 제거함으로써 보다 안전한 코드 작성이 가능하다.

불변성의 예시(Swift)

// 불변성의 데이터를 만드는 법은 간단하다.
// Swift에서는 let으로 선언해주면 된다.
let numbers = [1, 2, 3, 4, 5]
let person = ["name": "Jun", "age": 27]
let uniqueNumbers = Set([1, 2, 3, 4, 5])

// 불변성을 가진 변수와 해당 값 수정하는 방법:
// 기존 변수에 새로운 값을 추가한 새로운 변수를 생성
let initialAmount = 100
let finalAmount = initialAmount + 50

print(finalAmount)  // Output: 150

// 불변성을 가진 배열과 조건에 맞는 수만을 가진 배열로 수정하는 방법:
// 고차 함수를 이용해 새로운 배열을 생성
let squaredNumbers = numbers.map { $0 * $0 }
let evenNumbers = numbers.filter { $0 % 2 == 0 }

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

장점

  • 불변성을 지향하기 때문에 테스트 및 유지보수가 용이하다.
  • 코드 재사용 및 모듈화가 용이하다.
  • 어떻게(How) 보다 무엇을(What) 할 것인지를 강조하기 때문에 가독성이 좋다.
  • 병렬 프로그래밍에 효과적이다.

단점

  • 객체 지향 프로그래밍에 익숙한 사람들은 사고 방식의 전환이 필요하다.
  • 불변성의 데이터보다 가변성의 데이터를 사용하는 것이 더 효율적인 상황이 있을 수 있다.
  • 중첩 함수 및 고차 함수들로 프로그램이 복잡해지면 디버깅이 어려울 수 있다.
  • 모든 유형의 프로그램에 적합하지 않을 수 있다.

객체 지향 프로그래밍과 함수형 프로그래밍의 차이

  • OOP와 FP 모두 소프트웨어를 구축하기 위한 강력한 프로그래밍 패러다임을 제공하지만 접근 방식에 대한 차이가 있다.
  • 개발자는 프로젝트의 요구 사항 또는 개인 선호도를 기반으로 OOP와 FP 중에서 선택할 수 있다.
  • 최신 언어들은 OOP와 FP를 함께 적절하게 사용하여 보다 나은 소프트웨어를 개발할 수 있다.
객체 지향 프로그래밍(OOP)함수형 프로그래밍(FP)
개념객체의 개념이 중심
→ 객체들끼리 상호작용
1급 객체로서 함수의 개념이 중심
→ 함수는 값으로 취급되어 변수, 인자, 반환값으로 사용
제어 흐름명령형 제어 흐름 구조선언적 제어 흐름 구조
부수 효과프로그램에 영향을 미치는 경우가 생길 수 있음순수 함수를 이용하여 최소화를 목표로 함
모듈화캡슐화, 상속 및 다형성을 통해 모듈화고차 함수 및 불변 데이터 구조를 통해 모듈화

참조

https://didu-story.tistory.com/322

https://jongminfire.dev/함수형-프로그래밍이란

profile
iOS개발 공부 중입니다.

0개의 댓글