2

Romans、ルビー、および 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")

まず、私は Template Haskell を初めて使用するので、それが正しいかどうかを知りたいです。実際の計算はコンパイル時に行われますよね?

次に、構文を改善するにはどうすればよいですか?

代わりに、の$(roman "CCLXXXI")ようなものroman "CCLXXXI"、またはそれ以上のものが欲しいです。これまでのところ、構文を改善できませんでした。

4

2 に答える 2

3

実際の計算はコンパイル時に行われますよね?

正しい。Template Haskell コードは整数リテラルを生成していますが、これは明らかにコンパイル時に評価する必要があります。実行時に計算を行うには、関数適用など、他の種類の式を生成する必要があります。

次に、構文を改善するにはどうすればよいですか?

あなたは本当にできません。コンパイル時のコードは、通常のコードとはまったく異なる動作をする可能性があるため、正当な理由により、通常のコードから目立つように設計されています。別の方法は、代わりに構文を使用できる準引用符を作成することです[roman| CCLXXXI |]

ただし、($)演​​算子の使用はここでは冗長であるため、次のように書くこともできます

print $(roman "CCLXXI")

これはおそらく少しきれいに見えます。

于 2012-03-30T19:40:40.630 に答える
1

まず、あなたが欲しいものを説明してくれたらいいのにと思います。ローマ数字のコンパイル時の変換が必要なリンクからNum a => a取得しましたが、簡単に読んだだけでは正確に取得できなかった可能性があります。

余分なTH構文が問題になる理由はわかりませんが、TemplateHaskellがなくてもできると思います。準引用符を使用すると、次のような構文になります。

[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
    ...
于 2012-03-30T19:46:10.017 に答える