在Haskell的惯用矢量代数

Fel*_*cob 12 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;
}
Run Code Online (Sandbox Code Playgroud)

我尝试将其翻译成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
Run Code Online (Sandbox Code Playgroud)

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

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

Mat*_*att 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
Run Code Online (Sandbox Code Playgroud)

对您的版本的一个一般评论:创建“只写”Haskell 代码非常容易。第一行发生了太多事情,很难解析。您的where块朝着正确的方向迈出了一步,但我会进一步分解概念组件。

还有,胡格尔。我不知道存在一个函数drop,但我知道如果它存在,它会将 anInt和 aVector放到一个新的Vector. Hoogle 不索引Vector,但向量的 API 与列表的 API 非常相似。我搜索了“[a] -> Int -> [a]”和“Int -> [a] -> [a]”并找到了drop

Stackage确实有索引Vector,因此搜索“Int -> Vector a -> Vector a”可以在那里工作)