1. 程式人生 > 實用技巧 >最小棧實現

最小棧實現

1. 什麼是函數語言程式設計

  • 與面向物件程式設計,程序式程式設計 並列的程式設計正規化(程式設計式思想)
  • 核心思想:把運算過程抽象成函式,在程式設計的過程中面向函式進行程式設計
  • 最主要特徵:函式是第一等公民
  • 只有純的,沒有副作用的函式

 

2. 函式是一等公民

  • 函式可以儲存在變數中
  • 函式可作為返回值
  • 函式可作為引數

let arr = [1, 2, 3, 4]

const map = (array, fn) => {
  let results = []
  for (let value of array) {
    results.push(fn(value))
  }

  return results
}

arr = map(arr, v => v * v)

console.log(arr)

  

3. 高階函式-純函式

a.概念:

  • 純函式對於相同的輸入永遠得到相同的輸出
  • 沒有任何的副作用
  • 純函式中的函式指的是數學中的函式

b.好處:

  • 可快取
  • 可測試函式
  • 可並行處理
  1. 在多執行緒環境下並行操作共享的記憶體資料很有可能會出現意外情況
  2. 純函式不需要訪問共享的資料,所以在並行環境下可以任意執行純函式(web worker)
  • 副作用:
  1. 副作用讓函式變得不純,如果函式依賴於外部的狀態就無法保證輸出相同,就會帶來副作用
  2. 配置檔案,資料庫,獲取使用者輸入,等所有的外部互動都會產生副作用

function getSum(n1, n2) {
  return n1 + n2
}

console.log(getSum(1, 2))
console.log(getSum(1, 2))
console.log(getSum(1, 2))

  

4. 高階函式-柯里化Curry

  • 柯里化Curry也是高階函式
  • 柯里化Curry內部使用閉包,對函式的引數做了 “快取”
  • 可以把多元函式轉換成一元函式,可以組合使用函式產生強大的功能
  • 函式的柯里化可以讓我們給一個函式傳遞較少的引數得到一個已經記住了某些固定引數的新函式,接收剩餘的引數,返回結果
  • 讓函式變得更靈活,讓函式的顆粒更小

const compose = function (f, g) {
  return function (x) {
    return f(g(x));
  };
}

compose(f, compose(g, h))
// 等同於
compose(compose(f, g), h)
// 等同於
compose(f, g, h)

  

5. 高階函式-函式組合

  • 函式可以看做一個處理資料的管道,管道中輸入引數x,在管道中對資料處理後得到結果y
  • 通過函式組合可以把多個醫院函式組合成一個功能更強大的函式
  • 函式組合需要滿足結合律
  • 函式組合預設的執行順序是從右向左

function compose(t, f, g) {
  return function (value) {
    return t(f(g(value)))
  }
}

function reverse(array) {
  return array.reverse()
}

function first(array) {
  return array[0]
}

function toUpper(array) {
  return array.toUpperCase()
}

const last = compose(toUpper, first, reverse)

console.log(last(['one', 'two', 'there']))

  

6. 高階函式-管道

8. 函子

a. 概念:

  1. 容器:包含值和值的變形關係(這個變形關係就是函式)
  2. 函數語言程式設計的運算不直接操作值,而是由函子完成
  3. 函子就是一個實現了map契約的物件
  4. 我們可以把函子想象成一個盒子,這個盒子裡封裝一個值
  5. 想要處理盒子中的值,我們需要給盒子的map方法傳遞一個處理的函式(純函式),由這個函式對值進行處理
  6. 最終map方法返回一個包含新值的盒子(函子)

  b: 作用:

  • 處理純函式的副作用
  • 異常處理
  • 非同步操作

9. 函子基本使用-Functor

class Container {
  static of(value) {
    return new Container(value)
  }

  constructor(value) {
    this._value = value
  }

  map(fn) {
    return Container.of(fn(this._value))
  }
}

let r = Container.of(5).map(x => x + 2).map(x => x * x)

console.log(r)

  

10. 函子空值處理-MayBe

a. 作用:

  • 處理外部的空值情況,防止空值的異常
class MayBe {
  static of(value) {
    return new MayBe(value)
  }

  constructor(value) {
    this._value = value
  }

  map(fn) {
    return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
  }

  isNothing() {
    return this._value === null || this._value === undefined
  }
}

// let r = MayBe.of('Hello world').map(x => x.toUpperCase())
 let r = MayBe.of(null).map(x => x.toUpperCase())
 console.log(r)

  

11. 函子異常處理-Either

  • Either 判斷選擇兩者中的任何一個
class Left {
  static of(value) {
    return new Left(value)
  }

  constructor(value) {
    this._value = value
  }

  map(fn) {
    return this
  }
}

class Right {
  static of(value) {
    return new Right(value)
  }

  constructor(value) {
    this._value = value
  }

  map(fn) {
    return Right.of(fn(this._value))
  }
}


function parseJson(str) {
  try {
    return Right.of(JSON.parse(str))
  } catch (e) {
    return Left.of({ error: e.message })
  }
}

console.log(parseJson('{name: zhangsan}'))

  

12. 函子延遲-IO

  • IO函子內部封裝的值是一個函式,這裡把函式作為值來處理
  • IO函子可以把不純的操作儲存到這個函式中,延遲執行這個不純的操作(惰性執行),包裝當前的操作
  • 把不純的操作交給呼叫者來處理

var fs = require('fs');

var readFile = function(filename) {
  return new IO(function() {
    return fs.readFileSync(filename, 'utf-8');
  });
};

var print = function(x) {
  return new IO(function() {
    console.log(x);
    return x;
  });
}

readFile('./user.txt')
.flatMap(print)

  

13. 函子非同步處理-Task

// task
const fs = require('fs')
const { task } = require('folktale/concurrency/task')
const { split, find } = require('lodash/fp')

function readFile(filename) {
  return task(resolver => {
    fs.readFile(filename, 'utf-8', (err, data) => {
      if (err) resolver.reject()

      resolver.resolve(data)
    })
  })
}

readFile('package.json')
  .map(split('\n'))
  .map(find(x => x.includes('version')))
  .run()
  .listen({
    onRejected: err => {
      console.log(err)
    },
    onResolved: value => {
      console.log(value)
    }
  })
console.log()

  

14. 函子巢狀-Monad

  • Monad內部封裝的值是一個函式(這個函式返回函子)
  • 目的是通過Join方法避免函子巢狀
const fp = require('lodash/fp')
const fs = require('fs')

class IO {
  static of(value) {
    return new IO(function () {
      return value
    })
  }

  constructor(fn) {
    this._value = fn
  }

  map(fn) {
    return new IO(fp.flowRight(fn, this._value))
  }
}

let readFile = function (filename) {
  return new IO(function () {
    return fs.readFileSync(filename, 'utf-8')
  })
}

let print = function (x) {
  return new IO(function (params) {
    console.log(x)
    return x
  })
}

let cat = fp.flowRight(print, readFile)
let r = cat('package.json')
console.log(r._value()._value())

  

15. 函數語言程式設計的作用

  • 讓程式碼可以重用

16. 函數語言程式設計的庫

  • Lodash---
  • Folktale---函子非同步處理庫

17. 問題總結:

  • 函子是隻有類可以實現嘛?
  • static 的作用?