c#函数式编程 Functional Programming in C# [16]

4.2 使用 ForEach 执行副作用

  在第三章中,我们讨论了Func和Action之间的二分法。 我们在Map中又遇到了这个问题。Map需要一个Func,那么如果我们想对给定结构中的每个值执行一个Action,我们该怎么做?
  您可能知道 List< T > 有一个 ForEach 方法,它接受一个 Action< T >,它为列表中的每个项目调用:

using static System.Console;
new List<int> { 1, 2, 3 }.ForEach(Write);
// prints: 123

  这基本上就是我们想要的。 让我们概括一下,这样我们就可以在任何 IEnumerable 上调用ForEach:

public static IEnumerable<Unit> ForEach<T> (this IEnumerable<T> ts, Action<T> action) 
	=> ts.Map(action.ToFunc()).ToImmutableList();

  这段代码把 Action 改成了一个返回 Unit 的函数,然后依赖于Map的实现。这只会创建一个懒惰地评估的 Unit 序列。 在这里,我们实际上希望执行副作用;因此调用了ToImmutableList。不出所料,其用法是,

Enumerable.Range(1, 5).ForEach(Write);
// prints: 12345

  现在我们来看看Option的ForEach的定义。这个定义在Map中是很简单的,使用ToFunc函数将Action转换为Func:

public static Option<Unit> ForEach<T> (this Option<T> opt, Action<T> action) 
	=> Map(opt, action.ToFunc());

  ForEach的名字可能有点反直觉–记住,一个 Option 最多只有一个内部值,所以给定的动作将被精确地调用一次(如果Option是Some)或永远不会被调用(如果它是None)。
  下面是一个使用 ForEach 将一个动作应用于一个 Option 的例子:

var opt = Some("John");
opt.ForEach(name => WriteLine($"Hello {name}"));
// prints: Hello John

  然而,请记住第2章,我们应该把纯逻辑和副作用分开。我们应该用Map来表示逻辑,用ForEach来表示副作用,所以最好把前面的代码改写成如下:

opt.Map(name => $"Hello {name}").ForEach(WriteLine);

隔离副作用   让你用ForEach应用的Action的范围尽可能的小:用Map处理数据转换,用ForEach处理副作用。这遵循了FP的一般理念,即如果可能的话避免副作用,否则就将其隔离。

  花点时间在 REPL 中进行试验,看看 Map 和 ForEach 可以与 IEnumerable 和 Option 一起使用。下面是一个例子:

using static System.Console;
using String = LaYumba.Functional.String;
Option<string> name = Some("Enrico");
name.Map(String.ToUpper).ForEach(WriteLine);
// prints: ENRICO

IEnumerable<string> names = new[] { "Constance", "Albert" };
names.Map(String.ToUpper).ForEach(WriteLine);
// prints: CONSTANCE
//         ALBERT

  注意到你可以使用相同的模式,无论你是用Option还是用IEnumerable工作。这不是很好吗?现在你可以把 Option 和 IEnumerable 视为特殊的容器,你有一组核心函数可以与它们进行交互。 如果你遇到一种新的容器,并且定义了 Map 或 ForEach,你可能会对它们的作用有一个很好的概念,因为你认识到了这种模式。

注意 在前面的代码中,我使用了LaYumba.Functional.String,一个通过静态方法暴露System.String一些常用功能的类。这使得我可以将String.ToUpper作为一个函数来引用,而不需要指定ToUpper实例方法所作用的实例,如:s => s.ToUpper()

  总之,ForEach与Map类似,但它接受一个Action而不是一个函数,所以它被用来执行副作用。让我们继续讨论下一个核心函数。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值