函子我应该什么时候使用它们它们的预期用途是什么

发布于 2024-07-23 12:01:11 字数 1435 浏览 5 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

薄荷梦 2024-07-30 12:01:11

我认为您混淆了不同语言的术语。 您似乎在 C++ 或 Java 意义上使用“Functor”,例如请参阅维基百科页面。 在 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 语法的优点是它更简洁,并且可以对参数进行类型推断。

更新: 匿名方法(delegate 关键字)相对于 lambda 的优点是,如果不需要参数,可以完全省略参数:

// 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,谓词采用某种类型的实例并返回 true > 或

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

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

回复:更新的问题

您会问(我认为)是否有很多 lambda 用例。 还有更多的内容无法列出!

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

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

Where 方法是 IEnumerable 接口上的扩展方法,其中在本例中,T 是某种Person 类。

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

I think you're confusing terms from different languages. You seem to be using "Functor" in the C++ or Java sense, e.g. see the wikipedia page. In C++, it's an object of a class that overloads the function-call operator, so it can be used as a function but with state.

This is logically the same thing as a delegate bound to an instance method in C# (or any .NET language).

There are three ways to write such a thing. First, you can write an ordinary method, and then assign the name of the method to a delegate variable.

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

void Foo()
{
    Action a = MyMethod;
    a();
}

Second, you can use anonymous method syntax, introduced in C# 2.0:

void Foo()
{
    Action a = delegate { Console.WriteLine("Hi!"); }
    a();
}

Thirdly, you can use lambda syntax, introduced in C# 3.0:

void Foo()
{
    Action a = () => Console.WriteLine("Hi!");
    a();
}

The advantage of the last two is that the body of the method can read and write local variables in the containing method.

The advantage of lambda syntax over anon-methods are that it is more succinct and it does type inference on parameters.

Update: The advantage of anon-methods (delegate keyword) over lambdas is that you can omit the parameters altogether if you don't need them:

// 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!"); };

I know of only one situation where this is worth knowing, which is when initializing an event so that you don't have to check for null before firing it:

event EventHandler Birthday = delegate { };

Avoids a lot of nonsense elsewhere.

Finally, you mention that there are four kinds of functor. In fact there are an infinity of possibly delegate types, although some authors may have their favourites and there obviously will be some common patterns. An Action or Command takes no parameters and returns void, and a predicate takes an instance of some type and returns true or false.

In C# 3.0, you can whip up a delegate with up to four parameters of any types you like:

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

Re: Updated Question

You ask (I think) if there are many use cases for lambdas. There are more than can possibly be listed!

You most often see them in the middle of larger expressions that operate on sequences (lists computed on-the-fly). Suppose I have a list of people, and I want a list of people exactly forty years old:

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

The Where method is an extension method on the IEnumerable<T> interface, where T in this case is some kind of Person class.

This is known in .NET as "Linq to Objects", but known elsewhere as pure functional programming on sequences or streams or "lazy" lists (all different names for the same thing).

酒中人 2024-07-30 12:01:11

在 .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);

这里是< code>max 作为闭包捕获到委托中。

回复“框架内的类是否为此做好了准备?” - 很多都是这样,LINQ 走了很长的路,让这种情况变得更广泛。 LINQ 提供了(例如)所有 IEnumerable 的扩展方法 - 这意味着没有基于委托的访问的集合可以免费获取它们:

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());

这里是 Where< /code> 和 OrderBy 方法是采用委托的 LINQ 扩展方法。

In .NET terms, I think what you are describing is the Delegate - and it exists in all of .NET, not just C#.

I'm not sure that a "closure" would a "type" in the same was as a comparer/predicate/transformer, since in C# terms a closure is simply an implementation detail but can be any of those three.

In .NET, delegates are used in two main ways:

  • as the eventing mechanism
  • to provide functional-style programming

The first is important, but it sounds like you are more interested in the second. In reality, they operate much like single-method interfaces... consider:

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

A closure is more where we bring additional scope from outside the delegate into the delegate:

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

here the max is captured into the delegate as a closure.

Re "Are classes within the framework prepaired for this?" - many are, and LINQ goes a long way to allowing this even wider. LINQ provides extension methods over (for example) all of IEnumerable<T> - meaning that collections without delegate-based access aquire them for free:

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());

Here the Where and OrderBy methods are LINQ extension methods that take delegates.

孤凫 2024-07-30 12:01:11

术语有趣; 我对“Functor”一词的自发解释是它指的是匿名方法。 这就是我的看法。

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

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

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 变量方法范围。 如果我选择将事件处理程序作为其自己的单独方法来实现,我还需要找出一种方法来让它获取实例,可能是通过引入类级别成员,这看起来并不像单元测试类的完美设计。

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

  • 我没有兴趣从该特定位置以外的其他地方引用该代码段
  • 该方法需要访问超出常规方法范围的数据

Interesting with terminology; my spontaneous interpretation of the term "Functor" was that it referred to anonymous methods. So that will be my take on it.

These are some of my typical uses:

Comparisons (usually for sorting a list):

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);
});

I will use this approach for comparisons, rather than having it in its own method an using a delegate for that method, in the cases where this particular sort happens in one place only. If it is likely that I will want to use the same comparison somewhere else, it gets to live in its own, reusable method.

Another of my fairly common uses is in unit testing, when the test relies on some event being raised. I have found that to be essential when unit testing workflows in 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; 

In this case it is simpler to have the event handler declared as anonymous methods since it uses the instance variable that is defined in the unit test method scope. Had I intstead opted to implelment the event handler as its own separate method I would also need to figure out a way for it to pick up instance, probably by introducing a class level member, which would not seem like a perfect design in a unit test class.

There are more cases where I find this in my code, but they usually have one or two things in common:

  • I have no interest in referencing that piece of code from anywhere else than in that particular place.
  • The method needs access to data that would be out of the scope for a regular method
似最初 2024-07-30 12:01:11

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

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

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

哈斯克尔说得对。 您声明一个类型实现函子接口,然后您获得映射方法。

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

The real answer is that a functor is a type of mathematical object, and is "reified" by different languages in different ways. For example, suppose that you have a "container" object that stores a bunch of other objects of the same type. (For example, a set or array) Then, if your language had a method that let you 'map' over the container, so that you could call a method on each object in the container, then the container would be a functor.

In other words, a functor is a container with a method that lets you pass a method to its contained things.

Every language has its own way of doing this, and they sometimes conflate the usage. For example, C++ uses function pointers to represent the "passing in" of a method, and calls the function pointer a "functor." Delegates are just a handle on methods that you can pass in. You are using the terminology "incorrectly", just like C++ does.

Haskell gets it right. You declare that a type implements the functor interface, and then you get the mapping method.

Functions (like lambdas) are functors too, but it can be kind of hard to think of a function as a "container". In short, a function is a "container" around a return value, constructed in such a way that the return value (possibly) depends on the function's arguments.

不弃不离 2024-07-30 12:01:11

我确定你指的是 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》一书(除了我认为最好的之外,在 Lambda 方面有非常好的部分。(ISBN 978-0-596-51922-3)

I'm sure you mean Lambda Expressions. Those are Small Functions you can write very quickly, and they have the characteristic "=>" Operator. These are a new Feature of C# 3.0.

This Example will be a classic Transformer; to use one we need a delegate to define the signature for the Lambda Function.

delegate int Transformer(int i);

Now Declare a Lambda with this delegate:

Transformer sqr = x => x * x;

We can use it like a normal function:

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

These are used in LINQ Queries a lot, for example to Sort (Comparer), to Search through (Predicate).

The book "C# Pocket Reference" (apart from beign the best around in my opinion, has a very good part on Lambdas. (ISBN 978-0-596-51922-3 )

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文