函數式編程—4—純函數(函子)

Point Free (編程風格)

將數據處理的過程定義成與數據無關的合成運算,不需要代表數據的那個參數,主要吧簡單的運算步驟合成到一起,在使用這種模式之前,我們需要定義一些輔助的基本運算函數

  • 不需要指明處理的數據
  • 只需要合成運算過程
  • 需要定義一些輔助的基本運算函數
const fp = require('loadsh/fp')
const f = fp.flowRight(fp.join('-'),fp.map(_.toLower),fp.split(' ')) 

簡單演示

HELLO WORD ==> hello_word

普通方式

function func1(str){
    return str.toLowerCase().replace(/\s+/g,'_')
}
console.log(func1('HELLO WORD')) // hello_word

Point Free

const fp = require('loadsh/fp')
const func2 = fp.flowRight(fp.replace(/\s+/g,'_'),fp.toLower)
console.log(func2('HELLO WORD')) // hello_word

案例演示

word wild web ==> W.W.W

const fp = require('loadsh/fp')
const firstLetterToUpper = fp.flowRight(fp.join('.'),fp.map(fp.flowRight(fp.toUpper,fp.first)),fp.split(' '))
console.log(firstLetterToUpper('word wild web')) // W.W.W

Functor (函子)

爲什麼需要學習函子

把副作用控制在可控的範圍內以及異常處理、異步操作等

什麼是Functor

  • 容器 包含值和值變形的關係
  • 函子 一個特殊的容器,通過普通對象直線,該對象具有map方法,map方法可以運行一個函數對值進行處理(變形關係)
  • 實現鏈式調用
class Container {
    constructor(value){
        this._value = value
    }
    map(fn){
        return Container.of(fn(this._value))
    }
    static of(value){
        return new Container(value)
    }
}

let r = Container.of(1).map(x=>x+1).map(x=>x*x)
console.log(r) // 4

函子總結

  • 函數式編程的運算不字節操作值,而是使用函完成
  • 函子就是實現了map契約的對象
  • 我們可以把函子想象成一個盒子,這個盒子裏封裝了一個值
  • 想要處理盒子中的值,我們需要給盒子的map方法傳遞一個處理值的函數(純函數),這個函數來對值進行處理
  • 最終map方法返回了一個包含新值的盒子(函子)
    關於副作用異常的出現方式
class Container {
    constructor(value){
        this._value = value
    }
    map(fn){
        return Container.of(fn(this._value))
    }
    static of(value){
        return new Container(value)
    }
}

Container.of(null).map(x=>x.toUpperCase()) //報錯
// 輸入爲null 會觸發副總用

MayBe (函子)

  • 對錯誤進行相應的處理
  • 可以對外部空值的情況做處理(空值副作用在允許的範圍)
class Mybe {
    static of (value) {
        return new Mybe(value)
    }
    constructor(value) {
        this._value = value

    }
    map(fn) {
        return this.isNothing() ? Mybe.of(null) : Mybe.of(fn(this._value))
    }
    isNothing() {
        return this, this._value === null || this._value === undefined
    }

}
const r1 = Mybe.of('hello word').map(x=>x.toUpperCase()) // null
const r2 = Mybe.of(null).map(x=>x.toUpperCase()) // null
console.log(r1) // Mybe { _value: 'HELLO WORD' }
console.log(r2) // Mybe { _value: null } 

MyBe 函子問題 多次出現null後無法確定 null出現的位置

class Mybe {
    static of (value) {
        return new Mybe(value)
    }
    constructor(value) {
        this._value = value

    }
    map(fn) {
        return this.isNothing() ? Mybe.of(null) : Mybe.of(fn(this._value))
    }
    isNothing() {
        return this, this._value === null || this._value === undefined
    }
}
const r1 = Mybe.of('hello word')
            .map(x=>x.toUpperCase())
            .map(x=>null)
            .map(x=>x.split(' '))
            .map(x=>null)
            .map(x=>x.split(' '))
console.log(r1) // Mybe { _value: null } 

EitHer (函子)

  • EitHer兩者中的任何一個,類似於if…else …的邏輯
  • 異常會讓函數變得不純,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 parseJosn(str){
    try {
        return Right.of(JSON.parse(str))
    } catch (e) {
        return Left.of({err:e.message})
    }
}

const r = parseJosn('{ name zs }').map(x=>x.name.toUpperCase())
console.log(r) // Left { _value: { err: 'Unexpected token n in JSON at position 2' } }

const r2 = parseJosn('{ "name":"zs"}').map(x=>x.name.toUpperCase())
console.log(r2) // Right { _value: 'ZS' }

IO (函子)

  • _value是一個函數,這裏是吧函數作爲值來處理
  • 可以吧不純的動作存儲到_value中,延遲執行這個不純的操作(惰性執行),包裝當前的操作純函數
  • 把不純的操作交給調用者來處理
const fp = require('loadsh/fp')
class IO {
    static of (value){
        return new IO(()=>{
            return value
        })
    }
    constructor(fn){
        this._value=fn
    }
    map(fn){
        return new IO(fp.flowRight(fn,this._value))
    }
    value(){
        return this._value()
    }
}
// 當前在node環境中  調用
const r = IO.of(process).map(process=>process.execPath)
console.log(r) // IO { _value: [Function (anonymous)] }
console.log(r.value()) // /Users/zhaozhongyang/.nvm/versions/node/v13.13.0/bin/node

folktale

一個標準的函數式編程庫

  • 與loadsh、ramda不同的是,他沒有提供很多功能函數
  • 只提供了一些函數式處理的操作,例如 compose,curry等,一些函子 Task、Either、MayBe等

基本使用

const {
    compose,
    curry
} = require('folktale/core/lambda')

const _ = require("loadsh")

// curry 需要傳遞兩個參數 第一個參數是傳入函數的所需參數數量
let f = curry(2,(x,y)=>x+y)
console.log(f(1)(2)) // 3

// compose函數與 loadsh中的 flowRight 基本一致
let f2 = compose( _.toUpper,_.first)
console.log(f2(['a','b'])) // A

folktale——task(函子) 異步任務

const { task } =require('folktale/concurrency/task')
const fs = require('fs')
const fp = require('loadsh/fp')
// 文件處理使用task 函子
const readFile = (filename)=>task(resolver=>{
    fs.readFile(filename,'utf-8',(err,data)=>{
        if(err){
            resolver.reject(err)
        }
        resolver.resolve(data)
    })
})

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

folktale——Pointed(函子)

  • 實現了of靜態方法的函子
  • of 是爲了避免使用new來創建對象,更深層次的含義是of方法吧值放到了上下文Context(把值放到容器中,使用map方法來處理值)
class Container {
    constructor(value){
        this._value = value
    }
    map(fn){
        return new Container(fn(this._value))
    }
}

folktale——Monad(函子)

  • 實現了of靜態方法的函子
  • of 是爲了避免使用new來創建對象,更深層次的含義是of方法吧值放到了上下文Context(把值放到容器中,使用map方法來處理值)
  • 解決函子嵌套

IO函子的問題

函子嵌套

// 模擬 linux cat 命令
const readFile = (filename)=>new IO(()=>{
    return fs.readFileSync(filename,'utf-8')
})
const print = (x)=>new IO(()=>{
    console.log(x._value())
    return x
})


const cat = fp.flowRight(print,readFile)

const f = cat('package.json')._value()._value()
console.log(f)

實現方式 扁平化處理

const fs = require('fs')
const fp = require('loadsh/fp')
class IO {
    static of (value){
        return new IO(()=>{
            return value
        })
    }
    constructor(fn){
        this._value=fn
    }
    map(fn){
        return new IO(fp.flowRight(fn,this._value))
    }
    join(){
        return this._value()
    }
    flatMap(fn){
        return this.map(fn).join()
    }
}

// 模擬 linux cat 命令
const readFile = (filename)=>new IO(()=>{
    return fs.readFileSync(filename,'utf-8')
})
const print = (x)=>new IO(()=>{
    return x
})

const f = readFile('package.json').map(x=>fp.toUpper(x)).flatMap(print).join()
console.log(f)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章