>  기사  >  웹 프론트엔드  >  함수형 프로그래밍 소개 및 요약(코드 포함)

함수형 프로그래밍 소개 및 요약(코드 포함)

yulia
yulia원래의
2018-09-11 17:01:311750검색

최근에 함수형 프로그래밍의 고전 입문을 읽고 있는데, 관심이 있거나 필요하신 분은 한 번 읽어보셔도 좋습니다.

1. 함수형 프로그래밍이란

함수형 프로그래밍은 주로 수학적 함수와 그 개념을 기반으로 하기 때문에 먼저 수학에서의 함수, 즉
y = f(x)
, 즉 함수 f를 살펴보겠습니다. (x)는 x를 매개변수로 사용하고 y는 임의의 숫자일 수 있습니다. 여기에는 다음과 같은 몇 가지 핵심 사항이 포함됩니다.
1. 함수는 항상 값을 반환해야 합니다. 3. 함수는 수신된 매개변수에 따라 실행되어야 합니다(예: 이 함수를 분석하려면 먼저 세 번째 항목을 살펴보십시오. 함수는 외부 환경이 아닌 수신된 매개변수에 따라 실행되어야 합니다. 여기서 계산Tax 함수는 외부 백분율값에 의존합니다. 따라서 이 함수는 수학적 의미에서 함수라고 부를 수 없습니다. 그러면 어떻게 수학적 의미에서 함수로 변환할 수 있습니까? 매우 간단합니다

varcalculateTax = (value,percentValue) =>{return value/100 * (100 + ratesValue)};

이제 이 함수를 실제 함수라고 부를 수 있습니다.

이제 함수형 프로그래밍을 간단한 기술 용어로 정의해 보겠습니다. 함수형 프로그래밍은 함수가 입력 및 출력에만 의존하여 자체 논리를 완성하는 패러다임입니다. 이렇게 하면 함수가 여러 번 호출될 때 동일한 결과를 반환합니다. 이 함수는 외부 환경 변수를 변경하지 않습니다.

2. 참조 투명성


함수 정의에 따르면 모든 함수는 동일한 입력 및 출력에 대해 동일한 값을 반환한다고 결론을 내릴 수 있습니다. 함수의 이러한 속성을 참조 투명성이라고 합니다

예를 들어

// 一个计税函数
var percentValue = 5;
var calculateTax = (value) => {return value/100 * (100 + percentValue)};
함수 내부에서 전역 변수에 의존하지 않고 단순히 입력을 반환하는 간단한 함수를 정의합니다. 이제
var identity = (i) => { return i };

와 같은 다른 함수 호출 사이에 적용된다고 가정해 보겠습니다. 참조 투명성의 정의에 따라

sum(4,5) + identity(1);

로 변환할 수 있습니다. 이 프로세스를 대체 모델이라고 부르므로 함수의 결과를 직접 대체할 수 있습니다(주로 이 함수의 논리는 다른 전역 변수에 의존하지 않습니다.

함수는 주어진 입력 결과에 대해 동일한 값을 반환하므로 실제로 이를 캐시할 수 있습니다. 예를 들어 계승을 계산하는 "계수" 함수가 있습니다. 계승을 계산하려면 인수가 필요합니다. 예를 들어 5의 계승은 120입니다. 사용자가 두 번째로 5의 계승값을 입력하면 참조 투명성(동일한 입력에 대해 동일한 결과가 반환됨)으로 인해 결과가 120이라는 것을 알지만 기계는 이를 알 수 없도록 해야 합니다. 향후 호출이 결과를 다시 계산할 필요 없이 직접 반환할 수 있도록 머신이 결과를 캐시합니다. 이는 함수형 프로그래밍에서 참조 투명성과 캐시 가능한 코드의 중요성을 보여줍니다.

3. 함수형, 선언형 및 추상형


함수형 프로그래밍은 선언형 프로그래밍과 추상 코드 작성을 옹호합니다.

선언형 프로그래밍이란 무엇입니까? 배열의 모든 요소를 ​​인쇄한다고 가정하면 다음 방법을 사용할 수 있습니다

sum(4,5) + 1;
이 코드에서는 코드에 수행할 작업을 정확하게 알려줍니다. 예: 배열 길이 가져오기, 배열 반복, 인덱스별로 각 요소 가져오기. 이것은 명령형 프로그래밍입니다. 명령형 프로그래밍은 컴파일러에게 무엇을 해야 할지 알려주는 것을 옹호합니다. 다른 방법을 살펴보겠습니다

var array = [1,2,3];
for(let i = 0; i < array.length; i++){
    console.log(array[i])
}

위 코드에서는 배열 길이 가져오기, 배열 반복, 인덱스를 사용하여 배열 요소 가져오기 등을 제거했습니다. 우리는 무엇을 해야 할지(예: 배열 요소 인쇄), 배열 길이 가져오기, 루핑 등 모든 작업을 기계에서 수행하지만, 수행 방법에만 관심을 두면 됩니다. 선언적 프로그래밍
함수형 프로그래밍 제안 코드의 다른 곳에서 재사용할 수 있는 추상적인 방식으로 함수를 만듭니다.

4. 순수함수

순수함수란 무엇인가요? 순수 함수 함수는 주어진 입력에 대해 동일한 출력을 반환합니다. 예를 들어

var arr = [1,2,3];
arr.forEach((ele) => { console.log(ele) })

위의 double은 동일한 입력에 대해 항상 동일한 출력을 반환하기 때문에 순수 함수입니다. 순수 함수는 참조 투명성을 따르므로 간단히 double(5)를 10으로 바꿀 수 있습니다. 그렇다면 순수 함수의 멋진 점은 무엇일까요? 살펴보겠습니다

1.4.1 순수 함수는 테스트 가능한 코드를 생성합니다불순한 함수에는 부작용이 있습니다. 이전 세금 계산 함수를 예로 들어 보겠습니다.

var double = (value) => value * 2;

이 함수는 주로 외부 환경에 따라 달라지기 때문에 순수 함수가 아닙니다. 그 논리를 계산하고 외부 환경이 바뀌면 결과에 영향을 미칩니다. 따라서 순수 함수의 주요 특징은 외부 변수에 의존하지 않으며 외부 변수를 변경해서는 안 된다는 것입니다. 외부 변수를 변경하면 다른 기능의 동작이 변경될 수 있습니다. 즉, 시스템 동작을 예측할 수 없게 만드는 부작용이 발생할 수 있습니다.

1.4.2 합리적인 코드

이중함수 등 이름을 통해 함수의 기능을 유추해야 합니다

var double = (value) => value * 2;

我们可以通过函数名轻易的推出这个函数会把给定的数值加倍,因此根据引用透明性,我们可以直接把 double(5) 替换成 10。还有一个例子,Math.max(3,4,5,6) 结果是什么?虽然我们只看到了函数的名字,但是我们很容易看出结果,我们看到实现了吗?并没有,为什么,就是因为 Math.max 是纯函数啊!!!

5、 并发代码

纯函数允许我们并发的执行代码,因为纯函数不会改变它的环境,这意味着我们根本不需要担心同步问题。当然,js 是单线程的,但是如果项目中使用了 webworker 来并发执行任务,该怎么办?或者有一段 Node 环境中的服务端代码需要并发的执行函数,又该怎么办呢?

// 非纯函数代码
let global = &#39;something&#39;
let function1 = (input) => {
    // 处理 input
    // 改变 global
    global = "somethingElse"
}
let function2 = () => {
    if(global === "something"){
        // 业务逻辑
    }
}

如果我们需要并发的执行 function1 和 function2,假设 function1 在 function2 之前执行,就会改变 function2 的执行结果,所以并发执行这些代码就会造成不良的影响,现在把这些函数改为纯函数。

let function1 = (input,global) => {
    // 处理 input
    // 改变 global
    global = "somethingElse"
}
let function2 = (global) => {
   if(global === "something"){
        // 业务逻辑
    }
}

此处我们把 global 作为两个函数的参数,让它们变成纯函数,这样并发执行的时候就不会有问题了。

6、可缓存

既然纯函数对于给定的输入总能返回相同的输出,那么我们就能缓存函数的输出,例如

var doubleCache = (value) => {
    const cache = {};
    return function(value){
        if(!cache[value]){
            cache[value] = value * 2
            console.log(&#39;first time&#39;)
        }
        return cache[value];
    }
}
var double = doubleCache();
double(2) // first time,4
double(2) // 4
// 或者直接使用立即执行函数
var double = ((value) => {
    const cache = {};
    return function(value){
        if(!cache[value]){
            cache[value] = value * 2
            console.log(&#39;first time&#39;)
        }
        return cache[value];
    }
})()
double(2) // first time,4
double(2) // 4

这个函数中,假设我们第一次输入 5,cache 中并没有,于是执行代码,由于闭包的存在,cache[5] = 10,第二次我们调用的时候,cache[5] 存在,所以直接 return 10,看到了吗?这就是纯函数的魅力!!!别忘记这是因为纯函数的引用透明性。

7、 管道与组合

纯函数应该被设计为一次只做一件事,并且根据函数名就知道它所做的事情。
比如 linux 系统下有很多日常任务的命令,如 cat 用于打印文件内容,grep 用于搜索文件,wc 用于计算行数,这些命令一次只解决一个问题,但是我们可以用管道或组合来完成复杂的任务。假设我们需要在一个文件中找到一个特定的名称并统计它的出现次数,在命令行要输入如下指令
cat jsBook | grep -i “composing” | wc
上面的命令通过组合多个函数解决了我们的问题。组合不是 linux 命令独有的,它们是函数式编程范式的核心。
我们把它们称为函数式组合。来看一个 compose 函数的例子

var add1 = (value) =>{ return value+1 };
var double = (value) => {return value*2 };
var compose = (a,b) => {
    return (c) => {
       return a(b(c));
    }
}
var doubleAndAdd1 = compose(add1,double);
doubleAndAdd1(5) // 打印 5 * 2 + 1 = 11

compose 函数返回一个函数,将 b 的结果作为 a 的参数,这里就是将 double 的结果作为 add1 的参数,来实现了函数的组合。

8、 纯函数是数学函数

还记得我们之前的缓存函数吗,假设我们多次调用 double 对象,那么 cache 中就会变成这样

{
    1: 2,
    2: 4,
    3: 6,
    4: 8,
    5: 10
}

假设我们设置 double 的输入范围限制为 1 - 5,而且我们已经为这个范围建立的 cache 对象,因此只要参照 cache 就能根据指定输入返回指定输出。也就是一一对应的关系。
那么数学函数的定义是什么呢?
在数学中,函数是一种输入集合和可允许的输出集合之间的关系,具有如下属性:每个输入都精确地关联一个输出。函数的输入称为参数,输出称为值。对于一个给定的函数,所有被允许的输入集合称为该函数的定义域,而被允许的输出集合称为值域。

上面的定义和纯函数完全一致,例如在 double 中,你能找到定义域和值域吗?当然可以!通过这个例子,可以很容易看到数学函数的思想被借鉴到函数式范式的世界

9、 我们要做什么?

我们将通过学习,构建出一个 ES6-Functional 的函数式库,通过构建的过程,我们将理解如何使用 JavaScript 函数,以及如何在日常工作中应用函数式编程。

10、小结

这一节我们只是简单的介绍了函数式编程的概念,以及什么是纯函数,其中最重要的就是引用透明性。然后研究了几个短小的例子,通过例子来加深对函数式编程的理解。接下来我们将一步一步深入了解函数式编程。

위 내용은 함수형 프로그래밍 소개 및 요약(코드 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.