这种C#扩展方法是不纯的吗?如果是的话,是坏代码吗?

这种C#扩展方法是不纯的吗?如果是的话,是坏代码吗?,c#,functional-programming,extension-methods,C#,Functional Programming,Extension Methods,我正在学习一点函数编程,我想知道: 1) 如果我的ForEach扩展方法是纯的?我这样称呼它似乎违反了“不要弄乱传递的对象”,对吗 它不是纯粹的,因为它可以称为不纯的方法。我认为根据典型的定义,纯度是一个传递闭包-只有当它调用的所有函数(直接或间接)都是纯函数,或者如果这些函数的效果被封装(例如,它们只会变异一个非转义的局部变量),函数才是纯函数。事实上,因为lambda表达式包含赋值,根据定义,这个函数现在是不纯的。赋值是否与当前函数外定义的一个参数或另一个对象相关无关。。。函数必须没有任何副

我正在学习一点函数编程,我想知道:

1) 如果我的
ForEach
扩展方法是纯的?我这样称呼它似乎违反了“不要弄乱传递的对象”,对吗


它不是纯粹的,因为它可以称为不纯的方法。我认为根据典型的定义,纯度是一个传递闭包-只有当它调用的所有函数(直接或间接)都是纯函数,或者如果这些函数的效果被封装(例如,它们只会变异一个非转义的局部变量),函数才是纯函数。

事实上,因为lambda表达式包含赋值,根据定义,这个函数现在是不纯的。赋值是否与当前函数外定义的一个参数或另一个对象相关无关。。。函数必须没有任何副作用才能被称为纯函数。有关更精确(尽管相当简单)的定义,请参见,该定义详细说明了函数必须满足的两个条件才能被视为纯函数(无副作用是其中之一)。我相信lambda表达式通常是作为纯函数使用的(至少我可以想象它们最初是从数学的角度来研究的),但显然C#在这方面并不严格,因为纯函数语言是这样的。因此,这可能是一个不错的做法,尽管这样一个函数是不纯净的,这绝对值得一提。

是的,它不是纯净的,但这是一个没有意义的观点,因为它甚至不是一个函数

由于该方法不返回任何内容,因此它执行任何操作的唯一选项是影响正在发送的对象,或者影响不相关的内容(如写入控制台窗口)

编辑:
回答你的第三个问题;是的,这是糟糕的代码,因为它似乎在做一些它没有做的事情。该方法返回一个集合,因此它看起来是纯的,但由于它只返回发送进来的集合,它实际上并不比第一个版本更纯。若要理解,该方法应使用
Func
委托作为转换,并返回已转换项的集合:

public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Func<T,T> converter) {
   foreach (T item in source) {
      yield return converter(item);
   }
}
公共静态IEnumerable ForEach(此IEnumerable源代码,Func转换器){
foreach(源中的T项){
收益-收益转换器(项目);
}
}

当然,如果分机调用是纯的,则仍取决于转换器函数。如果它不复制输入项,只是更改它并返回它,那么调用仍然不是纯的。

杂质并不一定意味着坏代码。许多人发现使用副作用来解决问题既简单又有用。关键是首先要知道如何以一种纯粹的方式去做,这样你就知道什么时候杂质是合适的:)

NET在类型系统中没有纯洁性的概念,因此接受任意委托的“纯洁”方法总是不纯洁的,这取决于它的调用方式。例如,“Where”,又名“filter”,通常被认为是一个纯函数,因为它不修改其参数或修改全局状态

但是,没有什么能阻止你把这样的代码放在参数中。例如:

things.Where(x => { Console.WriteLine("um?"); 
                    return true; })
      .Count();
所以这绝对是Where的不纯用法。可枚举项可以在迭代时执行任何操作

您的代码是否有问题?没有。使用foreach循环同样“不纯”——您仍在修改源对象。我一直都是这样写代码的。将一些选择、过滤器等链接在一起,然后对其执行ForEach以调用一些工作。你说得对,它更干净更容易

示例:ObservableCollection。由于某种原因,它没有AddRange方法。那么,如果我想给它添加一些东西,我该怎么做

foreach(var x in things.Where(y => y.Foo > 0)) { collection.Add(x)); } 

我更喜欢第二个。至少,我不认为它比第一种方式更糟糕

什么时候是坏代码?当它在一个意想不到的地方对代码产生副作用时。这是我使用Where的第一个示例。即使如此,有时范围非常有限,用法也很明确

链接ForEach

我写过这样的代码。为了避免混淆,我会给它另一个名字。主要的困惑是“这是立即评估的还是懒惰的?”。ForEach意味着它将立即执行一个循环。但是返回IEnumerable意味着将根据需要处理这些项。所以我建议给它取另一个名字(“进程”、“修改序列”、“OnEach”……诸如此类),并让它变得懒惰:

public static IEnumerable<T> OnEach(this IEnumerable<T> src, Action<T> f) {
  foreach(var x in src) {
    f(x);
    yield return x;
  }
}
公共静态IEnumerable OnEach(此IEnumerable src,动作f){
foreach(src中的变量x){
f(x);
收益率x;
}
}

基本上,任何调用函数参数的公共.NET方法都是不纯的,因为它可以调用不纯代码?我们可以想象一个类型系统,在该系统中,你可以对“纯度”进行“泛型”,因此G(Func f)可以使G为纯-when-f-is-pure和不纯-when-f-is-purpure。但是,直到我们在类型系统中有了它,如果他们的情妇不是纯正的,那么区分纯正的HoF和纯正的HoF是没有用的吗?我不知道你在问什么。。。“直到我们在类型系统中拥有它”,甚至还没有“纯”属性,更不用说它的类型系统了;我们已经在讨论未来幻想了,所以没有什么东西本身是“有用的”,这都是纸上谈兵式的语言设计推测。无论如何,我只是对你的答案投了更高的票,因为它比我的好,而且更多的是关于这个话题。更高阶的代码不是专注于定义和类型系统,而是编写好的代码,最大限度地减少意外/意外的影响和交互。@Noldorin:不,我不是这个意思。它没有返回值,所以它不是一个函数。
foreach(var x in things.Where(y => y.Foo > 0)) { collection.Add(x)); } 
things.Where(x => x.Foo > 0).ForEach(collection.Add);
public static IEnumerable<T> OnEach(this IEnumerable<T> src, Action<T> f) {
  foreach(var x in src) {
    f(x);
    yield return x;
  }
}