拿 C# 搞函数式编程 - 2
2021-01-25 18:12
标签:需要 运行 one ica list() 建议 参数类型 ons 世界 前一阵子在写 CPU,导致一直没有什么时间去做其他的事情,现在好不容易做完闲下来了,我又可以水文章了哈哈哈哈哈。 有关 FP 的类型部分我打算放到明年再讲,因为现有的 C# 虽然有一个 另外, 这一部分我们介绍一下 本文试图直观地讲,目的是让读者能比较容易的理解,而不是准确知道其概念如何,因此会尽量避免使用一些专用的术语,如范畴学、数学、λ 计算等等里面的东西。感兴趣的话建议参考其他更专业的资料。 Functor 也叫做函子。想象一下这样一件事情: 现在我们有一个纯函数 这个纯函数只干一件事情:判断输入是不是奇数。 那么现在问题来了,如果我们有一个整数列表,要怎么去做上面这件事情呢? 可能会有人说这太简单了,这样就可: 上面这句干了件什么事情呢?其实就是:我们将 现在我们做一次抽象,我们将这个列表想象成一个箱子 它分别接收一个把东西从 你暂且可以简单地认为,判断一个箱子是不是 我们应该都接触过 C# 的 此时我们把这个 用 Haskell 写这个 而之所以这个 由于自带的 对于 有了上面的东西,现在我们说说 你可以非常容易的发现,如果你为 那 那 看起来和 以 然后我们就可以干这件事情了: 我们的这个函数 Monad 继承自 Applicative,并另外包含几个额外的操作: 它用一个装在 为什么说这是充当胶水的作用呢?想象一下如果我们有两个 实现以上操作: 以上方法可以自行柯里化后使用,以及我调换了一些参数顺序便于使用,所以可能和定义有所出入。 想象一下,现在世界上只有一种函数:纯函数。它接收一个参数,并且对于每一个参数值,给出固定的返回值,即 那现在问题来了,如果我需要可空的值 前者可能只需要给函数增加一个参数:是否有值,然而后者呢?牵扯到时间、硬件、环境等等一切和产生随机数种子有关的状态,我们当然可以将所有状态都当作参数传入,然后生成一个随机数,那更复杂的, 这类函数都是与环境和状态密切相关的,状态是可变的,并不能简单的由参数做映射产生固定的结果,即这类函数具有副作用。但是,我们可以将状态和值打包起来装在箱子里,这个箱子即 以随机数 我们现在已经有两个函数, 然后我们有 利用符号表示即为: 这样我们将状态等带有副作用的操作全部隔离在了 Monad 中,我们接触到的东西都是不变的,并且满足 当然这个例子使用 利用符号表示即为: 拿 C# 搞函数式编程 - 2 标签:需要 运行 one ica list() 建议 参数类型 ons 世界 原文地址:https://www.cnblogs.com/hez2010/p/12008677.htmlpattern matching expressions,但是没有 discriminated unions 和 records,只能说是个半残废,要实现 FP 那一套的类型异常的复杂。西卡西,discriminated unions 和 records 这两个东西官方已经定到 C# 9 了,所以等明年 C# 9 发布了之后我再继续说这部分的内容。concepts(type classes)、traits 、intersect & sum types 和高阶类型也可能会随着 C# 9、10 一并到来。因此到时候再讲才会讲得更爽。另外吹一波 traits类型系统,同样是图灵完备的类型系统,在表达力上要比OOP强太多,欢迎大家入坑,比如 Rust 和未来的 C#。Functor、Applicative和 Monad 都是些什么。Functor
IsOddbool IsOdd(int value) => (value & 1) == 1;
var list = new Listint>();
return list.Select(IsOdd).ToList();
IsOdd 函数应用到了列表中的每一个元素上,将产生的新的列表返回。M,那么我们的需要干的事情就是:把一个装着 A 类型东西的箱子变成一个装着 B 类型东西的箱子(A、B类型可相同),即 fmap函数,而做这个变化的方法就是:进入箱子M,把里面的A变成B。A变成B的函数、一个装着A的M,产生一个装着B的M。M Fmap(this M input, Func func);
Functor,就是判断它有没有 fmap这个操作。Maybe
Nullable类型,比如 Nullable,或者写成 int? t,这个t,当里面的值为 null 时,它为 null,否则他为包含的值。Nullable想象成这个箱子 M。那么我们可以这么说,这个M有两种形式,一种是 Just,表示有值,且值在 Just 里面存放;另一种是 Nothing,表示没有值。Nullable类型定义的话,大概长这个样子:data Nullable x = Just x | Nothing
Nullable既可能是 Nothing,又可能是 Just,只是因为 C# 的 BCL 中包含相关的隐式转换而已。Nullable不太好具体讲我们的各种实现,且只接受值类型的数据,因此我们自己实现一个Maybe:public class Maybe
Maybe,我们可以写一下它的 fmap函数:public static Maybe Fmap(this Maybe input, Func func)
=> input switch
{
null => Maybe.Nothing(),
{ HasValue: true } => new Maybe(func(input.Value)),
_ => Maybe.Nothing()
};
Maybeint> t1 = 7;
Maybeint> t2 = Maybeint>.Nothing();
Funcint, bool> func = x => (x & 1) == 1;
t1.Fmap(func); // Just True
t2.Fmap(func); // Nothing
Applicative
Applicative 是干什么的。Maybe实现一个 fmap,那么你可以说 Maybe就是一个 Functor。Applicative 也差不多,首先Applicative是继承自Functor的,所以Applicative本身就具有了 fmap。另外在 Applicative中,我们有两个分别叫做pure和 apply的函数。pure干的事情很简单,就是把东西装到箱子里:M
apply 干了件什么事情呢?想象一下这件事情,此时我们把之前所说的那个用于变换的函数(Func)也装到了箱子当中,变成了M,那么apply所做的就是下面这件事情:M Apply(this M input, M
fmap没有太大的区别,唯一的不同就是我们把func也装到了箱子M里面。Maybe为例实现 apply:public static Maybe Apply(this Maybe input, Maybe
Maybeint> input = 3;
Maybe
isOdd本身可能是 Nothing,当 input和isOdd任何一个为Nothing的时候,结果都是Nothing,否则是Just,并且将值存到这个 Just里面。Monad
returns、bind和then。returns干的事情和上面的Applicative中pure干的事情没有区别。public static Maybe Returns(this A input) => new Maybe(input);
bind干这么一件事情 :M Bind(this M input, Func> func);
M中的A,和一个A -> M这样的函数,产生一个M。then用来充当胶水的作用,将一个个操作连接起来:M Then(this M a, M b);
Monad,那么使用 then,就可以将上一个 Monad和下一个Monad利用函数组合起来将其连接,而不是写为两行语句。public static Maybe Bind(this Maybe input, Func> func)
=> input switch
{
{ HasValue: true } => func(input.Value),
_ => Maybe.Nothing()
};
public static Maybe Then(this Maybe input, Maybe next) => next;
完整
Maybe实现public class Maybe
有哪些常见的 Monads
C# 中有哪些 Monads
TaskNullableIEnumerable+SelectMany
为什么需要 Monads
f(x)对于相同参数恒不变。Maybe或者随机数Random等等,前者除了值本身之外,还带有一个是否有值的状态,而后者还跟计算机的运行环境、时间等随机数种子的因素有关。如果我们所有的函数都是纯函数,那么我们如何用一个函数去产生 Maybe 和 Random 呢?IO如何处理?Monad,这样我们所有涉及到副作用的操作都可以在这个箱子内部完成,将可变的状态隔离在其中,而对外则为一个单体,仍然保持了其不变性。Random为例,我们想给随机数加 1。(下面的代码我就用 Haskell 放飞自我了)nextRandom用于产生一个 Random Int,plusOne用于给一个 Int 加 1:nextRandom :: Random Int // 返回值类型为 Random Int
plusOne :: Int -> Int // 参数类型为 Int,返回值类型为 Int
bind和returns操作,那我们只需要利用着两个操作将我们已有的两个函数组合即可:bind (nextRandom (returns plusOne))
nextRandom >>= plusOne
f(g(x)) = g(f(x))!Monad的bind操作纯属小题大做,此例子中只需要利用Functor的 fmap操作能搞定:fmap plusOne nextRandom
plusOne nextRandom
上一篇:清除Windows系统图标缓存