拿 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 等等。

相关文章
相关标签/搜索