函數(shù)式編程

什么是函數(shù)式編程

函數(shù)式編程的概念誕生在二十世紀(jì)五十年代,近些年函數(shù)式編程獲得越來越多的關(guān)注,很多語言加入了函數(shù)式編程的支持。比如java 8 加入了lambda表達(dá)式。

函數(shù)式編程是一種編程范式凛膏,也就是如何編寫程序的方法論。通過組合函數(shù)的方式來編寫程序它浅。函數(shù)式編程主張聲明式編程而非命令式編程译柏。

函數(shù)式編程中的函數(shù)這個(gè)術(shù)語是指數(shù)學(xué)中的函數(shù),即自變量的映射y=f(x)姐霍。也就是說函數(shù)的值僅取決于函數(shù)的參數(shù)鄙麦,不依賴其它狀態(tài)典唇。我們要使用數(shù)學(xué)函數(shù)的思想來理解函數(shù)式編程。

函數(shù)式編程的理論基礎(chǔ)是lambda演算胯府,用函數(shù)組合的方式來描述計(jì)算過程介衔,即一個(gè)問題如果能用一套函數(shù)組合的算法來表達(dá)那么這個(gè)問題是可計(jì)算的。

函數(shù)式編程的主要思想是把運(yùn)算過程盡量寫成一系列的函數(shù)調(diào)用骂因。比如:

(a+b)*c-d

函數(shù)式編程要求運(yùn)算過程定義為不同的函數(shù):

subtract(multiply(add(a,b),c),d)

聲明式和命令式

函數(shù)式編程主張聲明式編程和編寫抽象的代碼炎咖。假設(shè)有一個(gè)數(shù)組,你想遍歷它并打印到控制臺寒波。命令式寫法:

//命令寫法
var array = [1, 2, 3];
for (let index = 0; index < array.length; index++) {
  console.log(array[index]);
}

為了解決問題乘盼,我們告訴程序該如何做:獲取數(shù)組長度,循環(huán)數(shù)組俄烁,用索引獲取每個(gè)元素绸栅。命令式編程主張告訴編譯器若何做。
在聲明式編程中页屠,我們要告訴編譯器做什么粹胯,而不是如何做。如何做被抽象到普通函數(shù)中辰企。聲明式寫法:

var array = [1, 2, 3];
// 聲明式寫法
array.forEach((element) => console.log(element));

我們使用了如何做的抽象函數(shù)forEach风纠,如此可以讓開發(fā)者只關(guān)心做什么的部分。把操作抽象為函數(shù)是函數(shù)式編程的核心思想牢贸。我們把循環(huán)的操作抽象為函數(shù)竹观,以便在需要時(shí)可以重用:

const forEach = (array, fn) => {
  for (let i = 0; i < array.length; i++) fn(array[i]);
};

函數(shù)式編程5個(gè)特點(diǎn)

函數(shù)是一等公民

函數(shù)和普通數(shù)據(jù)類型一樣,可以作為參數(shù)傳遞潜索,可以賦值栈幸,可以作為函數(shù)的返回值。

函數(shù)沒有副作用

對同樣的輸入帮辟,總是返回相同的輸出,不能修改外部變量的函數(shù)稱為純函數(shù)玩焰,否則稱該函數(shù)是有副作用的由驹。一個(gè)沒有副作用的函數(shù):

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

一個(gè)有副作用的函數(shù):

let discount = 0.8;
let price = (value) => discount * value;
// price依賴外部變量discount

price函數(shù)依賴外部變量discount,那么:

price(10) === 8

如果discount變化了昔园,那么:

discount = 0.6;
price(10) === 6;

對同樣的輸入輸出不同那么price函數(shù)是有副作用的蔓榄。

純函數(shù)不應(yīng)該修改外部變量,例如:

// 修改外部變量
var global = "value";
var badFunction = (value) => {
  global = "value2";
  return value * 2;
};

badFunction函數(shù)修改了global變量默刚,調(diào)用badFunction函數(shù)則影響了其它函數(shù)的行為甥郑。

引用透明性

對于同樣的輸入都將返回相同的值,函數(shù)的這一屬性被稱為引用透明性荤西。例如:

var double = (value) => value * 2;
// double(2) 可以用 4 替換

利用引用透明性我們可以緩存函數(shù)的值澜搅,比如我們有一個(gè)計(jì)算階乘的函數(shù)factorial伍俘,我們知道5的階乘是120,當(dāng)?shù)诙斡?jì)算5的階乘時(shí)不用重新計(jì)算了勉躺,直接使用緩存的值即可癌瘾。

數(shù)據(jù)是不可變的

在純的函數(shù)式編程語言中,數(shù)據(jù)是不可變的饵溅,沒有變量的概念妨退。所有的數(shù)據(jù)一旦產(chǎn)生就不能改變它的值,如果要改變只能生成新的數(shù)據(jù)蜕企。

只使用表達(dá)式不使用語句

函數(shù)式編程要求只使用表達(dá)式不使用語句咬荷,也就是每一步都是單純的運(yùn)算,并且有返回值轻掩。

函數(shù)式編程舉例

打印數(shù)組中偶數(shù)的值

命令式編程:

// 查找列表中的偶數(shù)
var array = [1, 2, 3, 4, 8];
for (let index = 0; index < array.length; index++) {
  const element = array[index];
  if (element % 2 === 0) {
    console.log(element);
  }
}

函數(shù)式編程要把操作過程抽象到函數(shù)中幸乒,把循環(huán)過程抽象到forEach 函數(shù)中,判斷是否為偶數(shù)if (element % 2 === 0){}抽象到unless函數(shù)中放典,調(diào)用:

const forEach = (array, fn) => {
  for (let index = 0; index < array.length; index++) {
    const element = array[index];
    fn(element);
  }
};

const unless = (num, fn) => {
  if (num % 2 === 0) {
    fn();
  }
};

forEach(array, (element) => {
  unless(element, () => console.log(element));
});

100以內(nèi)的奇數(shù)

// 命令式寫法
for (let index = 0; index < 100; index++) {
  if (index % 2) {
    console.log(index);
  }
}

函數(shù)式寫法:

function times(num, fn) {
  for (let index = 0; index < num; index++) {
    fn(index);
  }
}

function unless(predicate, fn) {
  if (predicate) {
    fn();
  }
}
// 100 以內(nèi)的奇數(shù)
times(100, (index) => {
  unless(index % 2, () => {
    console.log(index);
  });
});

數(shù)組函數(shù)式編程綜合應(yīng)用

統(tǒng)計(jì) books 評價(jià) good 和 excellent 的數(shù)量逝变,數(shù)據(jù)結(jié)構(gòu)如下:

let apressBooks2 = [
  {
    name: "beginners",
    bookDetails: [
      {
        id: 111,
        title: "C# 6.0",
        author: "ANDREW TROELSEN",
        rating: [4.7],
        reviews: [{ good: 4, excellent: 12 }],
      },
      {
        id: 222,
        title: "Efficient Learning Machines",
        author: "Rahul Khanna",
        rating: [4.5],
        reviews: [],
      },
    ],
  },
  {
    name: "pro",
    bookDetails: [
      {
        id: 333,
        title: "Pro AngularJS",
        author: "Adam Freeman",
        rating: [4.0],
        reviews: [],
      },
      {
        id: 444,
        title: "Pro ASP.NET",
        author: "Adam Freeman",
        rating: [4.2],
        reviews: [{ good: 14, excellent: 12 }],
      },
    ],
  },
];

過程式寫法:

function p() {
  const allBooks = apressBooks2.map((e) => e.bookDetails);
  const concatBooks = [];
  allBooks.forEach((e) => concatBooks.push(...e));
  const allPreviews = concatBooks.map((e) => e.reviews);
  const concatPreviews = allPreviews.filter((e) => e.length).map((e) => e[0]);
  return concatPreviews.reduce(
    (acc, e) => [acc[0] + e.good, acc[1] + e.excellent],
    [0, 0]
  );
}

函數(shù)式寫法:

// 省略reduce, concatAll, map等Api
function fn() {
  return reduce(
    concatAll(
      map(concatAll(map(apressBooks2, (e) => e.bookDetails)), (e) => e.reviews)
    ),
    (acc, e) => [acc[0] + e.good, acc[1] + e.excellent],
    [0, 0]
  );
}

函數(shù)式庫

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奋构,隨后出現(xiàn)的幾起案子壳影,更是在濱河造成了極大的恐慌,老刑警劉巖弥臼,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宴咧,死亡現(xiàn)場離奇詭異,居然都是意外死亡径缅,警方通過查閱死者的電腦和手機(jī)掺栅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纳猪,“玉大人氧卧,你說我怎么就攤上這事∈系蹋” “怎么了沙绝?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鼠锈。 經(jīng)常有香客問我闪檬,道長,這世上最難降的妖魔是什么购笆? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任粗悯,我火速辦了婚禮,結(jié)果婚禮上同欠,老公的妹妹穿的比我還像新娘样傍。我一直安慰自己横缔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布铭乾。 她就那樣靜靜地躺著剪廉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪炕檩。 梳的紋絲不亂的頭發(fā)上斗蒋,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機(jī)與錄音笛质,去河邊找鬼泉沾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛妇押,可吹牛的內(nèi)容都是我干的跷究。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼敲霍,長吁一口氣:“原來是場噩夢啊……” “哼俊马!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肩杈,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤柴我,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后扩然,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艘儒,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年夫偶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了界睁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡兵拢,死狀恐怖翻斟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情说铃,我是刑警寧澤杨赤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站截汪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏植捎。R本人自食惡果不足惜衙解,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望焰枢。 院中可真熱鬧蚓峦,春花似錦舌剂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至一汽,卻和暖如春避消,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背召夹。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工岩喷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人监憎。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓纱意,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鲸阔。 傳聞我的和親對象是個(gè)殘疾皇子偷霉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349