我刚刚尝试用Haskell重写一些代码,原本是一个简短的Javascript函数。原始的有2个嵌套循环,内部循环包含两个循环计数器的相等性检查:
function f(x, points){
var i, j;
var n = points.length;
var result = 0;
for(i=0; i<n; i++){
var xprod = 1;
for(j=0; j<n; j++){
if(j != i){
xprod *= (x - points[j][0]);
}
}
result += points[i][1] * xprod;
}
return result;
}
我希望能够在Haskell中进行简化,但是我无法弄清楚如何在不有效地递归写出原始步骤的情况下掌握i和j值。在Javascript中,Array.map将列表位置作为第二个参数传递到回调函数中,但是似乎Haskell中的map不会执行此操作。我当前的Haskell版本对我来说很糟糕,因为我要传递2个数组副本(每个循环一个):
xproduct :: Int -> Int -> Double -> [(Double,Double)] -> Double
xproduct _ _ _ [] = 1
xproduct i j x (pt:todo)
| i == j = (xproduct i (j+1) x todo)
| otherwise = (xproduct i (j+1) x todo) * (x - (fst pt))
solvestep :: Int -> Double -> [(Double,Double)] -> [(Double,Double)] -> Double
solvestep _ _ _ [] = 0
solvestep i x pts (pt:todo) = ((snd pt) * xprod) + (solvestep (i+1) x pts todo)
where xprod = xproduct i 0 x pts
solve :: Double -> [(Double,Double)] -> Double
solve x points = solvestep 0 x points points
有更好的方法吗?
答案 0 :(得分:7)
如果可能的话,我通常完全避免使用任何索引。在这种情况下,您真正使用的是:列表中的任何一个元素都带有所有其他元素。无需使用索引比较来表达这一点,而是编写一个函数,该函数将使您在列表中有一个合适的外观:
pickouts :: [a] -> [(a,[a])]
pickouts [] = []
pickouts (x:xs) = (x,xs) : (second (x:) <$> pickouts xs)
然后,实际的计算就变为
f :: Double -> [(Double,Double)] -> Double
f x points = sum [q * product [x-p | (p,_)<-ps] | ((_,q),ps) <- pickouts points]