分数を循環小数で表現しているページを見かけたので Haskell の練習に私もやってみました。
{- > fractionToDecimal 1 2 --=> "0.5" > fractionToDecimal 2 3 --=> "0.(6)" > fractionToDecimal 2 7 --=> "0.(285714)" > fractionToDecimal 1 66 --=> "0.0(15)" > fractionToDecimal 1 666 --=> "0.0(015)" > fractionToDecimal 12 123 --=> "0.(09756)" > fractionToDecimal 1 333333333333333333333 --=> "0.(000000000000000000003)" > fractionToDecimal 123 1234 --=> "0.0(99675850891410048622366288492706645056726094003241491085899513776 33711507293354943273905)" > fractionToDecimal 1234 12345 --=> "0.^C Interrupted. -} listLen :: Int listLen = 1000 fractionToDecimal :: Integer -> Integer -> String fractionToDecimal x y = if x > y -- 循環部分の変換が目的なので分子が分母より大きい数は余りを分子としている then fractionToDecimal remainder y else if isCirculator x y then integerNUM ++ (tupleToString $ inTheMiddleOfList 0 decimalNUM) else case remainder of 0 -> show quotient _ -> toString numList where (integerNUM,decimalNUM) = splitAt 2 $ toString $ take listLen $ -- -- ["0","1","4","2","8","5","7","1","4"...] --=>"0.14285714..." toString ::[String] -> String toString ls = concat $ head ls:".":tail ls numList :: [String] numList = toList x y (quotient, remainder)= quotRem x y -- 循環小数? -- 循環小数でない場合は take 関数で取得する長さを変化させても結果は一緒。 isCirculator :: Integer -> Integer -> Bool isCirculator x y = length (take listLen $ numList) /= length (take (listLen+1) $ numList) where numList = toList x y -- Maybe (先頭部分, 循環する長さ, 残りの部分)を文字列に変換 tupleToString :: Maybe (String, Int, String) -> String tupleToString tuple = case tuple of Nothing -> "ERROR!" -- 分数は割りきれるか循環小数なのでERROR にはならないはず。 Just (xs, n, ys) -> xs ++ "(" ++ fst (splitAt n ys) ++")" -- 中間部分から(0 からサーチ)の循環を見つける。 -- Maybe (先頭部分, 循環する長さ, 残りの部分) -- :循環数は残りの部分に含まれている。 inTheMiddleOfList :: Int -> String -> Maybe (String, Int, String) inTheMiddleOfList p xs | p > length xs = Nothing | otherwise = case tJunkan $ snd splitAtp of -- 分割して先頭からチェック Nothing -> inTheMiddleOfList (p+1) xs Just len -> Just (fst splitAtp,len,snd splitAtp) where splitAtp = splitAt p xs -- 先頭からの長さを1から増加させながら繰り返し部分の長さを見つける。 tJunkan :: String -> Maybe Int tJunkan xs = junkan' xs 1 junkan' :: String -> Int -> Maybe Int junkan' xs p | p > length xs = Nothing | otherwise = if isJunkan p xs 0 then Just p else junkan' xs (p+1) -- 先頭から指定の長さの部分がリスト全体に1回以上繰り返されている? isJunkan :: Int -> String -> Int -> Bool isJunkan l xs n | length sndXS < length fstXS && n > 0 = True -- 残りの長さがチェック部分より短い | fstXS == fstXS2 = isJunkan l sndXS2 (n+1) | otherwise = False -- 一致しない where (fstXS, sndXS) = splitAt l xs (fstXS2, sndXS2) = splitAt l sndXS -- toList 1 2 --=> ["0","5"] -- toList 1 3 --=> ["0","3","3","3"........] -- toList 1 7 --=> ["0","1","4","2","8","5","7","1"......] toList :: Integer -> Integer -> [String] toList x y = [show quotient] ++ if remainder == 0 then [] else toList (remainder * 10) y where (quotient, remainder)= quotRem x y
私の方がずっと長い・・・Orz