Мое приложение умножает векторы после (дорогостоящего) преобразования с использованием БПФ. В результате, когда я пишуЗапоминающее умножение
f :: (Num a) => a -> [a] -> [a]
f c xs = map (c*) xs
Я только хочу, чтобы вычислить БПФ c
один раз, а не для каждого элемента xs
. Там действительно нет необходимости хранить БПФ от c
для всей программы, только в локальной области.
я попытался определить мой Num
экземпляр как:
data Foo = Scalar c
| Vec Bool v -- the bool indicates which domain v is in
instance Num Foo where
(*) (Scalar c) = \x -> case x of
Scalar d -> Scalar (c*d)
Vec b v-> Vec b $ map (c*) v
(*) v1 = let Vec True v = fft v1
in \x -> case x of
Scalar d -> Vec True $ map (c*) v
v2 -> Vec True $ zipWith (*) v (fft v2)
Затем, в приложении, я вызываю функцию, аналогичную f
(который работает на произвольных Num
с), где c=Vec False v
, и я ожидал, что это будет быть столь же быстро, как если бы я взломать f
на:
g :: Foo -> [Foo] -> [Foo]
g c xs = let c' = fft c
in map (c'*) xs
функция g
делает запоминание из fft c
происходят, и мю ch быстрее, чем вызов f
(независимо от того, как я определяю (*)
). Я не понимаю, что происходит с f
. Является ли это моим определением (*)
в примере Num
? Имеет ли он какое-то отношение к f
, работающему над всеми Nums, и GHC, поэтому не может понять, как частично вычислить (*)
?
Примечание: Я проверил вывод ядра для моего экземпляра Num, и (*) действительно представлен как вложенные лямбды с преобразованием FFT на лямбда верхнего уровня. Таким образом, похоже, что это, по крайней мере, способно быть замеченным. Я также попытался как разумно, так и безрассудно использовать шаблоны ударов, чтобы попытаться заставить оценку не иметь никакого эффекта.
Как примечание стороны, даже если я могу понять, как сделать (*)
memoize его первый аргумент, есть еще одна проблема с тем, как это определено: Программист хочет использования тип данных Foo должен знать о это возможность memoization. Если она написали
map (*c) xs
никакой меморандума не возникнет. (Это должно быть написано как (map (c*) xs))
Теперь, когда я думаю об этом, я не совсем уверен, как GHC перепишет версию (*c)
, так как у меня есть (*)
. (c*)
делает c
первым аргументом *
, тогда как (*c)
делает c
вторым аргументом *
. Поэтому проблема заключается в том, что неясно, как следует писать умножение для обеспечения memoization. Является ли это просто неотъемлемым недостатком нотации infix (и неявное предположение, что аргументы *
являются симметричными)?
Вторая, менее актуальная проблема заключается в том, что корпус re отобразим (v *) в список скаляров. В этом случае (надеюсь), fft of v будет вычисляться и храниться, хотя это необязательно, так как другой множитель является скаляром. Есть ли способ обойти это?
Благодаря
компиляции с '-O2' и сравнительного анализа с скомпилированного кода? – jberryman
Да, но я проверял ядро, и, похоже, он ничего не компилирует. – crockeea
BTW, 'let c '= fft c на карте (c *) xs' does * not * вычисляет' fft', потому что Haskell ленив. 'c'' никогда не используется, поэтому он никогда не будет вычислен. – luqui