Идиоматическая векторная алгебра в Хаскелле
Как способ практиковать с векторной библиотекой в 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"