練習問題 5.1.5
かなり難易度の高い練習問題として,
convert
の逆を行うような関数を書け. すなわち,数を英語表現で入力し,それに対応する10進表現を出力する関数を書け.
rconvert :: String -> Int
rconvert = rconvert9 . noiseCancel
noiseCancel :: String -> [String]
noiseCancel = filter (/= "and") . words . map hyphen2space
hyphen2space :: Char -> Char
hyphen2space c = if c == '-' then ' ' else c
rconvert9 :: [String] -> Int
rconvert9 = ncombine9 . nums9
nums9 :: [String] -> ([String],[String])
nums9 = splitBy (== "million")
ncombine9 :: ([String],[String]) -> Int
ncombine9 = uncurry ((+) . (1000000*)) . cross (rconvert3, rconvert6)
rconvert6 :: [String] -> Int
rconvert6 = ncombine6 . nums6
nums6 :: [String] -> ([String],[String])
nums6 = splitBy (== "thousand")
ncombine6 :: ([String],[String]) -> Int
ncombine6 = uncurry ((+) . (1000*)) . cross (rconvert3, rconvert3)
rconvert3 :: [String] -> Int
rconvert3 = ncombine3 . nums3
nums3 :: [String] -> ([String],[String])
nums3 = splitBy (== "hundred")
ncombine3 :: ([String],[String]) -> Int
ncombine3 = uncurry ((+) . (100*)) . cross (rconvert2, rconvert2)
rconvert2 :: [String] -> Int
rconvert2 = ncombine2 . nums2
nums2 :: [String] -> ([String],[String])
nums2 = break (`elem` unitsTeens)
ncombine2 :: ([String],[String]) -> Int
ncombine2 = uncurry (+) . cross (rconv', rconv'')
rconv'' :: [String] -> Int
rconv'' [] = 0
rconv'' (x:_) = search (zip unitsTeens [1 .. 19]) x
rconv' :: [String] -> Int
rconv' [] = 0
rconv' (x:_) = search (zip tens [20,30 .. 90]) x
search :: [(String,Int)] -> String -> Int
search [] _ = 0
search ((x,n):xs) s = if x == s then n else search xs s
unitsTeens :: [String]
unitsTeens = (units ++ teens)
splitBy :: (a -> Bool) -> [a] -> ([a],[a])
splitBy p xs = case break p xs of
(ys,[]) -> ([],ys)
(ys,_:zs) -> (ys,zs)