首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

深入解析 Monad(3)Monad

深入解析 Monad(3)Monad

MonadMonad 本身也是一种 Functor。Monad 的目的在于描述副作用。
函数的副作用与组合方式清单 1 给出了一个简单的函数 increase。该函数的作用是返回输入的参数加 1        之后的值。除了进行计算之外,还通过 count++来修改一个变量的值。这行语句的出现,使得函数 increase 不再是纯函数,每次调用都会对外部环境造成影响。
清单 1.        包含副作用的函数
1
2
3
4
5
6
int count = 0;

int increase(int x) {
  count++;
  return x + 1;
}




清单 1 中的函数 increase 可以划分成两个部分:产生副作用的        count++,以及剩余的不产生副作用的部分。如果可以通过一些转换,把副作用从函数 increase 中剥离出来,那么就可以得到另外一个纯函数的版本 increase1,如清单 2 所示。对函数 increase1 来说,我们可以把返回值改成一个 Vavr 中的        Tuple2<Integer, Integer> 类型,分别包含函数原始的返回值 x + 1 和在 counter 上增加的增量值 1。通过这样的转换之后,函数        increase1 就变成了一个纯函数。
清单 2.        转换之后的纯函数版本
1
2
3
Tuple2<Integer, Integer> increase1(int x) {
  return Tuple.of(x + 1, 1);
}




在经过这样的转换之后,对于函数 increase1 的调用方式也发生了变化,如清单 3 所示。递增之后的值需要从 Tuple2 中获取,而 count 也需要通过 Tuple2 的值来更新。
清单 3.        调用转换之后的纯函数版本
1
2
3
4
int x = 0;
Tuple2<Integer, Integer> result = increase1(x);
x = result._1;
count += result._2;




我们可以采用同样的方式对另外一个相似的函数 decrease 做转换,如清单 4 所示。
清单 4. 函数 decrease        及其纯函数版本
1
2
3
4
5
6
7
8
int decrease(int x) {
  count++;
  return x - 1;
}

Tuple2<Integer, Integer> decrease1(int x) {
  return Tuple.of(x - 1, 1);
}




不过需要注意的是,经过这样的转换之后,函数的组合方式发生了变化。对于之前的 increase 和 decrease 函数,可以直接组合,因为它们的参数和返回值类型是匹配的,如类似        increase(decrease(x)) 或是 decrease(increase(x)) 这样的组合方式。而经过转换之后的 increase1 和        decrease1,由于返回值类型改变,increase1 和 decrease1 不能按照之前的方式进行组合。函数 increase1 的返回值类型与 decrease1        的参数类型不匹配。对于这两个函数,需要另外的方式来组合。
在清单 5 中,compose 方法把两个类型为 Function<Integer,        Tuple2<Integer, Integer>> 的函数 func1 和 func2 进行组合,返回结果是另外一个类型为 Function<Integer,        Tuple2<Integer, Integer>> 的函数。在进行组合时,Tuple2 的第一个元素是实际需要返回的结果,按照纯函数组合的方式来进行,也就是把        func1 调用结果的 Tuple2 的第一个元素作为输入参数来调用 func2。Tuple2 的第二个元素是对 count 的增量。需要把这两个增量相加,作为 compose        方法返回的 Tuple2 的第二个元素。
清单 5.        函数的组合方式
1
2
3
4
5
6
7
8
9
Function<Integer, Tuple2<Integer, Integer>> compose(
    Function<Integer, Tuple2<Integer, Integer>> func1,
    Function<Integer, Tuple2<Integer, Integer>> func2) {
  return x -> {
    Tuple2<Integer, Integer> result1 = func1.apply(x);
    Tuple2<Integer, Integer> result2 = func2.apply(result1._1);
    return Tuple.of(result2._1, result1._2 + result2._2);
  };
}




清单 6 中的 doCompose 函数对 increase1 和 decrease1 进行组合。对于一个输入        x,由于 increase1 和 decrease1 的作用相互抵消,得到的结果是值为 (x, 2) 的对象。
清单 6.        函数组合示例
1
2
3
Tuple2<Integer, Integer> doCompose(int x) {
  return compose(this::increase1, this::decrease1).apply(x);
}




可以看到,doCompose 函数的输入参数和返回值类型与 increase1 和 decrease1 相同。所返回的结果可以继续使用 doCompose        函数来与其他类型相同的函数进行组合。
返回列表