Решил тут заняться преждевременными оптимизациями. Очень часто во всяких численных методах используется конструкция вида:
f(x,y)=\sum_{i,j}{a_{x+i,y+j} p_{i,j}}, где a - сетка значений какой-то величины, p - константная матрица. И эта сумма обычно находится в самых узких местах программы.
Поскольку p известна заранее (на стадии компиляции), я решил заоптимизировать вычисление таким образом, что сумма не будет проходить по нулевым элементам и не делать умножение на 1 и -1.
Получение нужной функции:
{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies #-}
module Module1 where
import Language.Haskell.TH
import Data.VectorSpace
stencil2D :: (Ord b, Num b, VectorSpace a, b ~ Scalar a) => [[b]] -> ((Int,Int)->a) -> (Int, Int) -> a
stencil2D pat get =
let
lx = length $ head pat
ly = length pat
-- Zip indices and values:
i = [
((x', y'), a)
|(xx, y) <- zip pat [0..]
,(a, x) <- zip xx [0..]
,a /= 0
,let x' = x-(lx`div`2)
,let y' = y-(lx`div`2)
]
f r0 (r, a)
| a == 1 = (^+^)(get $ r^+^r0)
| a == -1 = (^-^)(get $ r^+^r0)
| True = (^+^)(a*^(get $ r^+^r0))
fun r = foldr (\b a->a.(f r b)) id i
in
\r -> fun r zeroV
Проверил бегло, вроде работает. Применение её в compile time и проверка:
-- Dummy get function:
fakeGet :: (Int, Int) -> Float
fakeGet _ = 1
foo = $([| stencil2D [[0,1,0],[1,-4,1],[0,1,0]] fakeGet |])
main = print $ foo (1,1)
Вопрос. (Моё знание и понимание template haskell и quasi-quotation на данный момент близко к нулю.) Действительно ли foo раскрутилась в функцию, с 5 операциями + и одним умножением? (zeroV^+^... я потом уберу).
Ну и прошу опытных людей заодно конструктивно поругать мой код в целом.