2012-03-30 76 views
2

Romans, rubies and the D的啓發,我想看看Haskell能否做到這一點。羅馬人,紅寶石和哈斯克爾

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/03/01/evolving-faster-haskell-programs-now-with-llvm /)描述瞭如何使用適當的交換機的LLVM後端預編譯沒有模板擴展的Haskell 98代碼。 – 2012-03-31 04:28:13

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'是'Integral'成員,而不是'Num'。 (我猜你在想'fromInteger'?) – hammar 2012-03-30 19:52:49

+0

糟糕。很好的接收。有時我不會打電話,我會編輯它。 – 2012-03-31 00:48:36