函數式編程之-Partial application

上一篇關於Currying的介紹,咱們提到F#是如何作Currying變換的:編程

let addWithThreeParameters x y z = x + y + z
let intermediateFn1 = addWithThreeParameters 1

給定一個接受三個參數的函數addWithThreeParameters,咱們經過app

let intermediateFn1 = addWithThreeParameters 1

這樣的方式建立出了一個新的函數intermediateFn1,其函數簽名爲:函數式編程

int -> int -> int

上面是F#用來描述函數簽名的方式,它表示函數接受兩個int類型的參數,返回類型爲int類型。
相似地:函數

(int -> int) -> int

表示參數爲(int -> int)類型的函數,返回類型爲int;而(int -> int)又是一個函數,表示接受一個int類型的參數,返回類型仍然爲int。學習

對於F#而言,函數自己須要接受三個參數,可是咱們調用的時候只給一個參數,從而建立出接受兩個參數的新函數,這種方式被稱做是Partial application。也就是說咱們經過Partial application的方式完成了Currying。
Partial application是經常使用的函數式編程風格,對於初學者而言也很重要。咱們在Currying一節介紹過Curring的目的是爲了生成一個只接受一個參數的新函數,從而能夠將兩個函數粘結在一塊兒。

那麼Partial application就是F#完成Curring的一種手段。設計

還記得C#版本的Currying嗎?咱們是通純手工方式建立了新的Currying函數。code

public Func<int, int> AddWithTwoParameters(int x)
{
    Func<int, int> subFunction = y => x + y;

    return subFunction;
}

在C#裏面函數式無法經過只傳遞一個參數的方式來生成一個新的函數,在C#中聲明一個三個參數的函數,調用的時候只傳入一個參數會編譯出錯,這是衆所周知的。
固然,非要用C#演示Partial application,咱們能夠用一種不天然的方式來作到:blog

public static Func<T2, T3, TR> Apply<T1, T2, T3, TR>
(Func<T1, T2, T3, TR> function, T1 arg1)
{
    return (arg2, arg3) => function(arg1, arg2, arg3);
}

//調用
Func<int, int, int> add = (a, b) => a + b;
var add5 = Apply(add, 5);
var add51 = add5(1);

F#是這樣作的:接口

let add5 = (+) 5    // partial application
let add51= add5 1

若是說Partial application還能夠用C#來勉強實現,後面描述的函數式特性幾乎不會出如今OO風格的代碼裏,因此後面的章節大都會以F#來演示。string

Partial application之因此在函數式編程中佔有很重要的地位,他不單單可以完成Curring變換,還能夠將函數變成一個可重用的組件,考慮下面的代碼:

[1;2;3] |> List.map add51

咱們將add51這個新函數應用到了[1;2;3]這個list中的每一個元素中。
下面的這個例子更復雜一些,用來演示經過Partial application來完成OO範式中的Dependency Injection功能。

  • 建立一個函數(add)用來把兩個數字加起來 ,除此以外,還要將運算過程記錄成log
  • Logging函數是一個接受兩個參數的函數,分別爲string和一個泛類型`a,返回類型爲unit,因此logging函數的簽名爲string->'a->unit
  • Logging函數有多種不一樣類型的實現,例如console logger,file logger
    你能夠認爲logging函數是擁有一個契約(string->'a->unit)的接口,對應的實現能夠有多個
  • 如何把具體的logger 注入到add函數中?
//建立一個調用add函數,而且能夠注入logger實現的函數
let addWithPluggableLogger logger x y =
    let result = x + y
    logger "x+y"  result 
    result 

// 建立一個consoleLogger
let consoleLogger argName argValue = 
    printfn "%s=%A" argName argValue 

//把addWithConsoleLogger作partial應用,把consoleLogger應用在上面,同時建立出一個具備添加log能力
//的add函數,他的函數簽名爲`int -> int -> int`,跟普通的add函數式一致的,
//可是此刻他擁有了添加log的能力: 
let addWithConsoleLogger = addWithPluggableLogger consoleLogger 
addWithConsoleLogger  1 2 
addWithConsoleLogger  42 99

也許你已經看出來函數參數的順序決定可否Partial application, 由於在作Partial應用的時候老是按照參數從左到右的順序執行的,你沒法把一個參數應用在函數的最後一個位置。
下節將會描述如何設計函數讓其可以支持Partial application,以及F#中的管道符|>的用法。想學習函數式編程的同窗能夠持續關注。

相關文章
相關標籤/搜索