pos显示ro什么意思(c++ 7种非修改性的range查询算法)

快鱼网 22 0

std::range算法简介。

range算法被定义在头中,而range的基础结构和核心类型被定义在头中。通常,range算法至少有两个重载:带有一对迭代器的重载和带有单一范围参数的重载。range版本采取 "投影",这有时允许更多的灵活性;例如,你可以针对一些选定的成员进行排序,或者在比较之前执行额外的转换。range版本没有并行执行选项(你不能传递std::执行策略)。range算法,与C++20的标准算法类似,也是constexpr。从C++20开始,没有对应于头的range数值算法。

下面,你可以找到显示标准算法和带有range的替代版本的例子。它们说明了一些基本概念,并尽量不使用高级的range组成或视图。我们将按照在cppreference/algorithms找到的顺序进行,在这一部分,我们将介绍 "非修改序列操作"。

1. all_of, any_of, none_of

标准算法

#include

#include

#include

#include

int main() {

const std::vector nums = {1, 2, 3, -4, 5, 6, 7, 8 };

auto is_positive = [](const auto& v) { return v > 0; };

// standard version:

auto res = std::all_of(begin(nums), end(nums), is_positive);

std::cout << "std::all_of: " << res << '\n';

res = std::any_of(begin(nums), end(nums), is_positive);

std::cout << "std::any_of: " << res << '\n';

}

range版本

// ranges version:

res = std::ranges::all_of(nums, is_positive);

std::cout << "std::ranges::all_of: " << res << '\n';

res = std::ranges::any_of(nums, is_positive);

std::cout << "std::ranges::any_of: " << res << '\n';

我们还可以写一个更复杂的例子,扫描一个自定义类型的容器。

#include

#include

#include

#include

struct Product {

std::string name_;

double value_ { 0.0 };

};

int main() {

const std::vector prods {

{ "box", 10.0 }, {"tv", 100.0}, {"none", -1.0}

};

auto is_positive = [](const auto& v) { return v > 0; };

auto is_positive_val = [](const Product& p) {

return p.value_ > 0;

};

// standard version:

auto res = std::all_of(begin(prods), end(prods), is_positive_val);

std::cout << "std::all_of: " << res << '\n';

res = std::any_of(begin(prods), end(prods), is_positive_val);

std::cout << "std::any_of: " << res << '\n';

// ranges version:

res = std::ranges::all_of(prods, is_positive, &Product::value_);

std::cout << "std::ranges::all_of: " << res << '\n';

res = std::ranges::any_of(prods, is_positive, &Product::value_);

std::cout << "std::ranges::any_of: " << res << '\n';

}

在range版本中,我们仍然可以使用is_positive,一个通用的谓词,但我使用了一个投影,它只 "接受 "Product::value_并将其传入谓词。在标准情况下,我不得不写一个自定义的lambda来识别Product的类型。

2. for_each

一个基于范围的for循环的替代方案。

#include

#include

#include

#include

struct Product {

std::string name_;

double value_ { 0.0 };

};

int main() {

const std::vector prods {

{ "box", 10.0 }, {"tv", 100.0}, {"none", -1.0}

};

auto out = [](const auto& v) { std::cout << v << ", "; };

// standard version:

std::cout << "std::for_each: \n";

std::for_each(begin(prods), end(prods), [](const Product& p){

std::cout << p.name_ << ", " << p.value_ << '\n';

});

std::cout << "std::for_each only names reverse: \n";

std::for_each(rbegin(prods), rend(prods), [](const Product& p){

std::cout << p.name_ << '\n';

});

// ranges version:

std::cout << "std::ranges::for_each: \n";

std::ranges::for_each(prods, [](const Product& p) {

std::cout << p.name_ << ", " << p.value_ << '\n';

});

std::cout << "std::ranges::for_each only names in reverse: \n";

std::ranges::for_each(prods | std::views::reverse,

out, &Product::name_);

}

令人振奋的是,在标准版本中以反向顺序打印需要使用rbegin/rend迭代器,然后使用一个自定义的单选函数来打印来自Product类的确切数据成员。而对于range,我们可以应用views::reverse,使用一个简单的输出函数,然后是一个投影。

缺少的是range算法的并行版本。

// standard:

std::for_each(std::execution::par, begin(prods), end(prods), /*...*/);

// no ranges version...

// std::ranges::for_each(std::execution::par, prods, /*... */); // doesn't compile...

所有的range算法都缺少并行版本,而不仅仅是for_each。

3. count_if

在下面的例子中,我们将计算名称以 "no "开头的Product。

#include

#include

#include

#include

struct Product {

std::string name_;

double value_ { 0.0 };

};

int main() {

const std::vector prods {

{ "box", 10.0 }, {"tv", 100.0}, {"none", -1.0},

{ "car", 1000.0 }, {"toy", 40.0}, {"none", 0.0}

};

// standard version:

auto res = std::count_if(begin(prods), end(prods), [](const Product& p){

return p.name_.starts_with("no");

});

std::cout << "std::count_if: " << res << '\n';

// ranges version:

res = std::ranges::count_if(prods, [](const Product& p) {

return p.name_.starts_with("no");

});

std::cout << "std::ranges::count_if: " << res << '\n';

// alternative version for "none":

res = std::ranges::count(prods, std::string{"none"}, &Product::name_);

std::cout << "std::ranges::count: " << res << '\n';

}

这个例子显示了三种方法,最后一种使用了一个投影,只检查Product::name_数据成员。在这个方法中,我们精确地搜索 "none",所以它比starts_with更严格。

4. find_if

到目前为止,我们的文本算法都是返回布尔值或积分值,但有了find*函数,我们就有了显示更多内容的迭代器(或子range)。

请看这个例子。

#include

#include

#include

#include

struct Product {

std::string name_;

double value_ { 0.0 };

};

int main() {

const std::vector prods {

{ "box", 10.0 }, {"tv", 100.0}, {"rocket", .0},

{ "car", 1000.0 }, {"toy", 40.0}, {"none", 0.0}

};

// standard version:

auto it = std::find_if(begin(prods), end(prods), [](const Product& p){

return p.name_.starts_with("ro");

});

if (it != end(prods))

std::cout << "std::find_if: " << it->name_ << '\n';

// ranges version:

auto res = std::ranges::find_if(prods, [](const Product& p) {

return p.name_.starts_with("ro");

});

if (res != end(prods))

std::cout << "std::ranges::find_if: " << res->name_ << '\n';

}

像其他算法一样,也有一个 "常规 "版本,你可以传递两个迭代器。

it = std::ranges::find_if(begin(prods), end(prods), [](const Product& p) {

return p.name_.starts_with("ro");

});

采取单一范围的版本是特殊的,因为它返回一个借用的迭代器。这种特殊类型允许检查临时/生命期对象的问题。当你传递两个迭代器时,这是不可能的(因为容器在某处存在),但用一个临时范围就可以。

struct Product {

std::string name_;

double value_ { 0.0 };

};

std::vector GetProds() {

return {

{ "box", 10.0 }, {"tv", 100.0}, {"rocket", .0},

{ "car", 1000.0 }, {"toy", 40.0}, {"none", 0.0}

};

}

int main() {

auto it = std::ranges::find_if(GetProds(), [](const Product& p) {

return p.name_.starts_with("ro");

});

std::cout << "std::ranges::find_if: " << it->name_ << '\n';

}

这不能编译,你会看到以下错误。

error: base operand of '->' has non-pointer type 'std::ranges::dangling'

22 | std::cout << "std::ranges::find_if: " << it->name_ << '\n';

| ^~

正如你所看到的,编译器检查了GetProds()返回一个临时的值,而我们会发现的迭代器是悬空的。

5. find_first_of

让我们来看看另一个find*函数的替代方案,它可以一次搜索多个元素。

#include

#include

#include

#include

struct Product {

std::string name_;

double value_ { 0.0 };

friend bool operator==(const Product& a, const Product& b) {

return a.name_ == b.name_ && abs(a.value_ - b.value_) < 0.0001;

}

};

int main() {

const std::vector prods {

{ "box", 10.0 }, {"default", 0.0 }, {"tv", 100.0}, {"rocket", .0},

{ "car", 1000.0 }, {"toy", 40.0}, {"none", 0.0 }, { "ball", 40.0 }

};

const std::vector invalids {

{"default", 0.0 }, {"none", 0.0 }

};

// standard version:

auto it = std::find_first_of(begin(prods), end(prods), begin(invalids), end(invalids));

if (it != end(prods)) {

std::cout << "std::find_first_of: " << it->name_ << " at: "

<< std::distance(begin(prods), it) <<'\n';

auto it2 = std::find_first_of(std::next(it), end(prods), begin(invalids), end(invalids));

if (it2 != end(prods))

std::cout << "std::find_first_of: " << it2->name_ << " at: "

<< std::distance(begin(prods), it2) <<'\n';

}

// ranges version:

const std::array arrInvalids{"default", "none"};

auto res = std::ranges::find_first_of(prods, arrInvalids,

std::ranges::equal_to{}, &Product::name_);

if (res != end(prods)) {

const auto pos = std::distance(begin(prods), res);

std::cout << "std::ranges::find_first_of: " << res->name_

<< " at: " << pos <<'\n';

auto res2 = std::ranges::find_first_of(prods | std::views::drop(pos+1), arrInvalids,

std::ranges::equal_to{}, &Product::name_);

if (res2 != end(prods)) {

std::cout << "std::ranges::find_first_of: " << res2->name_

<< " at: " << std::distance(begin(prods), res2) <<'\n';

}

}

}

std::find_first_of需要两对迭代器。我想在例子中的prod序列中找到 "无效的 "product。因为我在比较product,所以我必须为我的结构定义operator==。另外,我可以提供一个二进制操作,然后只比较名称。

auto cmpNames = [](const Product& a, const Product& b) {

return a.name_ == b.name_;

};

auto it = std::find_first_of(begin(prods), end(prods),

begin(invalids), end(invalids), cmpNames);

if (it != end(prods)) {

// ...

}

在range版中,我可以使用预测和默认比较器来达到类似的效果。

const std::array arrInvalids{"default", "none"};

auto res = std::ranges::find_first_of(prods, arrInvalids,

std::ranges::equal_to{}, &Product::name_);

后来有趣的部分是,对于第二次搜索,我可以使用drop来跳过范围中的前N个元素。

auto res2 = std::ranges::find_first_of(prods | std::views::drop(pos+1),

arrInvalids, std::ranges::equal_to{}, &Product::name_);

另外,你也可以使用一个有两对迭代器的版本。

auto res2 = std::ranges::find_first_of(std::next(res), end(prods),

begin(arrInvalids), end(arrInvalids),

std::ranges::equal_to{}, &Product::name_);

6. mismatch

通过不匹配算法,我们可以找到两个范围不同的第一个地方。

#include

#include

#include

#include

#include // quoted

int main() {

const std::string firstStr = "Hello Super World";

const std::string secondStr = "Hello Amazing World";

std::cout << "mismatch for " << std::quoted(firstStr)

<< " and " << std::quoted(secondStr) << '\n';

// standard version:

auto [first, second] = std::mismatch(begin(firstStr), end(firstStr), begin(secondStr));

{

const auto pos = std::distance(begin(firstStr), first);

std::cout << "std::mismatch: at pos " << pos << '\n';

}

// ranges version:

auto res = std::ranges::mismatch(firstStr, secondStr);

{

const auto pos = std::distance(begin(firstStr), res.in1);

std::cout << "std::ranges::mismatch: at pos " << pos << '\n';

}

}

range版本返回:

template

using mismatch_result = ranges::in_in_result;

这是一对两个迭代器,但我们可以通过.in1和.in2访问它们。

为什么不是一个简单的范围呢?在cpp参考中我们可以看到下面这句话。

Unlike std::pair and std::tuple, this class template has data members of meaningful names.

其结果在结构化绑定时表现不错,所以你可以写。

auto [n1, n2] = std::ranges::mismatch(firstStr, secondStr);

const auto pos = std::distance(begin(firstStr), n1);

std::cout << "std::ranges::mismatch: at pos " << pos << '\n';

代码几乎与标准版相同。

7. search

在另一个范围/容器中搜索模式。

#include

#include

#include

#include

#include // searchers

#include

int main() {

const std::string testString = "Hello Super World";

const std::string needle = "Super";

std::cout << "looking for " << std::quoted(needle)

<< " in " << std::quoted(testString) << '\n';

// standard version:

auto it = std::search(testString.begin(), testString.end(),

std::boyer_moore_searcher(needle.begin(), needle.end()));

if (it != testString.end()) {

const auto pos = std::distance(testString.begin(), it);

std::cout << "std::search: found at pos " << pos << '\n';

}

// ranges version:

auto res = std::ranges::search(testString, needle);

if (!res.empty()) {

const auto first = std::distance(testString.begin(), res.begin());

const auto last = std::distance(testString.begin(), res.end());

std::cout << "std::ranges::search: found between "

<< first << " and " << last << '\n';

}

}

标准版本返回第二个字符串开始的第一个字符串的迭代器(如果不在那里,则返回end())。而range版本则返回一个子range(或一个借用的子range)。

我们也可以使用投影以不区分大小写的方式进行检查。

// ranges version:

const std::string testString2 = "hello abc world";

const std::string needle2 = "ABC";

std::cout << "looking for " << std::quoted(needle2) << " in "

<< std::quoted(testString2) << '\n';

res = std::ranges::search(testString2, needle2,

std::ranges::equal_to{}, ::toupper, ::toupper);

if (!res.empty())

{

const auto first = std::distance(testString2.begin(), res.begin());

const auto last = std::distance(testString2.begin(), res.end());

std::cout << "std::ranges::search: found between "

<< first << " and " << last << '\n';

}

另一个函数 ranges::search_n可以方便地在输入范围内找到一个给定值的N个出现次数。

#include

#include

#include

#include

int main() {

const std::string sequence = "CTGCCCAGGGTTT";

const char letter = 'C';

const size_t count = 3;

std::cout << "looking for " << count << " "

<< letter << "'s in " << std::quoted(sequence) << '\n';

// standard version:

auto it = std::search_n(begin(sequence), end(sequence), count, letter);

if (it != end(sequence))

{

const auto pos = std::distance(begin(sequence), it);

std::cout << "std::search_n: found at pos " << pos << '\n';

}

// ranges version:

auto res = std::ranges::search_n(sequence, count, letter);

if (!res.empty())

{

const auto first = std::distance(begin(sequence), res.begin());

const auto last = std::distance(begin(sequence), res.end());

std::cout << "std::ranges::search_n: found between "

<< first << " and " << last << '\n';

}

}

在标准版本中,没有特殊的搜索器;你只能使用并行算法来调用它。

总结

在这篇文章中,我们涵盖了非修改性操作类别中的七种不同的算法 "类型":检查所有/无/部分元素的一些谓词,搜索,查找,一般迭代。总共有超过10个不同的例子。

ranges算法提供了一种更简单的方法来传递 "整个 "容器--只有一个参数,而不是传递给迭代器。它们也允许投影,并且有办法检测到临时范围的迭代器。它们也有局限性,比如缺乏高级搜索器或并行执行模式。

标签: 算法

抱歉,评论功能暂时关闭!