2012-03-30 1 views
2

Мотивированный Romans, rubies and the D, я хотел посмотреть, можно ли сделать то же самое в Haskell.Romans, рубины и 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") 

Во-первых, как я новичок в Template Haskell, я хотел бы знать, если я получил это право. Фактическое вычисление происходит во время компиляции, правильно?

и, во-вторых, как улучшить синтаксис?

Вместо $(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 без этого расширения шаблона. –

0

Во-первых, было бы хорошо, если бы вы объяснили, что хотите. Я беру это из ссылки, что вы хотите, чтобы перевод римских цифр во время компиляции был Num a => a, но, возможно, я не понял его в моем кратком чтении.

Я не понимаю, почему дополнительный синтаксис TH является проблемой, но я думаю, что вы можете сделать это без Template Haskell. Можно было бы использовать квази-Quoter, в результате чего в синтаксисе, как:

[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

+0

Упс. Хорошо поймал. Я его когда-нибудь отредактирую, я не на телефоне. –