기능 프로그래머가 되길 원합니다 (1 부)

기능 프로그래밍 개념을 이해하기위한 첫 번째 단계를 취하는 것이 가장 중요하고 때로는 가장 어려운 단계입니다. 하지만 꼭 그럴 필요는 없습니다. 올바른 관점이 아닙니다.

운전 학습

운전을 처음 배웠을 때 어려움을 겪었습니다. 우리가 다른 사람들이 그것을하는 것을 보았을 때 쉽게 보였습니다. 그러나 그것은 생각했던 것보다 더 어려운 것으로 판명되었습니다.

우리는 부모님의 차에서 연습을했고 우리 동네에서 거리를 마스터하기 전까지는 고속도로에서 실제로 모험을하지 않았습니다.

그러나 반복되는 연습과 부모님이 잊고 싶어하는 몇 가지 시급한 순간을 통해 운전하는 법을 배우고 마침내 면허를 얻었습니다.

우리의 면허증을 가지고 우리는 가능한 한 차를 꺼낼 것입니다. 여행 할 때마다 점점 좋아지고 자신감이 커졌습니다. 우리가 다른 사람의 차를 운전해야하거나 우리의 차가 마침내 유령을 포기했을 때 우리는 새로운 것을 사야했습니다.

다른 차 바퀴 뒤에서 처음은 어땠습니까? 바퀴 뒤에서 처음 이었습니까? 근처에도 안. 처음으로, 그것은 모두 너무 이국적이었다. 우리는 그 전에는 차에 있었지만 승객으로서 만있었습니다. 이번에 우리는 운전석에있었습니다. 모든 컨트롤이있는 것.

그러나 우리가 두 번째 차를 운전할 때 우리는 단순히 열쇠가 어디로 가고, 조명이 어디에 있는지, 턴 신호를 어떻게 사용하고, 사이드 미러를 어떻게 조정하는지와 같은 몇 가지 간단한 질문을했습니다.

그 후, 그것은 꽤 부드러운 항해였습니다. 그러나 왜 이번이 처음에 비해 그렇게 쉬운가?

새 차가 낡은 차와 거의 비슷했기 때문입니다. 그것은 자동차가 필요로하는 것과 같은 기본적인 것들을 모두 가지고 있었고 거의 같은 장소에있었습니다.

몇 가지가 다르게 구현되었고 몇 가지 추가 기능이 있었을 수도 있지만 처음 또는 두 번째로 사용할 때는 사용하지 않았습니다. 결국, 우리는 모든 새로운 기능을 배웠습니다. 적어도 우리가 걱정했던 것.

프로그래밍 언어를 배우는 것은 이런 식입니다. 첫 번째는 가장 어렵다. 그러나 일단 당신이 당신의 벨트 아래에 하나가 있으면, 후속 것이 더 쉽습니다.

처음 외국어를 시작할 때 다음과 같은 질문을합니다.“모듈은 어떻게 작성합니까? 배열을 어떻게 검색합니까? 부분 문자열 함수의 매개 변수는 무엇입니까?”

이 새로운 언어를 운전하는 법을 배우면 자신의 삶을 편하게 해줄 수있는 몇 가지 새로운 것들로 구어를 생각 나게 할 수 있다고 확신합니다.

첫 우주선

평생 동안 한 대의 자동차를 운전하든 수십 대의 자동차를 운전하든 우주선의 바퀴를 타고 가고 있다고 상상해보십시오.

우주선을 타려고한다면 길에서 운전할 수있는 능력이 크게 도움이되지 않을 것입니다. 당신은 제로에서 시작합니다. (우리는 결국 프로그래머입니다. 우리는 0부터 시작합니다.)

당신은 우주에서 사물이 매우 다르다는 것을 기대하고 훈련을 시작하게 될 것입니다.

물리학은 변하지 않았습니다. 같은 우주 안에서 탐색하는 방식.

기능 프로그래밍 학습과 동일합니다. 상황이 매우 다를 것으로 예상해야합니다. 프로그래밍에 대해 알고있는 많은 부분은 번역되지 않습니다.

프로그래밍은 사고력이며 기능적 프로그래밍은 매우 다르게 생각하도록 가르칩니다. 그렇기 때문에 아마도 구식 사고 방식으로 돌아 가지 않을 것입니다.

당신이 알고있는 모든 것을 잊어라

사람들은이 문구를 말하는 것을 좋아하지만 사실입니다. 기능 프로그래밍 학습은 처음부터 시작하는 것과 같습니다. 완전하지는 않지만 효과적으로. 비슷한 개념이 많이 있지만 모든 것을 다시 배우기를 기대하는 것이 가장 좋습니다.

올바른 관점에서 올바른 기대를 가질 수 있고 올바른 기대로 상황이 어려워도 그만 두지 않을 것입니다.

함수형 프로그래밍으로는 더 이상 할 수없는 프로그래머로서 익숙한 모든 종류의 것들이 있습니다.

차에서와 마찬가지로 차도에서 벗어나기 위해 백업을 사용했습니다. 그러나 우주선에는 반대가 없습니다. 이제“무엇입니까? 리버스?! 어떻게 운전하지 않아도 될까요?”

3 차원 공간에서 조종 할 수 있기 때문에 우주선에서 후진 할 필요가 없습니다. 일단 이것을 이해하면 다시는 되돌릴 수 없습니다. 실제로 언젠가는 자동차가 얼마나 제한적 이었는지 다시 생각할 것입니다.

기능 프로그래밍 학습에는 시간이 걸립니다. 그러니 인내하십시오.

냉정한 프로그래밍 방식의 세계를 빠져 나가서 Functional Programming의 온천을 살짝 살펴 보자.

이 멀티 파트 기사에서 다룰 내용은 첫 번째 기능 언어로 뛰어 들기 전에 도움이되는 기능 프로그래밍 개념입니다. 또는 이미 뛰어들었다면 이해를 돕습니다.

서두르지 마십시오. 이 시점부터 읽고 시간을내어 코딩 예제를 이해하십시오. 각 섹션마다 아이디어가 사라지도록 읽기를 중단 할 수도 있습니다. 그런 다음 나중에 돌아와서 마무리하십시오.

가장 중요한 것은 이해하는 것입니다.

청정

기능 프로그래머가 순도에 대해 이야기 할 때, 순수 기능을 참조합니다.

순수한 함수는 매우 간단한 함수입니다. 입력 매개 변수에서만 작동합니다.

다음은 순수 함수의 Javascript 예입니다.

var z = 10;
함수 추가 (x, y) {
    x + y를 반환;
}

추가 기능은 z 변수를 건드리지 않습니다. z에서 읽지 않고 z에 쓰지 않습니다. 입력 내용 인 x와 y 만 읽고 함께 더한 결과를 반환합니다.

그것은 순수한 기능입니다. 추가 기능이 z에 액세스 한 경우 더 이상 순수하지 않습니다.

고려해야 할 다른 기능은 다음과 같습니다.

함수 justTen () {
    리턴 10;
}

justTen 함수가 순수하면 상수 만 반환 할 수 있습니다. 왜?

입력 한 내용이 없으므로 그리고 순수하기 때문에 자체 입력 이외의 다른 것에 액세스 할 수 없으므로 반환 할 수있는 유일한 것은 상수입니다.

매개 변수를 사용하지 않는 순수한 함수는 작동하지 않으므로 그다지 유용하지 않습니다. justTen이 상수로 정의되어 있으면 더 좋습니다.

가장 유용한 순수 함수는 하나 이상의 매개 변수를 가져야합니다.

이 기능을 고려하십시오.

함수 addNoReturn (x, y) {
    var z = x + y
}

이 함수가 어떤 것도 반환하지 않는 것에 주목하십시오. x와 y를 더하고 변수 z에 넣지 만 반환하지는 않습니다.

입력 만 처리하기 때문에 순수한 기능입니다. 추가하지만 결과를 반환하지 않으므로 쓸모가 없습니다.

유용한 모든 순수 함수는 무언가를 반환해야합니다.

첫 번째 추가 기능을 다시 고려해 보겠습니다.

함수 추가 (x, y) {
    x + y를 반환;
}
console.log (add (1, 2)); // 3을 인쇄합니다
console.log (add (1, 2)); // 여전히 3을 인쇄합니다
console.log (add (1, 2)); // 항상 3을 인쇄합니다

add (1, 2)는 항상 3입니다. 놀랍지는 않지만 함수가 순수하기 때문에. add 함수가 외부 값을 사용하면 동작을 예측할 수 없습니다.

Pure Functions는 항상 동일한 입력으로 동일한 출력을 생성합니다.

순수 함수는 외부 변수를 변경할 수 없으므로 다음 함수는 모두 불완전합니다.

writeFile (파일 이름);
updateDatabaseTable (sqlCmd);
sendAjaxRequest (ajaxRequest);
openSocket (ipAddress);

이 모든 기능에는 부작용이 있습니다. 그것들을 호출하면 파일과 데이터베이스 테이블을 변경하거나 서버로 데이터를 보내거나 OS를 호출하여 소켓을 얻습니다. 입력 및 리턴 출력에서 ​​작동하는 것 이상을 수행합니다. 따라서 이러한 함수가 무엇을 반환할지 예측할 수 없습니다.

순수한 기능에는 부작용이 없습니다.

Javascript, Java 및 C #과 같은 명령형 프로그래밍 언어에서는 부작용이 어디에나 있습니다. 프로그램의 어느 곳에서나 변수를 변경할 수 있기 때문에 디버깅이 매우 어렵습니다. 따라서 잘못된 시간에 변수가 잘못된 값으로 변경되어 버그가 발생하면 어디에서 보입니까? 어디에나? 그 좋지 않다.

이 시점에서 아마도“순수한 기능만으로 어떻게하면 좋을까요?”라고 생각하고있을 것입니다.

함수형 프로그래밍에서는 순수 함수 만 작성하는 것이 아닙니다.

기능적 언어는 부작용을 제거 할 수 없으며 제한 할 수 있습니다. 프로그램은 실제 세계와 인터페이스해야하기 때문에 모든 프로그램의 일부가 불완전해야합니다. 목표는 불순한 코드의 양을 최소화하고 나머지 프로그램과 분리하는 것입니다.

불변성

다음 코드를 처음 보았을 때 기억하십니까?

var x = 1;
x = x + 1;

그리고 당신이 가르치는 사람은 수학 수업에서 배운 것을 잊어 버리라고 했습니까? 수학에서 x는 절대로 x + 1과 같을 수 없습니다.

그러나 명령형 프로그래밍에서는 x의 현재 값을 1에 더하고 그 결과를 x에 다시 넣는 것을 의미합니다.

함수 프로그래밍에서 x = x + 1은 불법입니다. 수학에서 잊어 버린 것을 기억해야합니다.

함수형 프로그래밍에는 변수가 없습니다.

저장된 값은 여전히 ​​기록으로 인해 변수라고하지만 상수입니다. 즉 x가 값을 가져 오면 평생 동안 그 값이됩니다.

걱정하지 마십시오. x는 일반적으로 지역 변수이므로 수명이 짧습니다. 그러나 살아있는 동안에는 절대 변할 수 없습니다.

다음은 웹 개발을위한 순수 함수형 프로그래밍 언어 인 Elm의 상수 변수의 예입니다.

addOneToSum y z =
    방해
        x = 1
    ...에서
        x + y + z

ML 스타일 구문에 익숙하지 않은 경우 설명하겠습니다. addOneToSum은 y와 z라는 두 개의 매개 변수를 사용하는 함수입니다.

let 블록 내에서 x는 1의 값에 바인딩됩니다. 즉, 나머지 수명 동안 1과 같습니다. let 블록이 평가 될 때 함수가 종료되거나 수명이 다하면 수명이 다한 것입니다.

in 블록 내에서 계산에 let 블록에 정의 된 값 viz가 포함될 수 있습니다. 엑스. x + y + z 계산 결과는 더 정확하게 반환되거나 x = 1이므로 1 + y + z가 반환됩니다.

다시 한 번,“변수없이 무엇을해야합니까?”라는 질문을들을 수 있습니다.

변수를 수정하고 싶을 때를 생각해 봅시다. 다중 값 변경 (예 : 개체 또는 레코드의 단일 값 변경)과 단일 값 변경 (예 : 루프 카운터)의 두 가지 일반적인 경우가 있습니다.

함수형 프로그래밍은 값이 변경된 레코드 사본을 작성하여 레코드의 값 변경을 처리합니다. 이를 가능하게하는 데이터 구조를 사용하여 레코드의 모든 부분을 복사하지 않고도 효율적으로 수행합니다.

함수형 프로그래밍은 단일 값 변경을 복사하여 동일한 방식으로 해결합니다.

아, 그리고 루프가 없어서.

“변수는없고 루프도 없습니다! 난 너가 싫어!!!"

기다려. 루프를 수행 할 수없는 것 (말장난 의도가 없음)이 아니라, 특정, 수행, 반복, 반복 등과 같은 특정 루프 구조가 없다는 것입니다.

함수형 프로그래밍은 재귀를 사용하여 루핑을 수행합니다.

Javascript에서 루프를 수행 할 수있는 두 가지 방법은 다음과 같습니다.

// 간단한 루프 구성
var acc = 0;
(var i = 1; i <= 10; ++ i)
    acc + = i;
console.log (acc); // 55를 인쇄합니다
// 루프 구문 또는 변수가없는 경우 (재귀)
함수 sumRange (시작, 종료, acc) {
    if (시작> 끝)
        반품 acc;
    반환 sumRange (시작 + 1, 종료, acc + 시작)
}
console.log (sumRange (1, 10, 0)); // 55를 인쇄합니다

기능적 접근 방식 인 재귀가 새로운 시작 (start + 1)과 새로운 누산기 (acc + start)로 자신을 호출하여 for 루프와 동일한 방식을 달성 한 것에 주목하십시오. 이전 값은 수정하지 않습니다. 대신 이전 값에서 계산 된 새 값을 사용합니다.

불행히도, 두 가지 이유로 인해 약간의 시간을 들여 공부하더라도 Javascript에서는보기가 어렵습니다. 하나는 자바 스크립트의 구문이 시끄럽고 두 번째는 재귀 적으로 생각하는 데 익숙하지 않은 것입니다.

Elm에서는 다음을 읽고 이해하기가 더 쉽습니다.

sumRange 시작 종료 acc =
    시작> 끝이면
        acc
    그밖에
        sumRange (시작 + 1) 종료 (acc + 시작)

실행 방법은 다음과 같습니다.

sumRange 1 10 0 =-sumRange (1 + 1) 10 (0 + 1)
sumRange 2 10 1 =-sumRange (2 + 1) 10 (1 + 2)
sumRange 3 10 3 =-sumRange (3 + 1) 10 (3 + 3)
sumRange 4 10 6 =-sumRange (4 + 1) 10 (6 + 4)
sumRange 5 10 10 =-sumRange (5 + 1) 10 (10 + 5)
sumRange 6 10 15 =-sumRange (6 + 1) 10 (15 + 6)
sumRange 7 10 21 =-sumRange (7 + 1) 10 (21 + 7)
sumRange 8 10 28 =-sumRange (8 + 1) 10 (28 + 8)
sumRange 9 10 36 =-sumRange (9 + 1) 10 (36 + 9)
sumRange 10 10 45 =-sumRange (10 + 1) 10 (45 + 10)
sumRange 11 10 55 =-11> 10 => 55
55

아마도 for 루프가 이해하기 쉽다고 생각할 것입니다. 논란의 여지가 있고 친숙성 문제 일 수 있지만 비 재귀 루프에는 돌연변이가 필요합니다.

여기에서 불변성의 이점에 대해 완전히 설명하지는 않았지만 프로그래머가 제한을 필요로하는 이유에서 글로벌 가변 상태 섹션을 확인하십시오.

한 가지 확실한 이점은 프로그램의 값에 액세스 할 수있는 경우 읽기 액세스 만 가능하므로 다른 사람이 해당 값을 변경할 수 없다는 것입니다. 당신도 우연한 돌연변이는 없습니다.

또한 프로그램이 멀티 스레드 인 경우 다른 스레드가 깔개를 아래에서 당길 수 없습니다. 이 값은 일정하며 다른 스레드가 변경하려는 경우 이전 스레드에서 새 값을 만듭니다.

90 년대 중반, 크리처 크런치 용 게임 엔진을 작성했으며 가장 큰 버그의 원인은 멀티 스레딩 문제였습니다. 그때 불변성에 대해 알고 있었으면 좋겠다. 그러나 당시에는 게임 성능에서 2 배속 또는 4 배속 CD-ROM 드라이브의 차이점에 대해 더 걱정했습니다.

불변성은 더 간단하고 안전한 코드를 만듭니다.

나의 두뇌!!!!

지금은 충분합니다.

이 기사의 다음 부분에서는 고차 함수, 기능 구성, 카레 등에 대해 이야기하겠습니다.

다음 : 파트 2

이것을 좋아한다면 아래의 을 클릭하면 다른 사람들이 이것을 Medium에서 볼 수 있습니다.

Elm에서 Functional Programming을 사용하여 서로 배우고 웹 응용 프로그램을 개발하도록 돕는 웹 개발자 커뮤니티에 참여하려면 Facebook Group, Elm Programming Learn https://www.facebook.com/groups/learnelm/을 확인하십시오.

내 트위터 : @cscalfani