我的应用程序在使用FFT进行(昂贵的)转换后对向量进行乘法运算。因此,当我写下时,
我只想计算一次 c 的FFT,而不是每个 xs 元素都要计算。 实际上没有必要为整个程序存储 c 的FFT,只需要在本地作用域中存储即可。
我尝试定义我的
f :: (Num a) => a -> [a] -> [a]
f c xs = map (c*) xs
我只想计算一次 c 的FFT,而不是每个 xs 元素都要计算。 实际上没有必要为整个程序存储 c 的FFT,只需要在本地作用域中存储即可。
我尝试定义我的
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
的记忆化,比调用f
要快得多(无论我如何定义(*)
)。我不明白f
出了什么问题。是我在Num
实例中定义(*)
的问题吗?它是否与f
在所有数字上运行有关,因此GHC无法确定如何部分计算(*)
?
注意:我检查了我的Num实例的核心输出,并且(*)确实表示为带有FFT转换的嵌套lambda在顶层lambda中。因此,看起来至少可以对其进行记忆化。我还尝试过精明和鲁莽地使用爆炸模式来尝试强制评估,但没有效果。
另外,即使我能够找出如何使(*)
记忆化其第一个参数,它的定义仍然存在另一个问题:想要使用Foo数据类型的程序员必须知道这种记忆化能力。如果她写了
map (*c) xs
没有记忆化会发生。(必须写成(map (c*) xs)
)。现在我想起来了,我不确定 GHC 会如何重写 (*c)
版本,因为我已经柯里化了 (*)
。但我进行了一个快速测试,验证了 (*c)
和 (c*)
都能按预期工作:(c*)
将 c
作为第一个参数传递给 *
,而 (*c)
将 c
作为第二个参数传递给 *
。所以问题是,要确保记忆化,应该如何编写乘法并不明显。这只是中缀符号(以及假设 *
的参数是对称的)固有的缺点吗?
第二个问题是,当我们将 (v*) 映射到一组标量时。在这种情况下,(希望)会计算和存储 v 的 fft,即使这是不必要的,因为另一个乘数是标量。有什么解决方法吗?
谢谢
-O2
进行编译并使用已编译的代码进行基准测试? - jberrymanlet c' = fft c in map (c*) xs
并不会计算fft
,因为 Haskell 是惰性的。c'
从未被使用,所以它永远不会被计算。 - luqui