我在Haskell中实现了FIR滤波器。我对FIR滤波器知之甚少,而且我的代码很大程度上基于现有的C#实现。因此,我觉得我的实现有太多的C#风格,并不像Haskell那样。我想知道是否有更惯用的Haskell方式来实现我的代码。理想情况下,我很幸运能够实现算法的高阶函数(map,filter,fold等)的组合。
我的Haskell代码如下所示:
var allPanels = $(".panel");
allPanels.hide(2000);
allPanels.stop().show();
此代码基于以下C#代码:
applyFIR :: Vector Double -> Vector Double -> Vector Double
applyFIR b x = generate (U.length x) help
where
help i = if i >= (U.length b - 1) then loop i (U.length b - 1) else 0
loop yi bi = if bi < 0 then 0 else b !! bi * x !! (yi-bi) + loop yi (bi-1)
vec !! i = unsafeIndex vec i -- Shorthand for unsafeIndex
正如您所看到的,它几乎是一个直接的副本。如何将我的实现转变为更像Haskell的实现呢?你有什么想法?我唯一能想到的就是使用public float[] RunFilter(double[] x)
{
int M = coeff.Length;
int n = x.Length;
//y[n]=b0x[n]+b1x[n-1]+....bmx[n-M]
var y = new float[n];
for (int yi = 0; yi < n; yi++)
{
double t = 0.0f;
for (int bi = M - 1; bi >= 0; bi--)
{
if (yi - bi < 0) continue;
t += coeff[bi] * x[yi - bi];
}
y[yi] = (float) t;
}
return y;
}
。
我知道Vector.generate
库有一个可用的实现。但它使用列表,对我的用例来说太慢了。此DSP
实施比Vector
中的实施快得多。
我也尝试使用DSP
实现算法。它比Repa
实施更快。结果如下:
Vector
答案 0 :(得分:2)
首先,我不认为你的初始矢量代码是忠实的翻译 - 也就是说,我认为它不同意C#代码。例如,假设“x”和“b”(C#中的“b”为coeff
)长度为3,并且所有值均为1.0
。然后,对于y[0]
,C#代码将生成x[0] * coeff[0]
或1.0
。 (对于continue
)
bi
但是,使用您的Haskell代码,help 0
会生成0.您的Repa
版本似乎遇到了同样的问题。
让我们从一个更忠实的翻译开始:
applyFIR :: Vector Double -> Vector Double -> Vector Double
applyFIR b x = generate (U.length x) help
where
help i = loop i (min i $ U.length b - 1)
loop yi bi = if bi < 0 then 0 else b !! bi * x !! (yi-bi) + loop yi (bi-1)
vec !! i = unsafeIndex vec i -- Shorthand for unsafeIndex
现在,你基本上是在进行计算这样的计算,比如y[3]
:
... b[3] | b[2] | b[1] | b[0]
x[0] | x[1] | x[2] | x[3] | x[4] | x[5] | ....
multiply
b[3]*x[0]|b[2]*x[1] |b[1]*x[2] |b[0]*x[3]
sum
y[3] = b[3]*x[0] + b[2]*x[1] + b[1]*x[2] + b[0]*x[3]
因此,考虑你正在做的事情的一种方法是“取b
向量,反转它,并计算结果的点i
,行b[0]
向上{ {1}},将所有相应的x[i]
和x
条目相乘,并计算总和“。
让我们这样做:
b