简体   繁体   English

如何在 Haskell 中检查数字是否为八进制

[英]how to check if a number is in octal in Haskell

I need a program that receives a String containing an octal number and converts it to decimal.我需要一个接收包含八进制数的字符串并将其转换为十进制的程序。 If the String contains anything that's not a number from 0 to 8, the function should return a 0.如果 String 包含任何不是 0 到 8 的数字,则该函数应返回 0。

This is what I got:这是我得到的:

octoDec :: [Char] -> Int
octoDec [] = 0
octoDec (x:xs) = ((digitToInt(x)) * 8 ^(length xs)) + octoDec xs

If I enter octoDec ['1','2','3'] I get 83 , which is expected.如果我输入octoDec ['1','2','3']我得到83 ,这是预期的。 However, how can I validate the user's without needing another function?但是,如何在不需要其他功能的情况下验证用户?

Edit: I've manage to build a function that checks if a number contains only digits between 0 and 7:编辑:我设法构建了一个函数来检查一个数字是否只包含 0 到 7 之间的数字:

isOcto :: [Char] -> Bool
isOcto [] = True
isOcto (x:xs) | (digitToInt(x) > 0) && digitToInt(x) < 7 = isOcto (xs)
              |otherwise = False

what i wanted is to merge these two functions into one and return zero to invalid.我想要的是将这两个函数合二为一,并返回零无效。

If you want octoDec to not only return the result, but also determine whether a result is even possible, return a Maybe Int instead of Int :如果您希望octoDec不仅返回结果,而且还确定结果是否可能,请返回Maybe Int而不是Int

octoDec :: [Char] -> Maybe Int
octoDec [] = Just 0
octoDec (x:xs) = do
  rest <- octoDec xs
  let d = digitToInt x
  guard $ d >= 0 && d <= 7 
  pure $ rest + d * 8^length xs

The guard function from Control.Monad will make the whole do block return Nothing if the condition doesn't hold.如果条件不成立, 来自Control.Monadguard函数将使整个do块返回Nothing

First, you'll want to use Horner's Method to efficiently convert a sequence of digits to a single value.首先,您需要使用Horner 方法将数字序列有效地转换为单个值。

> foldl (\acc n -> 8*acc + n) 0 (map digitToInt "123")
83

map digitToInt parses the string, and foldl (\\acc n -> 8*acc + n) 0 is a function that evaluates the result of the parse. map digitToInt解析字符串, foldl (\\acc n -> 8*acc + n) 0是评估解析结果的函数。

horner :: [Int] -> Int
horner = foldl (\acc n -> 8*acc + n) 0

parseString :: [Char] -> [Int]
parseString = fmap digitToInt

octoDec :: [Char] -> Int
octoDec = horner . parseString

However, digitToInt isn't quite right: it can accept digits greater than 7,但是, digitToInt不太正确:它可以接受大于 7 的数字,

> parseString "193"
[1,9,3]

and raises an exception for a value that isn't a digit at all.并为根本不是数字的值引发异常。

> parseString "foo"
[15,*** Exception: Char.digitToInt: not a digit 'o'

We can write a better a function:我们可以写一个更好的函数:

octalDigitToInt :: Char -> Maybe Int
octalDigitToInt c | c `elem` "01234567" = Just (digitToInt c)
                  | otherwise = Nothing

We can use that to convert an octal number into a sequence of digits, using traverse instead of fmap :我们可以使用它来将八进制数转换为数字序列,使用traverse而不是fmap

parseString' :: [Char] -> Maybe [Int]
parseString' = traverse octalDigitToInt

Valid octal string produce a Just value:有效的八进制字符串产生Just值:

> parseString' "123"
Just [1,2,3]

while invalid strings produce a Nothing value:而无效的字符串会产生一个Nothing值:

> parseString' "193"
Nothing
> parseString' "foo"
Nothing

(Think of traverse as being a function that not only applies a function to a list of values, but only produces a list of results if each application succeeds. More precisely, it's a combination of sequence and fmap : (将traverse视为一个函数,它不仅将函数应用于值列表,而且仅在每个应用程序成功时才生成结果列表。更准确地说,它是sequencefmap的组合:

traverse f = sequence . fmap f

where sequence is the function that "inverts" a value of type [Maybe Int] to a value of type Maybe [Int] .)其中sequence是将[Maybe Int]类型的值“反转”为Maybe [Int]类型的值的函数。)


With a more robust version of parseString , we need to adapt octoDec to handle the possibility that parsing will fail.使用更强大的parseString版本,我们需要调整octoDec来处理解析失败的可能性。 We do that by using fmap to "lift" horner into the Maybe functor.我们通过使用fmap来“提升” hornerMaybe仿函数。

octoDec' :: [Char] -> Maybe Int
octoDec' s = fmap horner (parseString' s)  -- or octoDec = fmap horner . parseString'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM