7

컴파일 타임에 함수 값을 미리 계산하고 싶습니다.하스켈 컴파일 타임 함수 계산

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 nn < 64로 호출됩니다 것을 알고 나는 0..64nmymodulus을 미리 계산하고 싶습니다 :

예 (실제 기능은 더 복잡하다, 컴파일 시도하지 않았다) 컴파일 할 때. 그 이유는 mymodulus이 정말 비싸고 여러 번 재사용 될 것입니다.

답변

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 

이것은 컴파일 타임에 생성되는 사례 표현의 추상 구문을 설명합니다.

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

-ddump-splices로 어떤 코드가 생성되는지 확인할 수 있습니다. 직접 스타일로 템플릿 코드를 작성했습니다. TH에 익숙한 사람은 패턴 코드를 더 간단하게 만들 수 있어야합니다.

또 다른 옵션은 값 테이블을 오프라인으로 생성하고 해당 데이터 구조 만 가져 오는 것입니다.

왜 그런지 말할 수 있습니다. 매우 복잡한 테이블 구동 함수가 있다고 가정합니다.

+0

'[Integer -> Integer]'테이블이 있습니다. 기본적으로 값이 주어지면 해당 목록에서 해당 함수를 사용하여 만들어진 값의 새 목록을 생성합니다. 그 기능 목록을 자동으로 구성 할 수 있습니다. 테이블의 각 목록에는 여러 가지 기능이 포함될 수 있습니다. 기본적으로'mod' 연산에 기반하여 사용할 목록을 선택합니다.그러나 그것은 내가 컴파일 시간에 이미 그것을 만들 수 있다는 것을 의미합니다. – Egon

2

TH를 사용하여 행운을 빌어도 테이블을 미리 컴파일 할 수있는 방법을 모르겠습니다. 다른 방법으로 런타임에 조회 테이블을 생성하는 것입니다.

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

나는 lookup table이 기본적으로 함수를 호출하는 것과 같을 것이라고 생각한다. Haskell 상점은 함수와 반환 값을 호출합니다. – Egon

+3

이 기능과 일반 기능의 차이점은 분명합니다. 하스켈이 모든 기능을 암기하는 것은 일반적인 오해입니다. 예를 들어보십시오. Haskell에게 memoize 할 것을 지시하는 방법에 대한 Hackage에 관한 자료 - memocombinators. – luqui

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 

당신은 그 계산을 다시 사용합니다 소수와 두 번째 줄의 계산을 시작합니다 (마지막 소수)의 첫 번째 통화를 볼 수 있습니다.