C++ C++;循环边的习惯用法

C++ C++;循环边的习惯用法,c++,loops,for-loop,design-patterns,C++,Loops,For Loop,Design Patterns,问题1:假设您有一个由n个浮点组成的数组,并且您想要计算一个由n个运行平均数组成的数组,该数组覆盖三个元素。中间部分很简单: for (int i=0; i<n; i++) b[i] = (a[i-1] + a[i] + a[i+1])/3. 即使这样也不够,因为不幸的是,对于n的情况,答案是否定的。 没有C++习惯用法,概括地说,这样的循环模式是以干净的方式包装的!p> 您可以通过制作这样的东西来实现,但仍然需要调整窗口大小 template <typename T, i

问题1:假设您有一个由n个浮点组成的数组,并且您想要计算一个由n个运行平均数组成的数组,该数组覆盖三个元素。中间部分很简单:

for (int i=0; i<n; i++)
    b[i] = (a[i-1] + a[i] + a[i+1])/3.

即使这样也不够,因为不幸的是,对于n的情况,答案是否定的。
没有C++习惯用法,概括地说,这样的循环模式是以干净的方式包装的!p> 您可以通过制作这样的东西来实现,但仍然需要调整窗口大小

template <typename T, int N> 
T subscript(T (&data)[N], int index) {
    if (index < 0 || index >= N) {
        return 0;
    }
    
    return data[index];
}

for (int i = 0; i < n; ++i) {
    b[i] = (subscript(a, i - 1) + subscript(a, i) + subscript(a, i + 1)) / 3.
}
模板
T下标(T(&data)[N],整数索引){
如果(索引<0 | |索引>=N){
返回0;
}
返回数据[索引];
}
对于(int i=0;i< /代码> 

高效而优雅地处理边界条件在任何编程语言中都是麻烦的——C++没有魔法锤。这是对信号/图像应用卷积滤波器的一个常见问题——在内核超出图像支持范围的图像边界处,您会做什么

通常有两件事是你试图避免的:

  • 越界数组索引(必须避免),以及
  • 特殊计算(这是不雅观的,由于额外的分支,导致代码速度变慢)
通常有三种方法:

  • 避免边界——这是最简单的方法,通常已经足够了,因为边界情况只占问题的一小部分,可以忽略
  • 扩展缓冲区的边界——向数组中添加额外的填充列/行,以便在边缘使用与一般情况相同的代码。当然,这会引起在填充中放置什么值的问题——这通常取决于您正在解决的问题,并在下一种方法中考虑
  • 边界处的特殊计算——这就是您在示例中所做的。当然,如何做到这一点取决于问题,并引发了与前一种方法类似的问题——当我的过滤器(在您的例子中是平均过滤器)扩展到阵列支持之外时,正确的做法是什么?我应该考虑数组支持之外的值吗?大多数图像过滤器库--例如:
    • 假设值为零或某个其他常数(如果
      i<0 | i>=n
      ,则定义
      a[i]=0
    • 复制边界值(例如
      a[i]=a[0]
      if
      i<0
      a[i]=a[n-1]
      if
      i>=n
    • 包装值(定义
      a[i]=a[(i+n)%n]
      ——在某些情况下有意义——例如,纹理过滤器)
    • 镜像边框((例如
      a[i]=a[abs(i+1)]
      if
      i<0
      a[i]=a[2n-i-1]
      if
      i>=n
    • 其他特殊情况(您所做的)
在合理的情况下,最好将特殊情况与一般情况分开(就像您所做的那样),以避免不雅观和缓慢的一般情况。可以始终在函数或运算符中包装/隐藏特殊情况和一般情况(例如,重载
运算符[]
)但是,这种唯一的糖可以像任何设计的C++习惯一样解决问题。在多线程环境(例如CUDA/SIMD)中,你可以做一些其他的预装出界限值的技巧,但是你仍然会遇到同样的问题。
这就是为什么程序员在引用任何类型的特殊情况编程时使用短语“edge case”的原因,它通常是一个时间接收器和恼人错误的来源。一些有效支持异常处理的语言(例如Ada)可以生成更漂亮的代码,但仍然会造成同样的痛苦。

最简单的方法是移动(非运行)平均数数组的元素数少于输入数(考虑到在边缘,无法使用相同数量的元素计算平均数,因此不要计算它们)。如果需要平均数数组具有相同的元素数,则需要对平均数据的性质进行一些假设。这是几种可能的解决方案之一(例如,您也可以通过多种方式填充数组),但这只是一个简单的例子。我正在寻找解决这类问题的通用方法。@Zzyzx-没有解决这类问题的通用方法。是的,有几种方法可以使用填充。在计算移动平均值之前,也可以使用某种形式的过滤。所有方法都涉及到做一些调整关于输入数据(或生成数据的过程)特征的假设.但并没有一些通用/通用的方法适用于每种情况。当然。我希望的是以某种方式清晰地封装边缘行为,同时最大限度地减少索引计算错误的可能性,并生成有效的代码。我想可能是对
范围
的某种泛化,这将w表示实际存在元素之外的“假”迭代器。谢谢,“边界”是更好的术语。在一些HPC应用程序(有限元素?)中,边界不能被忽略(它们是区域使用彼此数据的地方)这就是事情变得特别混乱的地方。我想知道是否有任何元编程工具可以以一种通用的方式处理这个问题。
template <typename T, int N> 
T subscript(T (&data)[N], int index) {
    if (index < 0 || index >= N) {
        return 0;
    }
    
    return data[index];
}

for (int i = 0; i < n; ++i) {
    b[i] = (subscript(a, i - 1) + subscript(a, i) + subscript(a, i + 1)) / 3.
}