C ++函子的优势-保持状态 [英] C++ functor advantage - holding the state

查看:88
本文介绍了C ++函子的优势-保持状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我确实研究了函子的整个概念,但是不幸的是,我无法理解函子相对于典型函数的真正优势.

I did study the whole idea of functors, unfortunately I can't understand the real advantage of functors over typical functions.

根据一些学术脚本,函子可以保持与函数不同的状态. 任何人都可以通过一些简单易懂的示例来详细说明吗?

According to some academic scripts, functors can hold state unlike functions. Can anyone elaborate on this one with some simple, easy to understand example ?

我真的不明白为什么典型的常规函数​​无法做到这一点.对于这种新手问题,我真的感到很抱歉.

I really can't understand why typical, regular function are not able to do the same. I'm really sorry for this kind of novice question.

推荐答案

作为一个非常简单的演示,让我们考虑一种快速排序.我们选择一个值(通常称为"pivot"),然后将输入集合分为比较小于枢轴的那些和比较大于或等于枢轴 1 的那些.

As a really trivial demonstration, let's consider a Quick sort. We choose a value (usually known as the "pivot") and separate the input collection into those that compare less than the pivot, and those that compare greater than or equal to the pivot1.

标准库已经具有std::partition,它可以自己进行分区-将集合分离为满足指定条件的项和不满足指定条件的项.因此,要进行分区,我们只需要提供一个合适的谓词即可.

The standard library already has std::partition that can do the partitioning itself--separate a collection into those items that satisfy a specified condition, and those that don't. So, to do our partitioning, we just have to supply a suitable predicate.

在这种情况下,我们需要一个简单的比较,例如:return x < pivot;.每次都传递枢轴值变得很困难. std::partition只是从集合中传递一个值,然后问:这是否通过了您的测试?"您无法告诉std::partition当前枢轴值是什么,并在调用它时将其传递给例程.当然,可以完成 (例如,Windows中的许多枚举功能都是以这种方式工作的),但是它变得非常笨拙.

In this case, we need a simple comparison something like: return x < pivot;. Passing the pivot value every time becomes difficult though. std::partition just passes a value from the collection and asks: "does this pass your test or not?" There's no way for you to tell std::partition what the current pivot value is, and have it pass that to your routine when it's invoked. That could be done, of course (e.g., many enumeration functions in Windows work this way), but it gets pretty clumsy.

当我们调用std::partition时,我们已经选择了枢轴值.我们想要的是一种将该值绑定到将传递给比较函数的参数之一的方法.一种非常丑陋的方法是通过全局变量传递"它:

When we invoke std::partition we've already chosen the pivot value. What we want is a way to...bind that value to one of the parameters that will be passed to the comparison function. One really ugly way to do that would be to "pass" it via a global variable:

int pivot;

bool pred(int x) { return x < pivot; }

void quick_sort(int *begin, int *end) { 
    if (end - begin < 2)
        return;

    pivot = choose_pivot(begin, end);

    int *pos = std::partition(begin, end, pred);
    quick_sort(begin, pos);
    quick_sort(pos, end);
}

真的希望我不必指出,如果我们可以帮助的话,我们宁愿不使用全局变量.避免它的一种相当简单的方法是创建一个函数对象.创建对象时,我们传递当前的枢轴值,并将该值存储为对象中的状态:

I really hope I don't have to point out that we'd rather not use a global for this if we can help it. One fairly easy way to avoid it is to create a function object. We pass the current pivot value when we create the object, and it stores that value as state in the object:

class pred { 
    int pivot;
public:
    pred(int pivot) : pivot(pivot) {}

    bool operator()(int x) { return x < pivot; }
};

void quick_sort(int *begin, int *end) { 
    if (end-begin < 2)
        return;

    int pivot = choose_pivot(begin, end);

    int *pos = std::partition(begin, end, pred(pivot));        
    quick_sort(begin, pos);
    quick_sort(pos, end);
}

这增加了一点点额外的代码,但是作为交换,我们取消了一个全局的,相当合理的交换.

This has added a tiny bit of extra code, but in exchange we've eliminated a global--a fairly reasonable exchange.

当然,从C ++ 11开始,我们还可以做得更好—该语言添加了"lambda表达式",可以为我们创建一个非常类似的类.使用此代码,我们的代码如下所示:

Of course, as of C++11 we can do quite a bit better still--the language added "lambda expressions" that can create a class pretty much like that for us. Using this, our code looks something like this:

void quick_sort(int *begin, int *end) { 
    if (end-begin < 2)
        return;

    int pivot = find_pivot(begin, end);

    auto pos = std::partition(begin, end, [pivot](int x) { return x < pivot; });
    quick_sort(begin, pos);
    quick_sort(pos, end);
}

这会更改用于指定类/创建函数对象的语法,但其基本概念仍与前面的代码相同:编译器生成带有构造函数和类的类. operator().我们放在方括号中的值将传递给构造函数,并且(int x) { return x < pivot; }基本上成为该类 2 operator()的主体.

This changes the syntax we use to specify the class/create the function object, but it's still pretty much the same basic idea as the preceding code: the compiler generates a class with a constructor and an operator(). The values we enclose in the square brackets are passed to the constructor, and the (int x) { return x < pivot; } basically becomes the body of the operator() for that class2.

这使代码更容易编写 ,但它并没有改变我们创建对象,捕获"构造函数中某些状态的基本事实,并使用重载的operator()进行比较.

This makes code much easier to write and much easier to read--but it doesn't change the basic fact that we're creating an object, "capturing" some state in the constructor, and using an overloaded operator() for the comparison.

当然,比较恰好是我们需要进行排序之类的事情.它更普遍地使用lambda表达式和函数对象,但我们当然不限于此.再举一个例子,让我们考虑标准化"双打的集合.我们想要找到最大的一个,然后将集合中的每个值除以该值,因此每个项目的范围都在0.0到1.0之间,但是所有项目彼此之间保持与以前相同的比率:

Of course, a comparison just happens to be what we need for things like sorting. It is a common use of lambda expressions and function objects more generally, but we're certainly not restricted to it. Just for another example, let's consider "normalizing" a collection of doubles. We want to find the largest one, then divide every value in the collection by that, so each item is in the range 0.0 to 1.0, but all retaining the same ratios to each other as they previously had:

double largest = * std::max_element(begin, end);
std::for_each(begin, end, [largest](double d) { return d/largest; });

在这里,我们仍然具有几乎相同的模式:创建一个存储一些相关状态的功能对象,然后重复应用该功能对象的operator()进行实际工作.

Here again we have pretty much the same pattern: create a function object that stores some relevant state, then repeatedly apply that function object's operator() to do the real work.

  1. 我们可以分为小于或等于和大于.或者我们可以创建三个组:小于,等于,大于.在存在许多重复项的情况下,后者可以提高效率,但是目前我们真的不在乎.
  2. 关于lambda表达式,不仅需要了解更多信息,我还简化了一些事情,而完全忽略了我们目前不关心的其他事情.

这篇关于C ++函子的优势-保持状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆