2017-07-12 62 views
2

我想用std :: transform替换for循环。因为我有一个算法和lambda函数小的经验,我不知道这是正确的方式C++变换和lambda - 替换循环

原始代码

for (size_t i=0; i < dataPhase.size(); ++i) 
{ 
    dataPhase[i] = fmod(dataPhase[i], pi*1.00001); 
} 

的std ::以拉姆达变换

std::transform(dataPhase.begin(), dataPhase.end(), dataPhase.begin(), 
       [](double v){return fmod(v, pi*1.00001); } 
); 

,我需要捕获这里?

我可以做些什么来代替在这样的情况下,在使用索引循环,如下面的代码:

const int halfsize = int(length/2); 
for (size_t i=0; i < length; ++i) 
{ 
    axis[i] = int(i) - halfsize; 
} 

编辑: 我想扩大的问题(如果允许的话)。

是否有可能与一些不同的东西

for(std::vector<complex<double> >::size_type i = 0; i != data.size(); i++) { 
    dataAmplitude[i] = abs(data[i]); 
    dataPhase[i]  = arg(data[i]); 
} 

这里不是原来的矢量被修改替换for循环在这种情况下,但用于两种不同载体的价值。 1

+0

如果你得到的结果,它可能是正确的。你观察到什么?算法在迭代器而不是索引上工作。将第二个片段转换为使用算法没有意义,因为在这种情况下,索引循环更具可读性 – user463035818

+0

'std :: transform'也可用于没有明确索引的类型(如链接列表和所以)。只要前两个参数满足“输入迭代器”类别,第三个参数满足“输出迭代器”要求。因此,函数中没有(概念上的)索引。 – JHBonarius

+0

@Matthias Pospiech试图删除所有for循环之前,也许你可以看看我的评论#1 ....大多数时候这会阻止你使用OpenMP。 –

回答

4

部分)

你不因为你只在lambda代码使用参数(V)和全局(PI)需要在这里捕捉。

只有当lambda必须访问当前范围的变量(即在函数中声明)时才需要捕获。您可以通过参考(&)或值(=)进行捕获。

下面是其中需要“通过引用捕获”,因为“结果”拉姆达内被修改的实例(但它也捕获“searchValue”):

size_t count(const std::vector<char>& values, const char searchValue) 
{ 
size_t result = 0; 
std::for_each(values.begin(), values.end(), [&](const char& v) { 
    if (v == searchValue) 
    ++result; 
}); 
return result; 
} 

(在现实世界请使用std::count_if()或甚至std::count()

编译器为每个捕获lamda创建一个未命名的函子(请参阅this question)。该函数的构造函数接受参数并将其存储为成员变量。所以'按值捕获'总是使用元素在lambda定义时的值。

下面是一个代码示例中的编译器可以产生我们在前面创建的拉姆达:

class UnnamedLambda 
{ 
public: 
UnnamedLambda(size_t& result_, const char& searchValue_) 
    : result(result_), searchValue (searchValue_) 
{} 

void operator()(const char& v) 
{ 
    // here is the code from the lambda expression 
    if (v == searchValue) 
    ++result; 
} 

private: 
size_t& result; 
const char& searchValue; 
}; 

和我们的函数可以被改写为:

size_t count(const std::vector<char>& values, const char searchValue) 
{ 
size_t result = 0; 
UnnamedLambda unnamedLambda(result, searchValue); 
for(auto it = values.begin(); it != values.end(); ++it) 
    unnamedLambda(*it); 
return result; 
} 

第2部分)

如果您需要索引,请继续使用for循环。

std :: transform允许处理单个元素,因此不提供索引。还有一些像std :: accumulate这样的其他算法可以处理中间结果,但我不知道任何提供索引的算法。

0

下面是lambda captures一些例子:

[] Capture nothing

[&] Capture any referenced variable by reference

[=] Capture any referenced variable by making a copy

[=, &foo] Capture any referenced variable by making a copy, but capture variable foo by reference

[bar] Capture bar by making a copy; don't copy anything else

[this] Capture the this pointer of the enclosing class

因此,如果PI在你的例子是本地变量(不是宏或全局变量),你不能让[],但使用[ PI]通过复制捕获(这是理智的):

std::transform(dataPhase.begin(), dataPhase.end(), dataPhase.begin(), 
      [pi](double v){return fmod(v, pi*1.00001); } 
); 

你的第二个例子是没有内置的std :: TR回答提供索引。我认为更好的解决方案是保持你的循环。

如果你真的想使用拉姆达(作为一个练习),你可以使用:

const int halfsize = int(length/2); 

auto lambda=[&axis,halfsize](const int i){axis[i] = i - halfsize;}; 

for (size_t i=0; i < length; ++i) 
{ 
    lambda(i); 
} 

const int halfsize = int(length/2); 

auto lambda=[halfsize](const int i){return i - halfsize;}; 

for (size_t i=0; i < length; ++i) 
{ 
    axis[i] = lambda(i); 
} 

,只取决于你想如何设计你的代码。

备注1:似乎你想避免“基本”for循环,但是它们并不是必须的邪恶,特别是如果你想使用OpenMP来获得一些性能(simd或多线程)。例如

#pragma omp simd 
for(auto& v_i :v) { // or worst std::transform 
    v_i = fmod(v_i, pi*1.00001); 
} 

不支持,不会编译。

然而

#pragma omp simd 
for (size_t i=0; i < dataPhase.size(); ++i) 
{ 
    dataPhase[i] = fmod(dataPhase[i], pi*1.00001); 
}  

可以用G ++ -fopenmp编译...潜在PERF增益如果SIMD可以使用。关于多线程,人们可以争辩说,即将到来的支持STL算法的并行执行,但这只适用于C++ 17。

注2:在C++中,但在D language你有一个的foreach指令,让你选择包括指数:

foreach (e; [4, 5, 6]) { writeln(e); } 
// 4 5 6 

foreach (i, e; [4, 5, 6]) { writeln(i, ":", e); } 
// 0:4 1:5 2:6 
+0

我不明白为什么我应该捕捉pi。这是一个常数。它使用了但没有返回。而且因为它是一个不能修改的consant。如果我想修改pi,它会让它抓住它。 –

+0

如果你不使用π,你会得到一个编译器错误(对于gcc,像'error:'pi'没有被捕获)。即使没有修改,这个pi定义了lambda的“上下文”。如果你想将你的lambda复制到另一个你必须复制的地方** pi **。这个上下文对于执行** closure **操作是必需的 –

+0

如果pi是一个全局常量(声明在函数ar类之外)你不必捕获它(捕获)lambda就像是一个方法调用,只有当这个方法必须访问任何* local *变量时,你必须捕获它们,一个不捕获的lambda就像一个全局函数甚至可以在需要全局函数的地方使用(在当前的C++中)。 –

0

您所有的例子可以被转化为std::transform的使用,其中一些额外的对象做腿部练习(我在这里使用boost,因为它是m的现有技术所需的类)

// 1 
for (size_t i=0; i < dataPhase.size(); ++i) 
{ 
    dataPhase[i] = fmod(dataPhase[i], pi*1.00001); 
} 

// 2 
const int halfsize = int(length/2); 
for (size_t i=0; i < length; ++i) 
{ 
    axis[i] = int(i) - halfsize; 
} 

// 3 
for(std::vector<complex<double> >::size_type i = 0; i != data.size(); i++) { 
    dataAmplitude[i] = abs(data[i]); 
    dataPhase[i]  = arg(data[i]); 
} 

正如你正确注意的OST,1变成

std::transform(dataPhase.begin(), dataPhase.end(), dataPhase.begin(), 
    [](double v){return fmod(v, pi*1.00001); }); 

2需要数字序列,所以我用boost::integer_range

const int halfsize = int(length/2); 
// provides indexes 0, 1, ... , length - 1 
boost::integer_range<int> interval = boost::irange(0, length); 
std::transform(interval.begin(), interval.end(), axis.begin(), 
    [halfsize](int i) {return i - halfsize;}); 

3涉及一对( 2元组)输出,所以我用boost::zip_iterator作为目的地

std::transform(data.begin(), data.end(), 
    // turns a pair of iterators into an iterator of pairs 
    boost::make_zip_iterator(dataAmplitude.begin(), dataPhase.begin()), 
    [](complex<double> d) { return boost::make_tuple(abs(d), arg(d)); }); 
+0

好的,我有兴趣替换循环原因有两个:非常可读性和速度高数组(以及标准算法的未来多线程支持)。 1)看起来像是一个合理的替代品,但是2,尤其是3)缺乏可读性并且引入了不必要的复杂性。因此非常有趣,但没有考虑实施。 –

+0

对于2,你可以改为使用'boost :: irange(-halfsize,halfsize)'和'std :: copy',但是我用'std :: transform'表示了所有这些' – Caleth

+0

@MatthiasPospiech我不确定你正在决定“不必要的复杂性”,除了“引入第三方库”。可读性是主观的,你可以习惯“功能”风格 – Caleth