JavaScript 함수형 프로그래밍 (1)

黄舟
풀어 주다: 2017-03-06 14:14:21
원래의
1068명이 탐색했습니다.

1. 소개

함수형 프로그래밍의 첫인상은 추상적이고 알 수 없는 코드가 가득한 모호한 학문적 코드일 수 있습니다. 대학의 컴퓨터 교수들만 사용하는 것입니다. 특정 시대에는 그랬을 수도 있지만, 최근에는 기술이 발전하면서 함수형 프로그래밍이 실제 제작에 큰 역할을 하게 되었고, 점점 더 많은 언어에 클로저를 추가하기 시작했습니다. , 익명 함수 및 함수형 프로그래밍의 기타 매우 일반적인 기능은 어느 정도 함수형 프로그래밍이 점차적으로 명령형 프로그래밍을 "동화"하고 있습니다.

JavaScript는 지난 2년 동안 React의 인기와 함께 RxJS, CycleJS, lodashJS, underscoreJS 및 기타 오픈 소스 라이브러리도 인기를 끌었습니다. 기능적 특성을 사용했습니다. 그럼 함수형 프로그래밍에 대한 몇 가지 지식과 개념을 소개하겠습니다.

2. 순수함수

아직도 중학교 때 배운 수학 지식을 기억하신다면 함수 f의 개념은 x, 출력y = f(x). 이것은 가장 간단한 순수 함수입니다. 순수 함수의 정의는 동일한 입력에 대해 관찰 가능한 부작용 없이 항상 동일한 출력을 얻고 외부 환경 상태에 의존하지 않는다는 것입니다.

예를 들어 Javascript에서 배열에 대한 일부 작업은 순수하고 일부는 그렇지 않습니다.

var arr = [1,2,3,4,5];

// Array.slice是纯函数,因为它没有副作用,对于固定的输入,输出总是固定的
// 可以,这很函数式
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]

// Array.splice是不纯的,它有副作用,对于固定的输入,输出不是固定的
// 这不函数式
xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
xs.splice(0,3);
//=> []
로그인 후 복사

함수형 프로그래밍에서 필요한 것은 다음과 같습니다. 호출될 때마다 데이터를 엉망으로 만드는 splice와 같은 함수가 아닌, Slice와 같은 순수 함수입니다.

함수형 프로그래밍에서 불순한 함수를 제외하는 이유는 무엇인가요? 또 다른 예를 살펴보겠습니다.

//不纯的
var min = 18;
var checkage = age => age > min;

//纯的,这很函数式
var checkage = age => age > 18;
로그인 후 복사


불순한 버전에서 checkage 의 동작은 입력 매개변수 age뿐만 아니라 Based 외부 변수 min, 즉 이 함수의 동작은 외부 시스템 환경에 따라 결정되어야 합니다. 대규모 시스템의 경우 외부 상태에 대한 이러한 의존성은 시스템 복잡성을 크게 증가시키는 주된 이유입니다.

순수한 checkage는 함수 내부에 키 번호 18을 하드코딩하고 있는데, 이는 확장성이 좋지 않다는 것을 나중에 curring에서 확인할 수 있습니다. 우아한 기능을 사용하여 이 문제를 해결하는 방법.

순수 함수는 시스템의 복잡성을 효과적으로 줄일 수 있을 뿐만 아니라 캐시 가능성과 같은 많은 훌륭한 기능을 가질 수 있습니다.

import _ from 'lodash';
var sin = _.memorize(x => Math.sin(x));

//第一次计算的时候会稍慢一点
var a = sin(1);

//第二次有了缓存,速度极快
var b = sin(1);
로그인 후 복사

3 함수 커링

정의 함수 카레의 방법은 간단합니다. 인수의 하위 집합을 사용하여 함수를 호출하고 나머지 인수를 처리하는 함수를 반환하도록 합니다.

예를 들어 덧셈 함수 var add = (x, y) => 의 경우 커링은 별로 쓸모가 없습니다.

아직도 위의 확인 기능을 기억하시나요? 다음과 같이 커리할 수 있습니다.

//比较容易读懂的ES5写法
var add = function(x){
    return function(y){
        return x + y
    }
}

//ES6写法,也是比较正统的函数式写法
var add = x => (y => x + y);

//试试看
var add2 = add(2);
var add200 = add(200);

add2(2); // =>4
add200(50); // =>250
로그인 후 복사

사실 커링은 함수를 "미리 로드"하는 방법입니다. 더 적은 수의 매개변수를 전달하면 이러한 매개변수를 이미 기억하는 새로운 함수를 얻을 수 있습니다. 매개변수의 "캐싱"과 함수를 작성하는 매우 효율적인 방법:

var checkage = min => (age => age > min);
var checkage18 = checkage(18);
checkage18(20);
// =>true
로그인 후 복사

4. 함수 조합

순수 함수를 사용하는 방법을 배웠고 이를 카레하는 방법을 통해 이러한 "패키지를 쉽게 작성할 수 있습니다. style" code:

import { curry } from 'lodash';

//首先柯里化两个纯函数
var match = curry((reg, str) => str.match(reg));
var filter = curry((f, arr) => arr.filter(f));

//判断字符串里有没有空格
var haveSpace = match(/\s+/g);

haveSpace("ffffffff");
//=>null

haveSpace("a b");
//=>[" "]

filter(haveSpace, ["abcdefg", "Hello World"]);
//=>["Hello world"]
로그인 후 복사

이것도 기능적 코드이지만 어떤 의미에서는 여전히 "우아하지 않습니다". 함수 중첩 문제를 해결하려면 "함수 합성"을 사용해야 합니다.

h(g(f(x)));
로그인 후 복사

우리가 정의하는 합성은 두 개의 순수 함수를 함께 결합할 수 있는 양면 테이프와 같습니다. 물론 3가지 기능을 겸비한 '삼면테이프'나 '4면테이프'나 'N면테이프'까지 확장할 수도 있다.

이 유연한 조합을 통해 빌딩 블록과 같은 기능적 코드를 결합할 수 있습니다.

//两个函数的组合
var compose = function(f, g) {
    return function(x) {
        return f(g(x));
    };
};

//或者
var compose = (f, g) => (x => f(g(x)));

var add1 = x => x + 1;
var mul5 = x => x * 5;

compose(mul5, add1)(2);
// =>15
로그인 후 복사

5. Point Free

커링 및 함수 구성을 사용하여 Point Free에 대한 기본 지식을 소개합니다. .

조심해 보면 이전 코드에서 객체와 함께 제공되는 일부 메서드를 항상 순수 함수로 변환하는 것을 좋아한다는 것을 알 수 있습니다.

var first = arr => arr[0];
var reverse = arr => arr.reverse();

var last = compose(first, reverse);

last([1,2,3,4,5]);
// =>5
로그인 후 복사


이렇게 접근하는 데에는 이유가 있습니다.

포인트 프리 모드에는 현재 중국어 번역이 없습니다. 관심이 있으시면 여기에서 영어 설명을 읽어보세요:

http://www.php.cn/

중국어 설명은 아마도

var map = (f, arr) => arr.map(f);

var toUpperCase = word => word.toUpperCase();
로그인 후 복사

와 같이 일시적인 중간 변수의 이름을 지정하지 않는 것 같습니다.

//这不Piont free
var f = str => str.toUpperCase().split(' ');
로그인 후 복사

이 함수에서는 str을 중간 변수로 사용하지만 이 중간 변수는 코드를 다음보다 더 길게 만들 뿐만 아니라 그것은 의미가 없습니다. 이 코드를 변형해 보겠습니다.

var toUpperCase = word => word.toUpperCase();
var split = x => (str => str.split(x));

var f = compose(split(' '), toUpperCase);

f("abcd efgh");
// =>["ABCD", "EFGH"]
로그인 후 복사


이 스타일은 불필요한 이름 지정을 줄이고 코드를 단순하고 다양하게 유지하는 데 도움이 됩니다. 물론 일부 함수에서 Point Free 스타일을 작성하려면 코드의 다른 부분은 Point Free 스타일이 낮아야 하며 여기서는 직접 선택해야 합니다.

六、声明式与命令式代码

命令式代码的意思就是,我们通过编写一条又一条指令去让计算机执行一些动作,这其中一般都会涉及到很多繁杂的细节。

而声明式就要优雅很多了,我们通过写表达式的方式来声明我们想干什么,而不是通过一步一步的指示。

//命令式
var CEOs = [];
for(var i = 0; i < companies.length; i++){
    CEOs.push(companies[i].CEO)
}

//声明式
var CEOs = companies.map(c => c.CEO);
로그인 후 복사


命令式的写法要先实例化一个数组,然后再对 companies 数组进行for循环遍历,手动命名、判断、增加计数器,就好像你开了一辆零件全部暴露在外的汽车一样,虽然很机械朋克风,但这并不是优雅的程序员应该做的。

声明式的写法是一个表达式,如何进行计数器迭代,返回的数组如何收集,这些细节都隐藏了起来。它指明的是做什么,而不是怎么做。除了更加清晰和简洁之外,map 函数还可以进一步独立优化,甚至用解释器内置的速度极快的 map 函数,这么一来我们主要的业务代码就无须改动了。

函数式编程的一个明显的好处就是这种声明式的代码,对于无副作用的纯函数,我们完全可以不考虑函数内部是如何实现的,专注于编写业务代码。优化代码时,目光只需要集中在这些稳定坚固的函数内部即可。

相反,不纯的不函数式的代码会产生副作用或者依赖外部系统环境,使用它们的时候总是要考虑这些不干净的副作用。在复杂的系统中,这对于程序员的心智来说是极大的负担。

七、尾声

任何代码都是要有实际用处才有意义,对于JS来说也是如此。然而现实的编程世界显然不如范例中的函数式世界那么美好,实际应用中的JS是要接触到ajax、DOM操作,NodeJS环境中读写文件、网络操作这些对于外部环境强依赖,有明显副作用的“很脏”的工作。

这对于函数式编程来说也是很大的挑战,所以我们也需要更强大的技术去解决这些“脏问题”。我会在下一篇文章中介绍函数式编程的更加高阶一些的知识,例如Functor、Monad等等概念。

以上就是JavaScript函数式编程(一)的内容,更多相关内容请关注PHP中文网(www.php.cn)!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿