首頁 web前端 js教程 函數式程式設計的介紹與歸納總結(附程式碼)

函數式程式設計的介紹與歸納總結(附程式碼)

Sep 11, 2018 pm 05:01 PM
函數式程式設計

最近在看函數式程式設計入門經典,自己總結歸納了一下,有興趣或是有需要的可以看一下。

1.、什麼是函數式程式設計

函數式程式設計主要是基於數學函數和它的思想,那麼我們先複習一下數學中的函數即
y = f(x)
即函數f(x) 以x 為參數,以y 為結果,x 和y 可以是任意的數字,其中包含了幾個關鍵點:
1、函數必須總是接收一個參數
2、函數必須總是傳回一個值
3、函數應該根據接收到的參數(例如x),而不是外部環境運行
4、對於給定的x,只會輸出唯一的y
讓我們從一個例子中來理解

// 一个计税函数
var percentValue = 5;
var calculateTax = (value) => {return value/100 * (100 + percentValue)};
登入後複製

用數學中的函數來分析一下這個函數,首先看第三條,函數應該根據接收到的參數,而不是外部環境運作。這裡的函數 calculateTax 依賴外部的 percentValue,因此,函數在數學的意義上就不能稱為函數,那麼我們可以怎麼將它改造成數學意義上的函數呢?很簡單

var calculateTax = (value,percentValue) => {return value/100 * (100 percentValue)};

#現在這個函數就可以被稱為一個真正的函數了。
現在讓我們用簡單的技術術語定義函數式程式設計:函數式程式設計是一種範式,只依賴輸入輸出就可以完成自身邏輯的函數。這保證了當函數多次呼叫時依然可以傳回相同的結果。函數不會改變任何外部環境的變數。

2 、引用透明性

根據函數的定義,我們可以得出結論:所有函數對於相同的輸入輸出都會傳回相同的值。函數的這個屬性被稱為引用透明性
來舉個例子

var identity = (i) => { return i };
登入後複製

我們定義了一個簡單的函數,在函數的內部不依賴全域變量,它會簡單的回傳輸入。現在假設它被應用於其他函數呼叫之間,如

sum(4,5) + identity(1);
登入後複製

根據引用透明性的定義,我們可以把它轉換為

sum(4,5) + 1;
登入後複製

該過程被稱為替換模型,因此你可以直接替換函數的結果(主要是因為函數的邏輯不依賴與其他全域變數)。
由於函數對於給定的輸入結果返回相同的值,實際上我們就可以快取它了,例如我們有一個函數 “factorial”來計算階乘。它接收一個參數以計算其階乘,例如 5 的階乘是 120。當使用者第二次輸入5 的階乘時,由於引用透明性(對於相同輸入返回相同結果)所以我們知道結果是120,但是機器並不知道,我們需要讓機器這個結果緩存下來以便以後調用直接返回結果,而不必再計算一遍。由此可看出引用透明性和可緩存程式碼在函數式程式設計中的重要性。

3、函數式、宣告式與抽象

函數式程式設計主張聲明式程式設計與寫抽象的程式碼
什麼是聲明式程式設計

#假設我們要列印出一個陣列中的所有元素,我們可以採用以下方法

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

在這段程式碼中,我們精確的告訴了程式碼該如何做。如:取得數組長度,循環數組,用索引取得每個元素。這就是命令式程式設計。命令式程式設計主張告訴編譯器怎麼做。

來看另一種方式

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

上面這段程式碼我們移除了取得數組長度,循環數組,用索引取得數組元素等。我們只關心要做什麼就行(即打印數組元素),獲取數組長度,循環等都由機器幫我們做了,我們只需要關心做什麼,而不是怎麼做,這就是聲明式編程

函數式程式設計主張以抽象的方式創建函數,這些函數能夠在程式碼的其他地方被重複使用。

4、 純函數

什麼是純函數?純函數式對給定的輸入傳回相同輸出的函數,例如

var double = (value) => value * 2;
登入後複製
登入後複製

上面的 double 就是一個純函數,因為對於相同的輸入總是會傳回相同的輸出。純函數遵循引用透明性,因此我們可以直接用 10 取代 double(5)。所以純函數的最了不起的地方是什麼?讓我們來看看


1.4.1 純函數產生可測試的程式碼

不純的函數具有副作用,以下以先前的課稅函數以例進行說明

var percentValue = 5;
var calculateTax = (value) => {return value/100 * (100 + percentValue)};
登入後複製

這個函數不是純函數,主要因為它以來外部環境計算其邏輯,當外部環境改變時,它會影響結果。因此,純函數的主要特徵是不依賴任何外部變量,也不應該改變任何外部變量。如果改變了外部變數可能會引起其他函數的行為的改變,即產生副作用,這會使系統的行為變得難以預測。


1.4.2 合理的程式碼

我們應該透過函數的名字來推理出函數的作用,例如 double 函數###
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

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

掌握Go語言的函數式程式設計和Lambda表達式 掌握Go語言的函數式程式設計和Lambda表達式 Nov 30, 2023 am 10:46 AM

在當代的程式設計世界中,函數式程式設計(FunctionalProgramming,簡稱FP)已經逐漸成為一種流行的程式設計範式。它強調以函數為基本構建塊來構建程序,將計算過程看作是函數之間的不斷傳遞和轉換。近年來,Go語言(又稱Golang)因其簡潔、高效、並發安全等特點,逐漸被廣泛應用於各個領域。雖然Go語言本身並不是純粹的函數式程式語言,但它提供了足夠的功

C++中的函式程式設計技巧 C++中的函式程式設計技巧 Aug 22, 2023 am 10:36 AM

C++語言中有許多優秀的程式設計技巧,其中函數式程式設計是一種非常實用的技術。函數式程式設計強調函數的重複使用性和靈活性,可以讓程式碼更加清晰和可維護。在本文中,我們將介紹C++中的函數式程式設計技巧。一、函數物件函數物件是一個可呼叫的對象,它可以被視為一個函數。 C++中的函式物件可以是類別的物件或是函數指標。函數物件可以用於STL演算法中,也可以作為其他函數的參數。下面是一個簡單

用 C++ lambda 表達式實作函數式程式設計有什麼好處? 用 C++ lambda 表達式實作函數式程式設計有什麼好處? Apr 17, 2024 am 10:18 AM

C++lambda表達式為函數式程式設計帶來了優勢,包括:簡潔性:匿名內嵌函數,提升程式碼可讀性。程式碼重用:可傳遞或儲存lambda表達式,方便重複使用程式碼。封裝:提供封裝程式碼段的方法,無需建立單獨函數。實戰案例:過濾列表中的奇數。計算列表中元素的總和。 lambda表達式實現了函數式程式設計的簡潔性、可重複使用性和封裝性。

如何利用懶惰求值優化Golang函數式程式? 如何利用懶惰求值優化Golang函數式程式? Apr 16, 2024 am 09:33 AM

透過使用惰性資料結構,可以在Go語言中實現惰惰求值:建立一個包裝器類型,封裝實際值,僅在需要時才計算。在函數式程式中最佳化斐波那契數列的計算,延後中間值的計算,直到實際需要。這可以消除不必要的開銷,提高函數式程式的效能。

C++中的函數式程式設計面試常見問題 C++中的函數式程式設計面試常見問題 Aug 22, 2023 pm 05:28 PM

隨著C++在電腦領域的廣泛應用和對程式設計範式的不斷探索,函數式程式設計也成為了一個備受矚目的話題。在C++中,函數式程式設計有許多特殊的概念和語法,因此在面試中也常涉及相關問題。本文就C++中的函數式程式設計面試常見問題進行了總結與解答。一、函數式程式設計的優缺點面試官可能會問到你對函數式程式設計的優缺點的理解。函數式程式設計具有以下優點:可讀性強。函數式程式設計只關注函數的輸

golang函數式程式設計的常見錯誤與陷阱 golang函數式程式設計的常見錯誤與陷阱 Apr 30, 2024 pm 12:36 PM

在Go中使用函數式程式設計時需要注意五個常見錯誤和陷阱:避免引用意外修改,確保傳回新建立的變數。解決並發性問題,使用同步機製或避免捕獲外部可變狀態。謹慎使用偏函數化,以提高程式碼可讀性和可維護性。始終處理函數中的錯誤,確保應用程式的健全性。考慮效能影響,使用內聯函數、扁平化資料結構和操作批次來最佳化程式碼。

Python Lambda表達式:揭秘匿名函數的強大奧秘 Python Lambda表達式:揭秘匿名函數的強大奧秘 Feb 24, 2024 am 09:01 AM

python中的Lambda表達式是匿名函數的另一種語法形式。它是一個小型匿名函數,可以在程式中任何地方定義。 Lambda表達式由一個參數列表和一個表達式組成,表達式可以是任何有效的Python表達式。 Lambda表達式的語法如下:lambdaargument_list:expression例如,下面的Lambda表達式傳回兩個數字的和:lambdax,y:x+y這個Lambda表達式可以傳遞給其他函數,例如map()函數:numbers=[ 1,2,3,4,5]result=map(lambda

Python Lambda表達式:縮寫,簡潔,強大 Python Lambda表達式:縮寫,簡潔,強大 Feb 19, 2024 pm 08:10 PM

pythonLambda表達式是一個強大且靈活的工具,可用於建立簡潔、可讀且易於使用的程式碼。它們非常適合快速建立匿名函數,這些函數可以作為參數傳遞給其他函數或儲存在變數中。 Lambda表達式的基本語法如下:lambdaarguments:expression例如,以下Lambda表達式將兩個數字相加:lambdax,y:x+y這個Lambda表達式可以傳遞給另一個函數作為參數,如下所示:defsum( x,y):returnx+yresult=sum(lambdax,y:x+y,1,2)在這個例子

See all articles