2012-03-30 4 views
2

동기 부여가 Romans, rubies and the D인데, 나는 하스켈에서도 같은 결과가 나오는지 알고 싶었다.로마인, 루비와 하스켈

module Romans where 

import Language.Haskell.TH 
import Language.Haskell.TH.Syntax 
import Data.Text 

num :: String -> String 
num s = rep $ pack s 
    where 
    r1 s1 = replace (pack "IV") (pack "IIII") s1 
    r2 s2 = replace (pack "IX") (pack "VIIII") s2 
    r3 s3 = replace (pack "XL") (pack "XXXX") s3 
    r4 s4 = replace (pack "XC") (pack "LXXXX") s4 
    rep = unpack . r4 . r3 . r2 . r1 

value :: String -> Int 
value s = cnt $ pack s 
    where 
    c1 s1 = (count (pack "I") s1) * 1 
    c2 s2 = (count (pack "V") s2) * 5 
    c3 s3 = (count (pack "X") s3) * 10 
    c4 s4 = (count (pack "L") s4) * 50 
    c5 s5 = (count (pack "C") s5) * 100 
    cnt t = c5 t + c4 t + c3 t + c2 t + c1 t 

roman :: String -> ExpQ 
roman s = return $ LitE (IntegerL (compute s)) 
    where 
    compute s = fromIntegral $ value $ num s 

과 :

{-# LANGUAGE TemplateHaskell #-} 

import Romans 

main = print $ $(roman "CCLXXXI") 

첫째,이 템플릿 하스켈에 새로운 오전, 나는 내가 바로 그것을 가지고 있는지 알고 싶습니다. 실제 계산은 컴파일 타임에 발생합니다. 맞습니까?

두 번째, 구문을 어떻게 개선합니까?

$(roman "CCLXXXI") 대신 roman "CCLXXXI" 또는 더 나은 것을 제안합니다. 지금까지 구문을 개선하지 못했습니다.

답변

3

실제 계산은 컴파일 타임에 발생합니까?

수정. 당신의 템플릿 Haskell 코드는 정수 리터럴을 생성하고있다. 이것은 컴파일 타임에 분명히 평가되어야한다. 계산을 런타임에 수행하려면 다른 종류의 표현식을 생성해야합니다. 기능 응용 프로그램.

두 번째, 구문을 어떻게 개선합니까?

정말 그렇습니다. 컴파일 타임 코드는 일반 코드와 상당히 다르게 동작 할 수 있기 때문에 컴파일 타임 코드는 정규 코드에서 눈에 띄도록 설계되었습니다. 다른 방법으로는 쿼시 쿼터를 작성하는 것이므로 [roman| CCLXXXI |] 구문을 대신 사용할 수 있습니다.

그러나, ($) 연산자의 사용은 여기에 중복, 그래서 당신은 아마 조금 더 예뻐 보이는

print $(roman "CCLXXI") 

를 작성할 수 있습니다.

+0

을, 돈 스튜어트 (멋진 [블로그 게시물을]이 http://donsbot.wordpress.com/2010 적절한 스위치가있는 LLVM 백엔드가 어떻게 템플릿 확장없이 하스켈 98 코드를 프리 컴파일할지 설명하는/03/01/evolving-faster-haskell-programs-now-with-llvm / –

0

우선 원하는대로 설명하면 좋을 것입니다. 나는 당신이 로마 숫자의 컴파일 시간 번역을 Num a => a으로 바꾸고 싶다는 링크에서 가져온다. 그러나 아마도 나는 나의 짧은 읽기에서 그것을 정확하게 얻지 못했다.

추가 TH 구문이 왜 문제인지는 알 수 없지만 템플릿 하스켈 없이도 할 수 있다고 생각합니다. 하나는 쿼시 쿼터를 사용하여 다음과 같은 구문을 사용합니다.

[r|XXVI|] 

그래도 여전히 매우 깨끗하지는 않습니다.

로마 숫자의 데이터 유형에 대한 해석 될 또 다른 방법 :

data Roman = M Roman | D Roman | C Roman | X Roman | V Roman | I Roman | O 
romanToInt :: Roman -> Int 
romanToInt = ... 

-- or use a shorter function name for obvious reasons. 
r = romanToInt 

{-# rewrite 
    "Roman M" forall n. romanToInt (M n) -> 1000 + romanToInt n 
    #-} 
-- many more rewrite rules are needed to ensure the simplifier does the work 

-- The resulting syntax would have spaces: 
val95 = r (V C) 

아니면 GHC의 -O2는 toInteger 메서드가 이미 호출 최적화? 나는 그것에 대해 잘 모르겠지만, 그럼 당신은 그냥 사소한 Integral 인스턴스를 사용할 수있는 경우 : 사실

instance Integral Roman where 
    toInteger (M n) = 1000 + toInteger n 
    ... 
+1

'toInteger'는'Num'이 아닌'Integral' 멤버입니다. (당신은'fromInteger'를 생각하고 있었나요?) – hammar

+0

죄송합니다. 멋지다. 언젠가 나는 편집하지 않을거야. –