Римляне, рубины и Хаскель - PullRequest
2 голосов
/ 30 марта 2012

По мотивам Римлянам, рубинам и 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")

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

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

Вместо $(roman "CCLXXXI") Я хотел бы что-то вроде roman "CCLXXXI", или даже что-то лучше,Пока что мне не удалось улучшить синтаксис.

Ответы [ 2 ]

3 голосов
/ 30 марта 2012

Фактическое вычисление происходит во время компиляции, верно?

Правильно.Ваш код шаблона Haskell генерирует целочисленный литерал, который, очевидно, должен оцениваться во время компиляции.Чтобы вычисления выполнялись во время выполнения, вам нужно сгенерировать выражение другого типа, например приложение функции.

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

Вы не можете, правда.Код времени компиляции предназначен для отличия от обычного кода по уважительной причине, поскольку код времени компиляции может вести себя совершенно иначе, чем обычный код.Альтернативой является написание квазиквотера, что позволит вам использовать вместо этого синтаксис [roman| CCLXXXI |].

Однако использование вами оператора ($) здесь избыточно, поэтому вы также можете написать

print $(roman "CCLXXI")

что, возможно, выглядит немного красивее.

0 голосов
/ 30 марта 2012

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

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

[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)

Или, возможно, -O2 от ghc уже оптимизирует вызовы Integer? Я не уверен в этом, но если это так, то вы можете просто использовать тривиальный Integral экземпляр:

instance Integral Roman where
    toInteger (M n) = 1000 + toInteger n
    ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...