함수형 프로그래밍이란 무엇입니까?
함수형 프로그래밍 은 명령형 프로그래밍 언어에서 사용되는 명령 목록이 아니라 함수와 해당 응용 프로그램을 사용하는 프로그래밍 스타일입니다
특히 1936년 수학자 알론조 처치(Alonzo Church)가 계산 가능성을 위한 공식 모델로 고안한 람다 미적분학( Lambda Calculus ) 으로 알려진 수학의 한 분야인 수학에 뿌리를 둔 보다 추상적인 프로그래밍 스타일입니다 . 하나의 표현식을 다른 표현식에 매핑하는 표현식과 함수로 구성됩니다. 기본적으로 이것이 함수형 프로그래밍에서 우리가 하는 일입니다. 함수를 사용하여 값을 다른 값으로 변환합니다.
이 기사의 저자는 최근 몇 년 동안 함수형 프로그래밍과 사랑에 빠졌습니다. 우리는 보다 기능적인 스타일을 장려하는 JavaScript 라이브러리를 사용하기 시작했고 Haskell 에서 코딩하는 방법을 배우면서 바로 심층으로 뛰어들었습니다 .
Haskell은 1990년대에 개발된 순수 함수형 프로그래밍 언어로 Scala 및 Clojure와 유사합니다. 이러한 언어를 사용하면 기능적 스타일로 코딩해야 합니다. Haskel을 배우면서 우리는 함수형 프로그래밍이 제공하는 모든 이점을 진정으로 이해할 수 있었습니다.
JavaScript는 명령형, 객체 지향 또는 기능적 스타일로 프로그래밍하는 데 사용할 수 있는 다중 패러다임 언어입니다. 그러나 함수는 변수에 할당될 수 있는 일급 개체 이므로 기능적 스타일에 특히 적합합니다 . 이는 또한 함수가 다른 함수 의 반환 값일 뿐만 아니라 다른 함수(종종 콜백 이라고도 함)에 대한 인수로 전달될 수 있음을 의미합니다. 다른 함수를 반환하거나 매개 변수로 받아들이는 함수를 고차 함수 라고 하며 함수형 프로그래밍의 기본 부분입니다.
기능적 스타일로 JavaScript를 프로그래밍하는 것은 특히 React의 부상과 함께 최근 몇 년 동안 훨씬 더 대중화되었습니다. React는 함수형 접근 방식에 적합한 선언적 API를 사용하므로 함수형 프로그래밍의 원칙을 확실하게 이해하면 React 코드가 향상됩니다.
Background Image
Progress Bar
00:00 00:00
함수형 프로그래밍이 좋은 이유는 무엇입니까?
요컨대, 기능적 프로그래밍 언어는 종종 간결하고 명확하며 우아한 코드로 이어집니다. 코드는 일반적으로 테스트하기 쉽고 문제 없이 다중 스레드 환경에 적용할 수 있습니다.
만약 당신이 많은 다른 프로그래머들과 이야기한다면, 함수형 프로그래밍을 절대적으로 혐오하는 사람부터 절대적으로 좋아하는 사람에 이르기까지 각각의 함수형 프로그래밍에 대해 완전히 다른 의견을 듣게 될 것입니다. 우리(이 기사의 저자)는 척도의 "좋아" 끝에 앉아 있지만, 특히 일반적으로 프로그래밍을 가르치는 방식과 매우 다른 접근 방식이기 때문에 모든 사람이 차를 마시는 것은 아니라는 점에 전적으로 감사합니다.
그러나 일단 함수형 프로그래밍의 요령을 터득하고 사고 과정이 클릭되면 그것은 제2의 천성이 되어 코드 작성 방식을 바꿉니다.
사이버 먼데이 세일 60% 할인 - 추천
규칙 1: 함수를 정화하라
함수형 프로그래밍의 핵심 부분은 작성하는 함수가 "순수"한지 확인하는 것입니다. 이 용어를 처음 사용하는 경우 순수 함수는 기본적으로 다음 조건을 충족합니다.
참조 투명성이 있습니다. 즉, 동일한 인수가 주어지면 함수는 항상 동일한 값을 반환합니다. 모든 함수 호출은 반환 값으로 대체될 수 있으며 프로그램은 여전히 동일한 방식으로 작동합니다.
부작용이 없습니다 . 이것은 함수가 함수 범위 밖에서 변경하지 않는다는 것을 의미합니다. 여기에는 전역 값 변경, 콘솔에 로깅 또는 DOM 업데이트가 포함될 수 있습니다.
순수 함수 에는 최소한 하나의 매개변수가 있어야 하며 값을 반환 해야 합니다. 생각해 보면 인수를 허용하지 않으면 작업할 데이터가 없으며 값을 반환하지 않으면 함수의 요점은 무엇입니까?
순수한 함수가 처음에는 완전히 필요하지 않은 것처럼 보일 수 있지만, 순수하지 않은 함수는 프로그램의 전체 변경으로 이어져 심각한 논리 오류로 이어질 수 있습니다!
예를 들어:
//impure
let minimum = 21
const checkAge = age => age >= minimum
//pure
const checkAge = age => {
const minimum = 21
return age >= minimum
}
impure 함수에서 checkAge함수는 가변 변수에 의존합니다 minimum. 예를 들어 minimum변수가 프로그램에서 나중에 업데이트되는 경우 checkAge함수는 동일한 입력으로 부울 값을 반환할 수 있습니다.
다음을 실행한다고 상상해 보십시오.
checkAge(20) >> false
이제 코드 후반부에서 changeToUK()함수가 의 값 minimum을 18로 업데이트한다고 상상해 봅시다.
그런 다음 다음을 실행한다고 상상해보십시오.
checkAge(20) >> true
이제 함수 checkAge는 동일한 입력을 받았음에도 불구하고 다른 값으로 평가됩니다.
순수 함수는 인수로 제공된 값 이외의 다른 값에 의존하지 않기 때문에 코드의 이식성을 향상시킵니다. 반환 값이 절대 변경되지 않는다는 사실은 순수 함수를 테스트하기 더 쉽게 만듭니다.
순수 함수를 일관되게 작성하면 돌연변이 및 부작용이 발생할 가능성도 제거됩니다.
돌연변이는 함수형 프로그래밍에서 큰 위험 신호이며, 그 이유에 대해 더 알고 싶다면 A Guide to Variable Assignment and Mutation in JavaScript에서 이에 대해 읽을 수 있습니다 .
함수의 이식성을 높이려면 함수가 항상 순수 하게 유지되도록 하십시오 .
규칙 2: 변수를 일정하게 유지
변수 선언은 모든 프로그래머가 가장 먼저 배우는 것 중 하나입니다. 사소해 보이지만 기능적 스타일의 프로그래밍을 사용할 때는 매우 중요합니다.
함수형 프로그래밍의 핵심 원칙 중 하나는 일단 변수가 설정되면 프로그램 전체에서 해당 상태로 유지된다는 생각입니다.
이것은 코드에서 변수의 재할당/재선언이 재앙이 될 수 있는 방법을 보여주는 가장 간단한 예입니다.
const n = 10
n = 11
TypeError: "Attempted to assign to readonly property."
생각해 보면 의 값은 과 n동시에 될 수 없습니다 . 그것은 논리적으로 말이 되지 않습니다.1011
명령형 프로그래밍의 일반적인 코딩 방법은 다음 코드를 사용하여 값을 증가시키는 것입니다.
let x = 5
x = x + 1
수학에서 이 진술 은 비논리적입니다. 양변에서 x = x + 1빼면 가 남을 것이기 때문에 분명히 사실이 아닙니다.x0 = 1
이러한 이유로 Haskell에서는 변수를 하나의 값에 할당한 다음 다른 값에 다시 할당할 수 없습니다. JavaScript에서 이를 달성하려면 항상 를 사용하여 변수를 선언const 하는 규칙을 따라야 합니다 .
나만의 개발자 포트폴리오 구축 - 무료
규칙 3: 화살표 기능 사용
수학에서 함수의 개념은 일련의 값을 다른 값 집합에 매핑하는 것입니다. 아래 다이어그램은 왼쪽 값 집합을 제곱하여 오른쪽 값 집합에 매핑하는 함수를 보여줍니다.
숫자를 제곱하는 수학 함수
이것이 화살표 표기법을 사용하여 수학으로 작성되는 방법입니다: f: x → x². 이는 함수 f가 값 x을 에 매핑함을 의미합니다 x².
화살표 함수를 사용하여 이 함수를 거의 동일하게 작성할 수 있습니다.
const f = x => x**2
JavaScript에서 기능적 스타일을 사용하는 주요 기능은 일반 함수가 아닌 화살표 함수를 사용하는 것입니다. 물론 이것은 결국 스타일로 귀결되며 일반 함수 대신 화살표 함수를 사용하는 것이 코드의 "기능"에 실제로 영향을 미치지는 않습니다.
그러나 함수형 프로그래밍 스타일을 사용할 때 적응하기 가장 어려운 것 중 하나는 모든 함수가 입력을 출력에 매핑하는 사고 방식입니다. 절차 같은 것은 없습니다. 화살표 함수를 사용하면 함수 프로세스를 훨씬 더 잘 이해할 수 있습니다.
화살표 함수에는 이 매핑을 시각화하는 데 실제로 도움이 되는 암시적 반환 값이 있습니다.
화살표 함수의 구조, 특히 암시적 반환 값은 순수 함수 작성을 장려하는 데 도움이 됩니다.
args => returnValue
특히 화살표 함수를 작성할 때 강조하고 싶은 또 다른 사항은 삼항 연산자를 사용하는 것입니다. 삼항 연산자 에 익숙하지 않은 경우 삼항 연산자 는 인라인 if...else문이며 형식 condition ? value if true : value if false입니다.
Quick Tip: How to Use the Ternary Operator in JavaScript 에서 자세한 내용을 읽을 수 있습니다 .
함수형 프로그래밍에서 삼항 연산자를 사용하는 주요 이유 중 하나는 else문의 필요성입니다. 프로그램 은 원래 조건이 충족되지 않는 경우 수행할 작업을 알고 있어야 합니다 . 예를 들어 Haskell은 else명령문을 실행하고 명령문이 제공되지 않으면 오류를 반환합니다.
삼항 연산자를 사용하는 또 다른 이유는 삼항 연산자가 잠재적 부작용이 있는 작업을 수행하는 데 사용할 수 있는 명령문이 아니라 항상 값을 반환하는 표현식 이기 때문입니다. if-else이는 반환 값이 있는지 확인하고 입력을 출력에 매핑하는 이미지를 유지할 수 있기 때문에 화살표 함수에 특히 유용합니다. 명령문과 표현식의 미묘한 차이에 대해 잘 모르겠다면 명령문과 표현식에 대한 이 가이드를 읽어볼 가치가 있습니다.
이 두 가지 조건을 설명하기 위해 다음은 삼항 연산자를 사용하는 간단한 화살표 함수의 예입니다.
const action = state => state === "hungry" ? "eat cake" : "sleep"
함수 는 인수 action값에 따라 "eat" 또는 "sleep" 값을 반환합니다 .state
따라서 결론적으로 코드를 더 기능적으로 만들 때 다음 두 가지 규칙을 따라야 합니다.
화살표 표기법을 사용하여 함수 작성
if...else명령문을 삼항 연산자로 바꾸기
Background Image
Progress Bar
00:00 00:00
규칙 4: For 루프 제거
반복 코드를 작성하기 위해 루프를 사용하는 것이 프로그래밍에서 매우 일반적이라는 점을 감안할 때 for루프를 피하라고 말하는 것은 꽤 이상해 보입니다. 사실 Haskell에 어떤 종류의 루프 연산도 없다는 사실을 처음 발견했을 때 for일부 표준 연산을 달성할 수 있는 방법을 이해하기 위해 애썼습니다. for그러나 함수형 프로그래밍에서 루프가 나타나지 않는 몇 가지 아주 좋은 이유가 있으며 곧 for루프를 사용하지 않고도 모든 유형의 반복 프로세스를 수행할 수 있다는 사실을 알게 되었습니다.
루프 를 사용하지 않는 가장 중요한 이유는 for루프가 가변 상태에 의존하기 때문입니다. sum간단한 함수 를 살펴보겠습니다 .
function sum(n){
let k = 0
for(let i = 1; i < n+1; i++){
k = k + i
}
return k
}
sum(5) = 15 // 1 + 2 + 3 + 4 + 5
보시다시피 루프 자체에서 a를 사용해야 하고 let루프 for내에서 업데이트하는 변수에도 사용해야 합니다 for.
이미 설명했듯이 함수형 프로그래밍의 모든 변수는 변경할 수 없어야 하므로 이는 함수형 프로그래밍에서 일반적으로 나쁜 습관입니다.
모든 변수가 불변인 코드를 작성하려면 재귀를 사용할 수 있습니다.
const sum = n => n === 1 ? 1 : n + sum(n-1)
보시다시피 어떤 변수도 업데이트되지 않습니다.
우리 중 수학자들은 이 모든 코드가 불필요하다는 것을 분명히 알 것입니다 0.5*n*(n+1). for그러나 루프와 재귀 의 가변성 사이의 차이를 설명하는 좋은 방법입니다 .
하지만 재귀가 가변성 문제에 대한 유일한 해결책은 아닙니다. 특히 배열을 다룰 때 그렇습니다. JavaScript에는 변수를 변경하지 않고 배열의 값을 반복하는 내장형 고차 배열 메서드가 많이 있습니다.
예를 들어 배열의 모든 값에 1을 더하고 싶다고 가정해 보겠습니다. 명령형 접근 방식과 for루프를 사용하여 함수는 다음과 같이 보일 수 있습니다.
function addOne(array){
for (let i = 0; i < array.length; i++){
array[i] = array[i] + 1
}
return array
}
addOne([1,2,3]) === [2,3,4]
그러나 for루프 대신 JavaScript의 내장 map메서드를 사용하여 다음과 같은 함수를 작성할 수 있습니다.
const addOne = array => array.map(x => x + 1)
이전 에 함수를 만난 적이 없다면 특히 JavaScript의 함수형 프로그래밍에 정말 관심이 있는 경우 map와 같은 JavaScript의 내장된 모든 고차 배열 메서드와 함께 함수에 대해 배울 가치가 있습니다 . Immutable Array Methods: How to Write Remarkably Clean JavaScript Codefilter 에서 더 많은 정보를 찾을 수 있습니다 .
Haskell에는 for루프가 전혀 없습니다. JavaScript를 보다 기능적으로 만들려면 대신 재귀 및 내장된 고차 배열 메서드를 사용하여 for 루프를 사용하지 마십시오.
사이버 먼데이 세일 60% 할인 - 추천
규칙 5: 유형 강제를 피하십시오
유형 선언이 필요하지 않은 JavaScript와 같은 언어로 프로그래밍할 때 데이터 유형의 중요성을 간과하기 쉽습니다. JavaScript에서 사용되는 7가지 기본 데이터 유형은 다음과 같습니다.
숫자
끈
부울
상징
빅인트
한정되지 않은
없는
Haskell은 유형 선언이 필요한 강력한 유형 의 언어입니다. 즉, 어떤 함수를 사용하기 전에 Hindley-Milner 시스템 을 사용하여 들어오는 데이터 유형과 나가는 데이터 유형을 지정해야 합니다 .
예를 들어:
add :: Integer -> Integer -> Integer
add x y = x + y
x두 개의 숫자( 와 ) 를 더하는 아주 간단한 함수입니다 y. 이와 같은 매우 간단한 것을 포함하여 모든 단일 함수에 대한 데이터 유형이 무엇인지 프로그램에 설명해야 하는 것이 약간 우스꽝스럽게 보일 수 있지만 궁극적으로 함수가 어떻게 작동하고 반환할 것으로 예상되는지 보여주는 데 도움이 됩니다. 이렇게 하면 코드가 훨씬 더 복잡해지기 시작할 때 디버그하기가 훨씬 쉬워집니다.
형식 선언은 다음 구조를 따릅니다.
functionName :: inputType(s) -> outputType
유형 강제는 데이터 유형 불일치를 해결하기 위해 사용할 수 있는(또는 악용 될 수 있는) 모든 종류의 해킹이 있는 JavaScript를 사용할 때 큰 문제가 될 수 있습니다. 다음은 가장 일반적인 문제와 이를 방지하는 방법입니다.
연결 . 일관성이 없는 로 "Hello" + 5평가됩니다 . "Hello5"문자열을 숫자 값과 연결하려면 "Hello" + String(5).
부울 문 및 0 . JavaScript에서 0명령문 if의 값은 와 동일합니다 false. 이로 인해 숫자 데이터가 와 같은지 확인하는 것을 무시하는 게으른 프로그래밍 기술이 발생할 수 있습니다 0.
예를 들어:
const even = n => !(n%2)
숫자가 짝수인지 아닌지를 평가하는 함수입니다. 기호를 사용하여 결과를 부울 값으로 !강제 변환하지만 결과 는 부울 값이 아니라 숫자( 또는 )입니다.n%2 ?n%201
이와 같은 해킹은 영리해 보이고 작성하는 코드의 양을 줄이면서 함수형 프로그래밍의 유형 일관성 규칙을 위반합니다. 따라서 이 함수를 작성하는 가장 좋은 방법은 다음과 같습니다.
// even :: Number -> Number
const even = n => n%2 === 0
또 다른 중요한 개념은 배열의 모든 데이터 값이 동일한 유형인지 확인하는 것입니다. 이것은 JavaScript에 의해 적용되지 않지만 동일한 유형이 없으면 고차 배열 메서드를 사용하려는 경우 문제가 발생할 수 있습니다.
예를 들어 product배열의 모든 숫자를 곱하고 결과를 반환하는 함수는 다음 유형 선언 주석으로 작성할 수 있습니다.
// product :: [ Number ] -> Number
const product = numbers => numbers.reduce((s,x) => x * s,1)
여기서 유형 선언은 함수의 입력이 유형의 요소를 포함하는 배열 Number이지만 단일 숫자만 반환한다는 것을 분명히 합니다. 유형 선언은 이 함수의 입력 및 출력으로 예상되는 것을 명확하게 합니다. 분명히 이 함수는 배열이 숫자로만 구성되지 않은 경우 작동하지 않습니다.
Haskell은 강력한 유형의 언어이고 JavaScript는 약한 유형이지만 JavaScript를 더 기능적으로 만들려면 함수를 선언하기 전에 유형 선언 주석을 작성하고 유형 강제 단축키를 피해야 합니다.
유형 일관성을 강화할 JavaScript 대신 강력한 유형의 대안을 원하는 경우 분명히 TypeScript 로 전환할 수 있음을 여기에서 언급해야 합니다.
Background Image
Progress Bar
00:00 00:00
결론
요약하면 다음은 기능적 코드를 달성하는 데 도움이 되는 다섯 가지 규칙입니다.
함수를 순수하게 유지하십시오 .
항상 const 를 사용하여 변수와 함수를 선언하십시오 .
기능에 화살표 표기법을 사용하십시오 .
for 루프 를 사용하지 마십시오 .
유형 선언 주석을 사용하고 유형 강제 단축키를 피하십시오.
이러한 규칙은 코드가 순전히 기능적임을 보장하지는 않지만 코드를 보다 기능적으로 만들고 보다 간결하고 명확하며 테스트하기 쉽게 만드는 데 큰 도움이 됩니다.
이 규칙이 우리를 도왔던 만큼 여러분에게도 도움이 되기를 진심으로 바랍니다! 우리 둘 다 함수형 프로그래밍의 열렬한 팬이며 모든 프로그래머가 함수형 프로그래밍을 사용하도록 강력히 권장합니다.
기능적 JavaScript에 대해 더 자세히 알고 싶다면 온라인에서 무료로 제공되는 Frisby 교수의 기능적 프로그래밍에 대한 대부분의 적절한 가이드를 읽는 것이 좋습니다 . 그리고 끝까지 가서 하스켈을 배우고 싶다면, 우리는 Try Haskell 대화형 튜토리얼을 사용하고 온라인에서도 무료로 읽을 수 있는 훌륭한 책인 Learn You A Haskell For Greater Good 책을 읽는 것을 추천합니다.