是否有类似 STL 的函数来用索引的某些函数填充数组?
Posted
技术标签:
【中文标题】是否有类似 STL 的函数来用索引的某些函数填充数组?【英文标题】:Is there an STL-like function to fill an array with some function of the index? 【发布时间】:2019-11-03 20:41:07 【问题描述】:在 C++ 中,我们有像 std::fill
或 std::fill_n
这样的函数,它们是用值填充指针数组、向量、std::array
s 和其他容器的便捷单线方法。一些容器也有自己的fill
方法来允许填充一个常数值。还有函数std::generate_n
和std::iota
,前者允许使用生成器函数填充元素,后者使用索引填充范围。
我正在寻找的是一个类似的解决方案 - 最好是单线解决方案并在标准库中定义 - 允许使用索引的某些功能填充容器。例如,这将是一个数组的解决方案:
std::array<int, 100> arr;
for (std::size_t i = 0; i < 100; i++)
arr[i] = f(i);
其中f(std::size_t i)
是索引的某个函数。
有没有集成的方法来做到这一点?
【问题讨论】:
你为什么不使用 std::generate_n ?这正是你想要的 这是你想要的吗? wandbox.org/permlink/tAwaouhYJtacJNjt @Oblivion 这将是一个解决方案,但是,我正在寻找不依赖静态变量的更通用的东西。例如接受 lambda 表达式的函数。 【参考方案1】:您可以使用有状态的 lambda:
std::array<int, 100> arr;
std::generate(arr.begin(), arr.end(), [i = std::size_t(0)]() mutable return f(i++););
但我认为它使代码比它需要的更复杂。使用普通循环可能是最好的选择。
【讨论】:
【参考方案2】:我无法使用编译器尝试此操作,因为我找不到具有 C++20 范围的编译器,但是一旦编译器实现它们,这样的东西应该可以工作:
std::ranges::transform(std::views::iota1, arr.size()+1, arr, f);
或者,如果您只想要生成值的序列,只需使用
创建它们的范围auto const values = std::views::iota1, arr.size()+1 | std::views::transform(f);
在您拥有 C++20 编译器之前,您可以使用其中一个范围库,例如在Boost 或Eric Nieblers ranges-v3
【讨论】:
如果目的地确实需要是std::array
,但是缺少一个步骤:将视图转换为数组。
甚至 gcc.godbolt.org 似乎都不知道这一点,这是新的:( gcc.godbolt.org/z/pgh7uS【参考方案3】:
考虑为此编写自己的类似STL的函数模板apply_idx_func()
:
template<typename FwdItor, typename F>
void apply_idx_func(FwdItor first, FwdItor last, F f)
for (size_t idx = 0; first != last; ++first, ++idx)
*first = f(idx);
举个例子:
auto main() -> int
std::array<int, 10> arr;
// just adds 100 to the index
auto func = [](size_t idx) -> int
return 100 + idx;
;
apply_idx_func(std::begin(arr), std::end(arr), func);
for (auto elem: arr)
std::cout << elem << ' ';
std::cout << '\n';
输出是:
100 101 102 103 104 105 106 107 108 109
【讨论】:
是否有一些关于“在哪里”放置自定义扩展的建议?例如哪个命名空间? @HerpDerpington 您可以创建自己的命名空间,例如:namespace xtd ...
(xtd 代表 eXtended sTandarD,与 @987654326 形成对比@命名空间)。【参考方案4】:
如果你总是遍历整个数组,你甚至不必使用任何 stl 函数,只需使用范围循环:
std::array<int, 100> arr;
int idx = 0;
for (auto& item : array)
item = f(idx++);
另一种选择是使用for_each
(如果将来您想仅填充部分数组,这可能会更有用)
#include <algorithm>
std::array<int, 100> arr;
int idx = 0;
std::for_each(begin(arr), end(arr), [&idx](int &n) n = f(idx++); );
不幸的是,在这两种情况下,您都必须有单独的索引变量(这里:idx
)
【讨论】:
【参考方案5】:#include <algorithm>
size_t i = 0;
std::generate_n(arr.begin(),arr.size(),[&i]()return f(i++););
或者可能分两步:
#include <numeric>
std::iota(arr.begin(),arr.end(),0);
std::transform(arr.begin(),arr.end(),f);
在 boost 的帮助下,我们可以一步完成:
std::transform(
arr.begin(),
arr.end(),
boost::irange(0,arr.size()),
[](const auto& val,const auto& index)return f(index);
);
https://www.boost.org/doc/libs/1_64_0/libs/range/doc/html/range/reference/ranges/irange.html
【讨论】:
以上是关于是否有类似 STL 的函数来用索引的某些函数填充数组?的主要内容,如果未能解决你的问题,请参考以下文章