我想在编译时预先计算函数的值。
示例(实际功能更复杂,没有尝试编译):
base = 10
mymodulus n = n `mod` base -- or substitute with a function that takes
-- too much to compute at runtime
printmodules 0 = [mymodulus 0]
printmodules z = (mymodulus z):(printmodules (z-1))
main = printmodules 64
我知道 mymodulus n
只会被n < 64
调用,我想预先计算mymodulus
n
0..64
的值mymodulus
在编译时。原因是{{1}}非常昂贵并且会多次重复使用。
答案 0 :(得分:13)
您应该使用Template Haskell。使用TH,您可以在编译时以编程方式生成代码。在这种情况下,你的mymodulus实际上是一个“模板”。
例如,我们可以按如下方式重写您的程序,以静态计算您的功能。首先,主代码像往常一样,但它不是调用你的模数函数,而是调用一个函数,它的主体是一个将在编译时生成的拼接:
{-# LANGUAGE TemplateHaskell #-}
import Table
mymodulus n = $(genmodulus 64)
main = mapM_ (print . mymodulus) [0..64]
以静态方式生成表的代码:
{-# LANGUAGE TemplateHaskell #-}
module Table where
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
genmodulus :: Int -> Q Exp
genmodulus n = return $ CaseE (VarE (mkName "n"))
[ Match (LitP (IntegerL i))
(NormalB (LitE (IntegerL (i `mod` base))))
[]
| i <- [0..fromIntegral n] ]
where
base = 10
这描述了case表达式的抽象语法,它将在编译时生成。我们只是生成一个大开关:
genmodulus 64
======>
case n of {
0 -> 0
1 -> 1
2 -> 2
3 -> 3
4 -> 4
...
64 -> 4 }
您可以看到使用-ddump-splices生成的代码。我以直接的方式编写了模板代码。更熟悉TH的人应该能够使模式代码更简单。
另一种选择是离线生成值表,然后只导入该数据结构。
您可能也会说为什么要这样做。我假设你有一个非常复杂的表驱动函数?
答案 1 :(得分:2)
我不知道有任何方法可以将它预编译到表查找中(尽管你可能对TH有一些好运)。另一种方法是在运行时生成一个类似
的查找表mymodulus' x = lt ! x
where lt = array (0, 64) [(i, mymodulus i) | i <- [0..64]]
答案 2 :(得分:0)
我记得顶级定义附带了一些特殊的行为。 如果你试试简单的例子:
primes = 2 : 3 : filter isPrime [5, 7 .. 1000000]
isPrime x = walk (tail primes) where
walk (y:ys) | (y*y > x) = True
| (x `mod` y) /= 0 = walk ys
walk _ = False
main = do
print $ last primes
print . last $ init primes
你会看到(最后一个素数)的第一次调用将启动素数计算,第二行将重用这些计算。