简体   繁体   English

Haskell编译时函数计算

[英]Haskell compile time function calculation

I would like to precalculate values for a function at compile-time. 我想在编译时预先计算函数的值。

Example (real function is more complex, didn't try compiling): 示例(实际功能更复杂,没有尝试编译):

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

I know that mymodulus n will be called only with n < 64 and I would like to precalculate mymodulus for n values of 0..64 at compile time. 知道只有n < 64才能调用mymodulus n ,并且我想在编译时预先计算mymodulusn值为0..64 The reason is that mymodulus would be really expensive and will be reused multiple times. 原因是mymodulus非常昂贵并且会多次重复使用。

You should use Template Haskell . 你应该使用Template Haskell With TH you can generate code programmatically, at compile time. 使用TH,您可以在编译时以编程方式生成代码。 Your mymodulus is effectively a "template" in this case. 在这种情况下,你的mymodulus实际上是一个“模板”。

For example, we can rewrite your program as follows, to compute your function statically. 例如,我们可以按如下方式重写您的程序,以静态计算您的函数。 first, the main code as usual, but instead of calling your modulus function, it calls a function whose body is a splice that will be generated at compile time: 首先,主代码像往常一样,但它不是调用你的模数函数,而是调用一个函数,它的主体是一个将在编译时生成的拼接:

{-# LANGUAGE TemplateHaskell #-}

import Table

mymodulus n = $(genmodulus 64)

main = mapM_ (print . mymodulus) [0..64]

And the code to generate the table statically: 以及静态生成表的代码:

{-# 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

This describes the abstract syntax of the case expression, which will be generated at compile time. 这描述了case表达式的抽象语法,它将在编译时生成。 We simply generate a big switch: 我们只是生成一个大开关:

    genmodulus 64
  ======>
    case n of {
      0 -> 0
      1 -> 1
      2 -> 2
      3 -> 3
      4 -> 4
      ...
      64 -> 4 }

You can see what code is generated with -ddump-splices. 您可以看到使用-ddump-splices生成的代码。 I've written the template code in direct style. 我以直接的方式编写了模板代码。 Someone more familiar with TH should be able to make the pattern code simpler. 更熟悉TH的人应该能够使模式代码更简单。

Another option would be to generate a table of values offline, and just import that data structure. 另一种选择是离线生成值表,并只导入该数据结构。

You might also say why you wish to do this. 您也可以说为什么要这样做。 I assume you have a very complex table-driven function? 我假设你有一个非常复杂的表驱动函数?

I don't know any way to precompile it down to a table lookup (though you may have some luck with TH). 我不知道有任何方法可以将它预编译到表查找中(尽管你可能对TH有一些好运)​​。 An alternative is to generate an a lookup table at runtime with something like 另一种方法是在运行时使用类似的方法生成查找表

mymodulus' x = lt ! x
    where lt = array (0, 64) [(i, mymodulus i) | i <- [0..64]]

As I remember there is some special behaviour attached to top-level definitions. 我记得有一些特殊的行为附加到顶级定义。 If you'll try simple example: 如果你试试简单的例子:

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

You'll see that first call of (last primes) will initiate calculation of primes and second line will reuse those calculations. 你会看到(最后一个素数)的第一次调用将启动素数的计算,第二行将重用这些计算。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM