访问Haskell中诸如map之类的函数内部的列表位置

时间:2019-04-26 19:43:26

标签: haskell

我刚刚尝试用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

有更好的方法吗?

1 个答案:

答案 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]