在Haskell的惯用矢量代数

时间:2015-06-04 03:28:05

标签: haskell

作为在Haskell中练习矢量库的一种方法,我试图重写我以前用C编写的Nelder-Mead最小化算法。 到目前为止,我在习惯性地翻译某些向量操作时遇到了一些麻烦。

例如,考虑一个函数,它从n + 1列表中找出n个向量的质心(过滤掉一个索引),

在C中,这可以写成

static void get_centroid(double **s, int n, int iz,
                         double *C)
{
  for (int i = 0; i < n+1; i++) {
    if (i != iz) {
      for (int j = 0; j < n; j++)
        C[j] += s[i][j];
    }
  }
  for (int j = 0; j < n; j++)
    C[j] /= n;
}

我尝试将其翻译成Haskell,最后得到以下内容

import Data.Vector
import qualified Data.Vector as V

type Node = Vector Double

type Simplex = Vector Node

centroid :: Simplex -> Int -> Node
centroid s iz = V.map (/ (fromIntegral $ V.length s)) $ V.zipWith (-) v (s ! iz) 
    where v = V.foldl go V.empty s 
        where go a b = V.zipWith (+) a b

我发现这段代码非常不优雅,因为它没有捕捉到正在发生的向量代数的本质(而且由于我加入和减去S [iz],因此效率也更低) 。

一种解决方案是实现某种向量空间类型类或使用更具体的线性代数库,但由于这些是常见的操作,我想知道是否有一个更惯用的直接&#39;溶液

1 个答案:

答案 0 :(得分:4)

我从dfeuer开始+1;一个更具体的图书馆几乎肯定会更清洁,更有效率。

但是,如果您正在寻找的是centroid函数的更惯用的实现,我喜欢这个:

centroid' :: Simplex -> Int -> Node
centroid' s iz = let t = foldl1 (V.zipWith (+)) (V.drop iz s)
                     n = fromIntegral (V.length t - 1)
                 in V.map (/ n) t

对您的版本的一般评论:创建“只写”Haskell代码非常容易。在你的第一行中有很多事情要进行解析。你的where块正朝着正确的方向前进,但我会更进一步突破概念组件。

此外,Hoogle。我不知道有一个函数drop,但我知道如果它存在,它会将IntVector放到新的Vector上。 Hoogle不会将Vector编入索引,但vector的API与列表的API非常相似。我搜索了“[a] - &gt; Int - &gt; [a]”和“Int - &gt; [a] - &gt; [a]”并找到drop

Stackage索引Vector,因此搜索“Int - &gt; Vector a - &gt; Vector a”在那里工作)