Идиоматическая векторная алгебра в Хаскелле

Как способ практиковать с векторной библиотекой в ​​Haskell, я пытаюсь переписать алгоритм минимизации Nelder-Mead, который я ранее написал на C. До сих пор у меня было немного проблем с переводом некоторых векторных операций идиоматически.

Например, рассмотрим функцию, которая находит центроид из n векторов из списка n + 1 (отфильтровывая один индекс),

В 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]).

Одним из решений было бы реализовать какой-то тип векторного пространства или использовать более конкретную библиотеку линейных алгебр, но поскольку это такие общие операции, мне было интересно, существует ли более идиоматическое "прямое" решение.

Ответ 1

Я бы начал с +1 для dfeuer; более конкретная библиотека почти наверняка будет более чистой и эффективной.

Однако, если вы ищете более идиоматическую реализацию вашей функции 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, но я знал, что если бы она существовала, ей понадобилось Int и Vector на новый Vector. Hoogle не индексирует Vector, но API для вектора очень похож на API для списков. Я искал "[a] → Int → [a]" и "Int → [a] → [a]" и нашел drop.

(Stackage индексирует Vector, поэтому выполняется поиск по "Int → Vector a → Vector a"