First working version

This commit is contained in:
Tissevert 2019-11-08 18:51:01 +01:00
commit f10b2c0f69
5 changed files with 96 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
dist*/
.ghc.environment.*

5
CHANGELOG.md Normal file
View file

@ -0,0 +1,5 @@
# Revision history for RomanNum
## 0.1.0.0 -- 2019-11-08
* A first working version, exposing a constructor, sum and shortcut for conversion from String

26
RomanNum.cabal Normal file
View file

@ -0,0 +1,26 @@
cabal-version: 2.4
-- Initial package description 'RomanNum.cabal' generated by 'cabal init'.
-- For further documentation, see http://haskell.org/cabal/users-guide/
name: RomanNum
version: 0.1.0.0
-- synopsis:
-- description:
-- bug-reports:
-- license:
license-file: LICENSE
author: Tissevert
maintainer: tissevert+devel@marvid.fr
-- copyright:
-- category:
extra-source-files: CHANGELOG.md
library
exposed-modules: Data.RomanNum
-- other-modules:
-- other-extensions:
build-depends: base ^>=4.11.1.0
, containers
hs-source-dirs: src
default-language: Haskell2010
ghc-options: -Wall

2
Setup.hs Normal file
View file

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

61
src/Data/RomanNum.hs Normal file
View file

@ -0,0 +1,61 @@
module Data.RomanNum (
RomanNum(..)
, nullus
, plus
, r
) where
import Data.Map (Map, (!))
import qualified Data.Map as Map (fromList)
newtype RomanNum = RomanNum Int
data RomanChar = I | V | X | L | C | D | M deriving (Bounded, Eq, Ord, Enum, Show, Read)
romanChars :: [RomanChar]
romanChars = [minBound .. maxBound]
base :: [(Int, [Char])]
base = base' 1 [] . concat $ show <$> romanChars where
base' _ l "" = l
base' size l chars =
base' (size * 10) ((size, take 3 chars):l) (drop 2 chars)
values :: Map RomanChar Int
values = Map.fromList $
zip romanChars (concat $ iterate (fmap (*10)) [1, 5])
instance Show RomanNum where
show (RomanNum 0) = "nullus"
show (RomanNum n) = show' n base where
show' 0 _ = ""
show' _ [] = error "Numeral system not fine-grained enough"
show' k ((size, digits):others)
| k < size = show' k others
| otherwise =
let decDigit = k `div` size in
showDecDigit decDigit digits ++ show' (k `mod` size) others
showDecDigit _ [] = error "no digits for conversion"
showDecDigit k [digit] = k `times` digit
showDecDigit k digits
| k == 4 || (k == 9 && length digits > 2) =
(head digits) : [head (drop (if k > 5 then 2 else 1) digits)]
| k > 4 = head (drop 1 digits) : (showDecDigit (k-5) digits)
| otherwise = k `times` (head digits)
times count letter = take count $ repeat letter
r :: String -> RomanNum
r "" = nullus
r "nullus" = nullus
r n@[_] = RomanNum (values ! (read n))
r (digit:nextDigit:otherDigits) =
let (rChar, nextRChar) = (read [digit], read [nextDigit]) in
if rChar < nextRChar
then RomanNum (values ! nextRChar - values ! rChar) `plus` r otherDigits
else RomanNum (values ! rChar) `plus` r(nextDigit:otherDigits)
nullus :: RomanNum
nullus = RomanNum 0
plus :: RomanNum -> RomanNum -> RomanNum
plus (RomanNum a) (RomanNum b) = RomanNum (a + b)