拿 C# 搞函數式編程 - 1

最近閒下來了,準備出一個 C# 搞 FP 的合集。本合集全部代碼均以 C# 8 爲示例。app

可能你說,爲何要這麼作呢?回答:爲了好玩。另外,意義黨們請 gun cu ke!函數

 

C# 有委託,並且有 Func<> 和 Action<>,能夠說函數被視爲一等功名,跟 int、bool 等類型並無什麼區別。那麼不少事情就簡單了。this

純函數

什麼是純函數呢?純函數就是 f(x),它們接收參數,獲得結果,而且相同的參數獲得的結果必定是相同的,用映射來講,它是滿射的。另外這個函數不會改變任何的狀態值,它是無反作用的。spa

柯里化

首先,有一個東西讓我以爲不爽,那就是通常來講 C# 裏的函數調用不是柯里化的,這也就意味着我無法一個一個傳參數進去,也無法把傳了一部分參數的調用做爲一個新函數拿去給別的地方用,那要怎麼辦呢?code

本身動手,豐衣足食!blog

一個標準的加法函數能夠這麼寫:string

var function = new Func<int, int, int>
    ((x, y) => x + y);
function(1, 2); // returns 3

若是咱們想以柯里化形式調用的話,理想狀態是這麼個樣子的:產品

function 1 2

可是這個括號咱們是省不了的,因此這樣也是能夠接受的:it

function(1)(2);

咱們看一下這個調用形式,不就是 Func<int, Func<int, int>> 嘛!so easy~io

咱們只須要把 Func<int, int, int> 轉化爲 Func<int, Func<int, int>>:

Func<int, Func<int, int>> Currying(Func<int, int, int> f) 
    => x => y => f(x, y);

這樣寫就 ok 啦。進一步改形成擴展方法:

public static class CurryingExtensions
{
    public static Func<int, Func<int, int>> 
        Currying(this Func<int, int, int> f) 
            => x => y => f(x, y);
}

因而咱們只須要:

var function = new Func<int, int, int>
    ((x, y) => x + y)
    .Currying();
function(1)(2); // returns 3

就能夠採用柯里化形式調用該函數啦。

進一步咱們用泛型改造,讓柯里化適用於任何類型:

public static class CurryingExtensions
{
    public static Func<T1, Func<T2, TOutput>> 
        Currying<T1, T2, TOutput>(this Func<T1, T2, TOuput> f)
            => x => y => f(x, y);
}

若是遇到更多參數,咱們只須要給這個靜態類裏面再加一個擴展方法便可。

那 Action<> 呢?這個東西在我看來徹底就是反作用,具體下方有講,咱們不用他(逃

Unit

什麼是 Unit 呢?Unit 就是任何函數調用後若是沒有結果,就會返回的一個東西。

可能你說,void 不就能夠了?

可是若是一個純函數,它沒有返回值(即 Action<>),意味着這個函數它有輸入沒輸出,那這個函數除了能用來產生反作用以外,就什麼都幹不了了。這不清真!

所以咱們須要一個 Unit 來代替 void,偷個懶,這個 Unit 就用 ulong 來代替吧。

高階函數

什麼叫作高階函數,把函數看成參數傳給另外一個函數,接收這個函數參數的函數就叫作高階函數。

舉個例子:f(g(x)),f 即高階函數。

假設咱們如今要開一個超市,超市有不少的產品,每種產品價格不一樣,不一樣產品可能還有各自的折扣。咱們有不少種快樂水,每種快樂水價格不同,可口快樂水 3.5 塊,百事快樂水 3 塊,麥當勞快樂水 9 塊,快樂水價格計算函數:

var happyWater = new Func<float, int, float>
    ((float price, int number) => number * price)
    .Currying();
// 調用:happyWater(快樂水單價)(快樂水件數);

var cocaHappyWater = happyWater(3.5f);
var pepsiHappyWater = happyWater(3);
var mcdHappyWater = happyWater(9);

超市可能有折扣,A 超市不打折,B 超市打八折,計算價格函數:

var calcPrice = new Func<Func<int, float>, float, int, float>
    ((calc, discount, number) => discount * calc(number))
    .Currying();
// 調用:calcPrice(快樂水價格計算函數)(超市折扣)(快樂水件數);

如今咱們分別在 A 超市買百事快樂水、B 超市買可口快樂水,麥當勞的太貴了咱們不買,價格計算函數爲:

var pepsiPriceCalc = calcPrice(pepsiHappyWater);
var cocaPriceCalc = calcPrice(cocaHappyWater);

var priceCalcA = pepsiPriceCalc(1); // A 超市
var priceCalcB = cocaPriceCalc(0.8f); // B 超市

最後咱們在 A 超市買了 3 瓶百事快樂水,B 超市買了 5 瓶可口快樂水,計算總價:

var priceA = priceCalcA(3);
var priceB = priceCalcB(5);
var total = priceA + priceB;

最後獲得 total = 23 元。

能夠看到這些函數都是可拆卸而且能夠隨意組合的,並且知足 f(g(x)) = g(f(x))。

貼上完整代碼示例:

using System;

namespace ColaMarket
{
    static class CurryingExtensions
    {
        public static Func<T1, Func<T2, TOutput>>
            Currying<T1, T2, TOutput>(this Func<T1, T2, TOutput> f)
                => x => y => f(x, y);

        public static Func<T1, Func<T2, Func<T3, TOutput>>>
            Currying<T1, T2, T3, TOutput>(this Func<T1, T2, T3, TOutput> f)
                => x => y => z => f(x, y, z);
    }

    class Program
    {
        static void Main(string[] args)
        {
            var happyWater = new Func<float, int, float>
                ((float price, int number) => number * price)
                .Currying();

            var cocaHappyWater = happyWater(3.5f);
            var pepsiHappyWater = happyWater(3);
            var mcdHappyWater = happyWater(9);

            var calcPrice = new Func<Func<int, float>, float, int, float>
                ((calc, discount, number) => discount * calc(number))
                .Currying();

            var pepsiPriceCalc = calcPrice(pepsiHappyWater);
            var cocaPriceCalc = calcPrice(cocaHappyWater);

            var priceCalcA = pepsiPriceCalc(1);
            var priceCalcB = cocaPriceCalc(0.8f);

            var priceA = priceCalcA(3);
            var priceB = priceCalcB(5);
            var total = priceA + priceB;

            Console.WriteLine(total);
        }
    }
}

 

下一篇將會講更多的東西,如 Functor、Applicative 和 Monad 等等。

相關文章
相關標籤/搜索