Functional Programming

이유석·2022년 5월 19일
0
post-thumbnail

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

정의

  • 자료처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임을 의미한다.
  • 프로그래밍 패러다임 중 하나이다. 일련의 코딩 접근 방식이다.

프로그래밍 패러다임

  • Procedual (절차적) → Object Oriented (객체지향) → Functional (함수형) 순서로 발전되어왔다.

  • 명령형 프로그래밍 (How) (Procedual, Object Oriented)

    • 알고리즘을 명시하고 목표는 명시하지 않는다.
    • 프로그램의 상태와 상태 변경을 시키는 '구문'의 관점에서 연산을 설명하는 방식
    • 절차적 프로그래밍 : 수행되어야 할 연속적인 계산 과정을 포함하는 방식
    • 객체지향 프로그래밍 : 객체들의 집합으로 프로그램의 상호작용을 표현
  • 선언형 프로그래밍 (What)
    • 알고리즘을 명시 하지않고, 목표만 명시한다.
    • 어떤 방식(How)가 아닌 무엇(What)과 같은지를 설명하는 방식
    • 함수형 프로그래밍 : 순수함수를 보조함수와 조합하는 방식

함수형 프로그래밍의 특징

1. Pure Function (순수 함수)

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

function add(a) {
  return a + num;
}

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

// O 순수 함수
function add(a, b){
  return a+b;
}

const result = add(2,3);

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

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

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

  • 함수형 프로그래미에서의 데이터는 변하지 않는 불변성을 유지해야 한다.
  • 데이터의 변경이 필요한 경우
    • 원본 데이터 구조를 변경하지 않고 해당 데이터의 복사본을 만들어서, 복사본의 일부를 변경하고 해당 복사본을 사용해 작업을 진행한다.
// Stateless, Immutability 불만족
let person = {name: "yuseogi", age: "24"}

function increaseAge(person) {
  person.age = person.age + 1;
  return person;
}

위와 같은 예제는 increaseAge 함수에서 전역으로 선언된 personage 속성을 변경하므로 불변성 유지를 못한다.

// Stateless, Immutability 만족
const person = {name: "yuseogi", age: "24"}

function increaseAge(person) {
  return {...person, age: person.age+1};
  // ...person : 객체 복사
  // age: person.age+1 : ...person을 통해 복사한 객체에 새로운 값 할당
}

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

3. Expressions (선언형 함수)

  • 선언형 프로그래밍은 무엇을 할 것인가에 주목한다.
  • 명령형 프로그래밍은 무엇을 어떻게 할 것인가에 주목한다.
// 명령형 프로그래밍
let numbers = [1,2,3];

function multiply(numbers, multiplier) {
  for (let i = 0; i < numbers.length; i) {
    numbers[i] = numbers[i]*multiplier;
  }
}

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

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

// 선언형 프로그래밍
function multiply(numbers, multiplier) {
  return number.map((num) => num*multiplier);
}

위의 예시는 for 문을 map 으로 대체했고, Javascript에서는 filter, map, take, reduce 등의 함수형 코드를 지원한다.

4. First-class, High-order functions (1급 객체, 고차 함수)
함수형 프로그래밍에서는 함수가 1급 객체가 된다. 1급 객체의 특징은 다음과 같다.

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

const transform = (numbers) => numbers.map(addTwo).map(multiplyTwo);

console.log(transform([1, 2, 3, 4])); // [6, 8, 10, 12]

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

함수형 프로그래밍에서는 함수가 고차 함수가 된다. 고차 함수의 특징은 다음과 같다.

  • 함수를 인자로써 전달 할 수 있어야 한다.
  • 함수의 반환 값으로 또 다른 함수를 사용 할 수 있다.
// 고차 함수
const addInform = (name) => (age) => age + name;
const yuseogi = addInform("유석");

console.log(yuseogi("24")); // 24유석

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

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

장점

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

단점

  • 순수함수를 구현하기 위해서는 코드의 가독성이 좋지 않을 수 있다
  • 함수형 프로그래밍에서는 반복이 for문이 아닌 재귀를 통해 이루어지는데 (deep copy), 재귀적 코드 스타일은 무한 루프에 빠질 수 있다
  • 순수함수를 사용하는 것은 쉬울 수 있지만 조합하는 것은 쉽지 않다

Java 에서의 함수형 프로그래밍

Java 8으로 update되면서, Java에서도 함수형 프로그래밍이 가능해졌다.

  • 함수형 프로그래밍 : 부수효과를 없애고 순수 함수를 만들어 모듈화 수준을 높이는 프로그래밍 패러다임
  • 부수효과 : 주어진 값 이외의 외부 변수 및 프로그래밍 실행에 영향을 끼쳐 예상과 다른 결과값이 나온다.

Java의 객체 지향은 명령형 프로그래밍이고, 함수형은 선언형 프로그래밍이다.

둘의 차이는 문제해결의 관점이다.

  • 객체 지향 : 데이터를 어떻게 처리할 지에 대한 명령을 통해 문제를 해결했다.
  • 함수형 : 무엇을 풀어나가야할지 결정하여 문제를 해결한다.

Java에서 활용할 수 있는 함수형 프로그래밍

  • lambda (람다 식)
  • stream api
  • 함수형 인터페이스
import java.util.Arrays;
import java.util.List;

public class stream {

	public static void main(String[] args) {
		List<String> myList = Arrays.asList("a", "b", "c", "d", "e");
 
        // 기존방식
        for(int i=0; i<myList.size(); i++){
            String s = myList.get(i);
            if(s.startsWith("c")){
                System.out.println(s.toUpperCase());
            }
        }
 
        // stream API를 이용한 방식
        myList.stream()
              .filter(s -> s.startsWith("c"))
              .map(String::toUpperCase)
              .forEach(System.out::println);
 
	}

}
profile
소통을 중요하게 여기며, 정보의 공유를 통해 완전한 학습을 이루어 냅니다.

0개의 댓글