是否有类似 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::fillstd::fill_n 这样的函数,它们是用值填充指针数组、向量、std::arrays 和其他容器的便捷单线方法。一些容器也有自己的fill 方法来允许填充一个常数值。还有函数std::generate_nstd::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 的函数来用索引的某些函数填充数组?的主要内容,如果未能解决你的问题,请参考以下文章

VBA 代码填充 7 个相邻单元格中的索引匹配函数

JavaScript中是不是有类似forEach的函数,但它只返回索引,而不是对象和索引[关闭]

stl中是不是有类似execute_if算法的东西?

09 STL-函数对象

STL算法设计理念 - 函数对象和函数对象当參数和返回值

是否有类似拉链的功能可以填充到最长的长度?