【问题标题】:how to check if a number is in octal in Haskell如何在Haskell中检查数字是否为八进制
【发布时间】:2021-12-23 14:34:26
【问题描述】:

我需要一个程序来接收包含八进制数的字符串并将其转换为十进制。如果 String 包含的不是 0 到 8 之间的数字,则该函数应返回 0。

这是我得到的:

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

如果我输入 octoDec ['1','2','3'] 我会得到 83 ,这是预期的。但是,如何在不需要其他功能的情况下验证用户?

编辑:我已经设法构建了一个函数来检查一个数字是否只包含 0 到 7 之间的数字:

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

我想要的是将这两个函数合并为一个并将零返回为无效。

【问题讨论】:

  • “需要另一个功能”是什么意思?需要定义另一个顶级函数?需要使用其他功能?需要定义任何其他函数吗?
  • 我希望在 octoDec 函数中检查八进制
  • Horner's Method 是一种更有效的计算值的方法。对于n-digit 数字,您只需要 O(n) 次乘法而不是 O(n) 次幂运算。

标签: haskell functional-programming


【解决方案1】:

如果您希望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

如果条件不成立,guard function from Control.Monad 将使整个 do 块返回 Nothing

【讨论】:

    【解决方案2】:

    首先,您需要使用Horner's Method 将数字序列高效地转换为单个值。

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

    map digitToInt 解析字符串,然后 foldl (\acc n -&gt; 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
    

    但是,digitToInt 不太正确:它可以接受大于 7 的数字,

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

    并为根本不是数字的值引发异常。

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

    我们可以写一个更好的函数:

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

    我们可以使用traverse而不是fmap来将八进制数转换为数字序列:

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

    有效的八进制字符串产生一个Just 值:

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

    当无效字符串产生Nothing 值时:

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

    (将traverse 视为一个函数,它不仅将函数应用于值列表,而且仅在每个应用程序成功时才生成结果列表。更准确地说,它是sequence 和@987654339 的组合@:

    traverse f = sequence . fmap f
    

    其中sequence 是将[Maybe Int] 类型的值“反转”为Maybe [Int] 类型的值的函数。)


    使用更健壮的parseString 版本,我们需要调整octoDec 以处理解析失败的可能性。我们通过使用fmaphorner“提升”到Maybe 函子中来做到这一点。

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

    【讨论】:

      猜你喜欢
      • 2011-02-22
      • 2019-12-06
      • 1970-01-01
      • 1970-01-01
      • 2020-06-27
      • 2014-10-22
      • 2018-05-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多