函子我啥时候应该使用它们它们的预期用途是啥[关闭]

Posted

技术标签:

【中文标题】函子我啥时候应该使用它们它们的预期用途是啥[关闭]【英文标题】:Functors when should I use them whats their intended use [closed]函子我什么时候应该使用它们它们的预期用途是什么[关闭] 【发布时间】:2010-11-01 17:28:52 【问题描述】:

我似乎无法绕开他们。

据我了解,它是在为类动态添加逻辑。框架内的类是否为此做好了准备?

我为什么要扩展类并在扩展中添加功能。我将可以在全球范围内访问,并且更容易维护。

我读过有 4 种仿函数类型:

比较器 关闭 谓词 变压器

我们可能应该处理每一个。

附言vb有类似的吗?

所以我可以说我认为 lambda 表达式是函子。这让我明白了一点:)(呵呵)

Lambda 表达式是函子吗? 匿名函数是函子吗?

但我问这个问题是因为我遇到了另一种类型的 fucntor,即这些:

delegate void FunctorDelegate(int value);
class Addition 
    FunctorDelegate _delegate;

    public Addition AddDelegate(FunctorDelegate deleg) 
        _delegate += deleg;
        return this;
    
    public int AddAllElements(IList< int> list) 
        int runningTotal = 0;
        foreach( int value in list) 
            runningTotal += value;
            _delegate(value);
        
        return runningTotal;
    

然后用这个来调用它:

 int runningTotal = new Addition()
     .AddDelegate(new FunctorDelegate(
                     delegate(int value) 
                         if ((value % 2) == 1) 
                             runningOddTotal += value;
                         
                     ))
    .AddDelegate(new FunctorDelegate(
                     delegate(int value) 
                         if ((value % 2) == 0) 
                             runningEvenTotal += value;
                         
                     ))
    .AddAllElements(list);

所以没有花哨的 lambda 风格的东西。

现在我有了这个例子,但完全不清楚为什么这是一个“好”的解决方案。

“在大多数情况下”作为 lambda 表达式或匿名方法使用的委托(函子)是否只是程序员的捷径?据我所知,只有少数情况下它们实际上是解决问题的首选。

【问题讨论】:

我倾向于说它不是一个好的解决方案;也许他们试图展示潜在用法,而不是典型用法。关于该代码的唯一“好”的事情(我在这里很慷慨)是它只枚举数据一次 - 但对此还有其他答案。例如,PushLINQ (MiscUtil) 允许对一个“仅一次”的数据馈送进行多个聚合,而 (IMO) 则更加优雅。 呵呵,这段代码是为了展示它的潜在用途,但在这方面做得很糟糕:)。我发布它是为了解释我遇到的“其他”类型的仿函数(匿名方法)。但目前尚不清楚/不清楚在我日常生活中的哪个地方这将成为“THE”解决方案。 Fredrik 有点回答。 【参考方案1】:

我认为您混淆了不同语言的术语。您似乎在使用 C++ 或 Java 意义上的“Functor”,例如see the wikipedia page。在 C++ 中,它是一个类的对象,它重载了函数调用运算符,因此它可以作为函数使用,但有状态。

这在逻辑上与绑定到 C#(或任何 .NET 语言)中的实例方法的委托相同。

这样的东西有三种写法。首先可以写一个普通的方法,然后把方法名赋给一个委托变量。

void MyMethod()  Console.WriteLine("Hi!"); 

void Foo()

    Action a = MyMethod;
    a();

其次,可以使用匿名方法语法,在 C# 2.0 中引入:

void Foo()

    Action a = delegate  Console.WriteLine("Hi!"); 
    a();

第三,您可以使用 C# 3.0 中引入的 lambda 语法:

void Foo()

    Action a = () => Console.WriteLine("Hi!");
    a();

后两者的优点是方法体可以在包含方法中读写局部变量。

lambda 语法相对于匿名方法的优势在于它更简洁,并且可以对参数进行类型推断。

更新: anon-methods(delegate 关键字)优于 lambdas 的优势在于,如果不需要参数,您可以完全省略它们:

// correct way using lambda
button.Click += (sender, eventArgs) => MessageBox.Show("Clicked!");

// compile error - wrong number of arguments
button.Click += () => MessageBox.Show("Clicked!");

// anon method, omitting arguments, works fine
button.Click += delegate  MessageBox.Show("Clicked!"); ;

我知道只有一种情况值得了解,即在初始化事件时,这样您就不必在触发之前检查null

event EventHandler Birthday = delegate  ;

在别处避免了很多废话。

最后,你提到有四种函子。事实上,可能的委托类型有无数种,尽管有些作者可能有他们的最爱,而且显然会有一些常见的模式。 ActionCommand 不接受参数并返回 void,谓词接受某种类型的实例并返回 truefalse

在 C# 3.0 中,您可以使用最多四个您喜欢的任何类型的参数来创建一个委托:

Func<string, int, double> f;  // takes a string and an in, returns a double

回复:更新问题

您问(我认为)lambda 是否有很多用例。实在是太多了!

您最常在对序列(动态计算的列表)进行操作的较大表达式的中间看到它们。假设我有一份人员名单,我想要一份正好是 40 岁的人员名单:

var exactlyForty = people.Where(person => person.Age == 40);

Where 方法是IEnumerable&lt;T&gt; 接口上的扩展方法,其中T 在这种情况下是某种Person 类。

这在 .NET 中称为“Linq to Objects”,但在其他地方称为序列或流上的纯函数式编程或“惰性”列表(同一事物的所有不同名称)。

【讨论】:

正确。现在我们正在取得进展。所以函子(如名称)在.net中并不真正存在,它们是代表。 lambda 的简洁之处在于我认为个人风格我不介意输入更多内容以保持可读性。但是类型推断非常酷,并且绝对是 lambda 风格的专家。我会在我的问题中添加更多内容。 感谢您的接受,我添加了一个关于省略委托参数的额外更新。 谢谢 :) 我认为我们在解决这个问题并为 lambda、委托和匿名函数提供案例方面做得很好。在我工作的地方,他们害怕这些东西。但是,嘿,你不能阻止未来的权利。 :)。【参考方案2】:

在 .NET 术语中,我认为您所描述的是 Delegate - 它存在于所有 .NET 中,而不仅仅是 C#。

我不确定“闭包”是否会是与比较器/谓词/转换器相同的“类型”,因为在 C# 术语中,闭包只是一个实现细节,但可以成为 这三个中的任何一个。

在 .NET 中,委托主要有两种使用方式:

作为事件机制 提供函数式编程

第一个很重要,但听起来您对第二个更感兴趣。实际上,它们的运行方式很像单方法接口……考虑一下:

List<int> vals = new List<int>  1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ;
List<int> evenVals = vals.FindAll(i => i % 2 == 0); // predicate
List<string> valsAsStrings = vals.ConvertAll(i => i.ToString()); // transformer
// sort descending
vals.Sort((x, y) => y.CompareTo(x)); // comparer

闭包更多的是我们将额外的范围从外部委托进入委托:

int max = int.Parse(Console.ReadLine()); // perhaps 6
List<int> limited = vals.FindAll(i => i <= max);

这里 max 被捕获到委托中作为闭包。

关于“框架内的类是否为此做好了准备?” - 很多,而且 LINQ 采用 long 的方式来允许这一点更广泛。 LINQ 为(例如)所有 IEnumerable&lt;T&gt; 提供了扩展方法——这意味着集合没有基于委托的访问可以免费获取它们:

int[] data =  1,2,3,4,5,6,7,8,9 ;
var oddData = data.Where( i => i % 2 == 1 );
var descending = data.OrderBy(i => -i);
var asStrings = data.Select(i => i.ToString());

这里的WhereOrderBy 方法是接受委托的LINQ 扩展方法。

【讨论】:

您好,感谢您的回答。我知道​​他们与代表有关。我认为在不遇到委托的情况下进行任何严肃的 c# 编程是不可能的。我主要将它们用于异步函数和事件。但我问这个问题是因为我遇到了另一种形式的函子。我不知道像谓词这样的 linq 也是函子。我会更新有关样式的问题。 LINQ 的东西……很复杂;-p IEnumerable 上的扩展方法使用委托,但 IQueryable 上的扩展方法使用表达式树,看起来与调用者相同 (假设它们使用 lambda 语句或查询语法)但它们完全、100% 不同;-p 所以只有部分 linq lamba 表达式的东西是函子。对。 :) 我喜欢那个认为这是个好主意的人 :P 它有效,而且我每天都使用这些东西,但只有少数人真正知道它是如何或为什么有效的 :) 不...不是这样;一个 lambda-delegate 是一个仿函数,一个匿名方法也是一个常规方法。区别在于 Func<...> 和 Expression> 之间的细微差别:marcgravell.blogspot.com/2009/03/explaining-expression.html 这是一篇非常有趣的文章,尽管可能需要更多阅读才能完全理解它。就像你说的那样,你在一个小(ish)的空间里覆盖了很多东西。但现在肯定更清楚了函子是什么以及为什么是函子。【参考方案3】:

对术语感兴趣;我对“函子”一词的自发解释是它指的是匿名方法。所以这将是我的看法。

这些是我的一些典型用途:

比较(通常用于对列表进行排序):

List<int> ints = new List<int>();
ints.AddRange(new int[]  9, 5, 7, 4, 3, 5, 3 );
ints.Sort(new Comparison<int>(delegate(int x, int y)
    
        return x.CompareTo(y);
    ));
// yes I am aware the ints.Sort() would yield the same result, but hey, it's just
// a conceptual code sample ;o)

// and the shorter .NET 3.5 version:
ints.Sort((x, y) =>

    return x.CompareTo(y);
);

我将使用这种方法进行比较,而不是在它自己的方法中使用该方法的委托,在这种特定排序仅发生在一个地方的情况下。如果我很可能想在其他地方使用相同的比较,它就会以自己的可重用方法存在。

我的另一个相当常见的用途是在单元测试中,当测试依赖于某个被引发的事件时。我发现在 Workflow Foundation 中对工作流进行单元测试时这是必不可少的:

WorkflowRuntime runtime = WorkflowHost.Runtime;  
WorkflowInstance instance = runtime.CreateWorkflow(typeof(CreateFile)); 
EventHandler<WorkflowEventArgs> WorkflowIdledHandler = delegate(object sender, WorkflowEventArgs e)

    // get the ICreateFileService instance from the runtime  
    ISomeWorkflowService service = WorkflowHost.Runtime.GetService<ISomeWorkflowService>();

    // set the desired file content  
    service.DoSomeWork(instance.InstanceId, inputData);
;  
// attach event handler
runtime.WorkflowIdled += WorkflowIdledHandler;  

instance.Start();  
// perform the test, and then detach the event handler
runtime.WorkflowIdled -= WorkflowIdledHandler; 

在这种情况下,将事件处理程序声明为匿名方法会更简单,因为它使用在单元测试方法范围内定义的instance 变量。如果我选择将事件处理程序作为它自己的单独方法来实现,我还需要想办法让它接收instance,可能是通过引入一个类级别成员,这似乎不是一个完美的设计单元测试类。

我在我的代码中发现这一点的情况更多,但它们通常有一个或两个共同点:

我没有兴趣从其他地方引用那段代码该方法需要访问超出常规方法范围的数据

【讨论】:

这看起来更像我最初认为的函子。我认为你帖子的最后两句话真的很吸引我,这解释了为什么我会使用 functors/anonymousmethods/lambdaexpression。【参考方案4】:

真正的答案是函子是一种数学对象,并且被不同的语言以不同的方式“具体化”。例如,假设您有一个“容器”对象,它存储了一堆相同类型的其他对象。 (例如,一个集合或数组)然后,如果您的语言有一个方法可以让您“映射”容器,以便您可以在容器中的每个对象上调用一个方法,那么 container 将是函子

换句话说,函子是一个容器,其中包含一个方法,可让您将方法传递给它所包含的事物。

每种语言都有自己的方式,有时它们会将用法混为一谈。例如,C++ 使用函数指针来表示方法的“传入”,并将函数指针称为“函子”。委托只是您可以传入的方法的句柄。您正在“错误地”使用术语,就像 C++ 一样。

Haskell 做对了。你声明一个类型实现函子接口,然后你得到映射方法。

函数(如 lambda)也是函子,但将函数视为“容器”可能有点困难。简而言之,函数是一个围绕返回值的“容器”,其构造方式使得返回值(可能)取决于函数的参数。

【讨论】:

【参考方案5】:

我确定您的意思是 Lambda 表达式。这些是您可以非常快速地编写的小函数,并且它们具有特征“=>”运算符。这些是 C# 3.0 的新特性。

这个例子将是一个经典的 Transformer;要使用一个,我们需要一个委托来定义 Lambda 函数的签名。

delegate int Transformer(int i);

现在用这个委托声明一个 Lambda:

Transformer sqr = x => x * x;

我们可以像普通函数一样使用它:

Console.WriteLine(sqr(3)); //9

这些在 LINQ 查询中被大量使用,例如排序(比较器)、搜索(谓词)。

《C# Pocket Reference》一书(除了我认为最好的书外,在 Lambdas 上也有很好的部分。(ISBN 978-0-596-51922-3)

【讨论】:

顺便说一句 - 对于 .NET 3.5,使用 Func 比定义一个新的 Transformer 委托来做同样的事情更常见...... 不仅仅是 lambda 表达式。正如我们已经建立的(我认为)只有部分或 lambda 表达式实际上是函子。

以上是关于函子我啥时候应该使用它们它们的预期用途是啥[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该嘲笑?

我啥时候应该使用 ugettext_lazy?

我啥时候应该使用 mongoDB 而不是关系数据库

我啥时候应该在 JPA 中使用 @JoinColumn 或 @JoinTable?

我啥时候应该参加内部课程[关闭]

我啥时候应该销毁令牌[关闭]