프로그래밍 패러다임

프로그래밍 패러다임은 프로그래머에게 프로그래밍의 관점을 갖게하고 결정하는 역할을 한다.

  • 명령형 프로그래밍: 프로그래밍의 상태와 상태를 변경시키는 구문의 관점에서 연산을 설명하는 방식
    • 절차지향 프로그래밍: 수행되어야 할 연속적인 계산 과정을 포함하는 방식 (C, C++)
    • 객체지향 프로그래밍: 객체들의 집합으로 프로그램의 상호작용을 표현 (C++, Java, C#)
  • 선언형 프로그래밍: 어떤 방법으로 해야 하는지(How)를 나타내기보다 무엇(What)과 같은지를 설명하는 방식
    • 함수형 프로그래밍: 순수 함수를 조합하고 소프트웨어를 만드는 방식 (클로저, 하스켈, 리스프)

명령형과 선언형의 프로그래밍 비교

  • 명령형: 알고리즘을 명시하고 목표는 명시 안함.
  • 선언형: 알고리즘 명시하지 않고 목표만 명시.

선언형 HTML 코드

/*
아래 html 코드를 보면, "어떻게"가 아닌 "무엇"을 하고 싶은지를 나타낸다.  
웹브라우저가 html을 어떻게 파싱하는지는 염두에 두지 않는다.  
headr와 paragraph를 웹사이트에 보여주고 싶다는 것만 명령하면 된다.
*/

<article>
  <header>
    <h1>Declarative Programming</h1>
    <p>Sprinkle Declarative in your verbiage to sound smart</p>
  </header>
</article>

명령형과 선언형 자바스크립트 코드 비교

// 명령형
function double (arr) {
  let results = []
  for (let i = 0; i < arr.length; i++){
    results.push(arr[i] * 2)
  }
  return results
}

function add (arr) {
  let result = 0
  for (let i = 0; i < arr.length; i++){
    result += arr[i]
  }
  return result
}

$("#btn").click(function() {
  $(this).toggleClass("highlight")
  $(this).text() === 'Add Highlight'
    ? $(this).text('Remove Highlight')
    : $(this).text('Add Highlight')
})


// 선언형
function double (arr) {
  return arr.map((item) => item * 2)
}

function add (arr) {
  return arr.reduce((prev, current) => prev + current, 0)
}

<Btn
  onToggleHighlight={this.handleToggleHighlight}
  highlight={this.state.highlight}>
    {this.state.buttonText}
</Btn>

"명령형 프로그래밍은 어떻게 할 것인가(How)를 표현하고, 선언형 프로그래밍은 무엇을 할 것인가(What) 표현한다."

함수형 프로그래밍

왜 함수형 프로그래밍을 배워야 할까?

"일반적인 프로그래밍은 그냥 생각하면 되는 것이고, 함수형 프로그래밍은 기존과 다르게 생각하는 방법을 알려줄 것이다. 그러므로 당신은 아마도 예전 방식으로 절대 돌아가지 않을 것이다."

함수형 프로그래밍은 프로그래밍 언어나 방식을 배우는것이 아니라 함수로 프로그래밍하는 사고를 배우는것이라고 할 수 있다.
즉, 새로운 계산방법을 배우는 것처럼 사고의 전환을 필요로 한다. 다양한 사고방식으로 프로그래밍을 바라보면 더욱 유연한 문제해결이 가능해진다.

배경설명

  • 1930년대에 알론소 처치의 람다 계산법부터 시작되었다.
    2

명령형 프로그래밍과의 비교

q12

함수형 프로그래밍은 계산을 수학적 함수의 조합으로 생각하는 방식을 말한다. 이것은 일반적인 프로그래밍 언어에서 함수가 특정 동작을 수행하는 역할을 담당하는 것과는 반대되는 개념으로, 함수를 수행해도 함수 외부의 값이 변경될 수 없다.

함수형 프로그래밍에 필요한 개념

1급 객체 (First Object, 또는 1급 시민)

  • 1급 객체(First class object)란 다음과 같은 조건을 만족하는 객체
    • 변수나 데이터 구조안에 담을 수 있다.
    • 파라미터로 전달 할 수 있다.
    • 반환값(return value)으로 사용할 수 있다.
    • 할당에 사용된 이름과 관계없이 고유한 구별이 가능하다.
    • 동적으로 프로퍼티 할당이 가능하다.

자바스크립트에서 함수(Function)은 객체(Object)이므로 1급 함수로 불린다.

고차 함수 (High-Order Function)

  • 람다 계산법에서 만들어진 용어로 아래 조건을 만족하는 함수
    • 함수에 함수를 파라미터로 전달할 수 있다.
    • 함수의 반환값으로 함수를 사용할 수 있다.

고차 함수는 1급 함수의 부분 집합(Subset)이다.
리액트의 고차 컴포넌트(HOC)는 컴포넌트를 사용하여 위의 조건을 만족하는 컴포넌트를 말한다.

불변성 (Immutablility)

  • 함수형 프로그래밍에서는 데이터가 변할 수 없는데, 이를 불변성 데이터라고 한다. (자바스크립트는 가능하지만 불가능한 언어들이 있다)
  • 데이터 변경이 필요한 경우, 원본 데이터 구조를 변경하지 않고 그 데이터를 복사본을 만들어 그 일부를 변경하고, 변경한 복사본을 사용해 작업을 진행한다.
// 불변이 아닌 변하는(Mutatable) 데이터
function rateColor(color, rating) {
  color.rating = rating;
  return color;
}

console.log(rateColor(color_lawn, 5), rating) // 5
console.log(color_lawn.rating) // 5

// 불변성 데이터
function rateColor(color, ratring) {
  return Object.assign({}, color, {ratring:ratring});
}

console.log(rateColor(color_lawn, 5), rating) // 5
console.log(color_lawn.rating) // 0  *변하지 않음*

const 키워드와 불변은 구분해야 한다. constObject로 사용되는 경우 변경 가능하다.

순수 함수 (Pure function)

  • 순수 함수란 함수형 프로그래밍에 필요한 개념으로 아래 조건을 만족하는 함수를 뜻한다.
    • 동일한 입력에는 항상 같은 값을 반환해야 한다.
    • 함수의 실행은 프로그램의 실행에 영향을 미치지 않아야 한다. (Side effect 가 없어야 한다)
      • 예를 들어, 함수 내부에서 인자의 값을 변경하거나 프로그램 상태를 변경하는 것

순수 함수를 호출하면 프로그램의 어떠한 변화도 없고, 입력 값에 대한 결과를 예상할수 있어서 테스트하기가 쉽다.

// 순수하지 않은 함수, DOM을 변경하는 부수효과를 발생시킴
function Header(text) {
  let h1 = document.createElement('h1');
  h1.innerText = text;
  document.body.appendChild(h1);
}

// 순수한 함수, 부수효과를 발생시키지 않음
// DOM을 변경하는 책임은 애플리케이션의 다른 부분이 담당하도록 한다.
const Header = (props) => <h1>{props.title}</h1>

데이터 변환방법

  • 함수형 프로그래밍은 데이터 변경이 불가능하기 때문에 기존 데이터의 복사본을 만들어 주는 도구들이 필요하다.
  • 자바스크립트에는 이미 Array.map, Array.reduce등 데이터 복사본을 만들기 위한 함수들을 제공하고 있다.
const schools = [
  "Yorktown"
  "Washington",
  "Wakefield"
];


// Array.join (합치기): 콤마(,)로 각 학교를 구분한 문자열 얻기
console.log(schools.join(",")); // "Yorktown", "Washington", "Wakefield"

// Array.filter (걸러내기): 'W'로 시작하는 학교만 있는 새로운 배열 만들기
// 원소를 제거하는 경우 Array.pop, Array.slice가 아닌 순수함수인 filter를 사용할 것
console.log(schools.filter(school => school[0] === "W")); // ["Washington", "Wakefield"]

// Array.map (맵핑): 모든 원소에 'High School' 문자열 추가된 새로운 배열 만들기
const highSchools = (schools.map(school => `${school} High School`));
console.log(highSchools.join("\n"));

// Array.reduce (축약): 배열에서 최대 값 찾기 (배열을 하나의 수로 변환)
const result = [21, 18, 42, 40, 64, 63, 24].reduce((max, num) => num > max ? num : max, 0);
console.log(result); // 64

합성 함수 (Function composition)

  • 합성 함수란 새로운 함수를 만들어거나 계산하기 위해 둘 이상의 함수를 조합하는 과정을 말한다. 함수형 프로그램은 여러 작은 순수 함수들로 이루어져있기 때문에 이 함수들을 연쇄적으로 또는 병렬로 호출해서 더 큰 함수를 만드는 과정으로 전체 프로그램을 구축해야 한다.

  • 메서드 체이닝 방식의 합성함수

const sum = (a, b) => a + b
const square = x => x * x
const addTen = x => x + 10

const computeNumbers = addTen(square(sum(3, 5))) // 74

// compose는 함수를 연쇄적으로 호출하면서 반환값을 전달한다 
const compose = (...fns) =>
  fns.reduce((prevFn, nextFn) =>
    (...args) => nextFn(prevFn(...args)),
    value => value
  );

// compose의 사용
const compute = compose(
  addTen,
  square,
  sum
)
compute(3, 5) // 74

함수형 프로그래밍을 구현하는 방법

함수형 프로그래밍은 순수 함수(pure function) 를 조합하고 공유 상태(shared state), 변경 가능한 데이터(mutable data) 및 부작용(side-effects) 을 피하여 소프트웨어를 만드는 프로세스다. 함수형 프로그래밍은 명령형(imperative) 이 아닌 선언형(declarative) 이며 애플리케이션의 상태는 순수 함수를 통해 전달된다

종합 예제

규칙

  • 모든 데이터는 변경이 불가능 해야한다.
  • 함수는 순수 함수로 만든다. 인자를 적어도 하나 이상 받게 만들고, 데이터나 다른 함수를 반환해야 한다.
  • 루프보다는 재귀를 사용한다.

시계 만들기

#######

Next..

  • 커링
  • 메모이제이션
  • 재귀
  • 합성
  • 모나드
  • 예제

참고