【问题标题】:convert string of digits to int in Haskell在Haskell中将数字字符串转换为int
【发布时间】:2018-09-03 00:20:31
【问题描述】:

我正在尝试在 Haskell 中编写这个名为 scanString 的函数,它接受一个字符串并将其转换为 int,如果它仅由数字组成,否则应返回 0。

例如,scanString "123" = 123 但 scanString "12a" = 0。

到目前为止,这是我的实现:

scanChar :: Char -> Int
scanChar c
  | 48 <= fromEnum c && fromEnum c <= 57 = (fromEnum c) - fromEnum '0'
  | otherwise = 0

scanString :: String -> Int
scanString str = case str of
  [] -> 0
  x:xs
    | 48 <= fromEnum x && fromEnum x <= 57 -> 
      ((scanChar x) * (10 ^ ((length str) -1 ))) + scanString xs
    | otherwise -> 0

此代码不正确,因为 scanString "3a" 会给出 30。

有没有一种方法(比如在 Java 或 Python 中)可以简单地终止一个函数并返回一个值?当然,关于这个函数的实现的建议会很棒!

提前致谢!

【问题讨论】:

  • 否则我觉得返回0 有点奇怪。通常在 Haskell 中,然后使用Maybe Int,从那时起我们可以区分NothingJust 0
  • 很遗憾,这是一道练习题,它指定了函数声明,接受一个字符串并返回一个整数。
  • 在这种情况下,我建议返回-1,因为这是一个不能仅用数字表示的数字。
  • 问题是,在处理"10"时,递归调用会返回0,至于"1a"的情况,很难区分这两种情况。
  • 48 &lt;= fromEnum c 可能比'0' &lt;= c 更具可读性(其他比较也是如此)。

标签: haskell


【解决方案1】:

您可以将以下内容变成一个带有一个参数的函数,该参数是一个字符串。这个函数只会从字符串中取出数字并将它们打包成另一个字符串,然后再进行转换。

 [read [d | d <- "12a", elem d "1234567890"] :: Int] !! 0

产量 12

【讨论】:

    【解决方案2】:

    为什么不

    scanString :: String -> Int
    scanString x = if all (`elem` "0123456789") x
                     then read x :: Int
                     else 0
    

    注意:它不会读取负整数。

    或者:

    import Data.Char (isDigit)
    scanString' :: String -> Int
    scanString' x = if all isDigit x
                    then read x :: Int
                    else 0
    

    还有一个simple solution here 使用readMaybe

    【讨论】:

      【解决方案3】:

      我认为这里的主要问题是你让scanChar :: Char -&gt; Int 为零字符 ('0') 以及其他字符返回一个零。因此,scanString 必须包含额外的逻辑,这只会使其更加复杂。

      所以我们可以通过例如返回一个-1 来清理scanChar(或者我们可以让它返回一个Maybe Int 并让它返回Nothing,不管你如何准确地指定它,关键是尝试将检查逻辑封装在一个函数中,这样我们就不再需要关心它了)。比如:

      scanChar :: Char -> Int
      scanChar c | '0' <= c && c <= '9' = fromEnum c - fromEnum '0'
                 | otherwise = -1
      

      所以现在我们可以将所有的数字解析逻辑封装在scanChar 中。现在我们仍然需要实现scanString :: String -&gt; Int。这可以通过编写一个与累加器一起使用的额外函数来完成。例如:

      scanString :: String -> Int
      scanString = go 0
          where go a s = ...
      

      所以这里go 充当模拟某种while 循环的函数。 a 参数是累加器,我们通过递归调用传递的参数,每次我们可以用更多数据更新它。最初我们将其设置为零。

      go函数基本分三种情况:

      • 到达字符串末尾,我们可以返回累加器;
      • 字符串的第一个字符不是数字,我们返回0;和
      • 字符串的第一个字符是数字,我们将累加器与10相乘,加上解析后的值,对字符串尾部进行递归。

      因此我们可以实现这三种情况,例如:

      scanString :: String -> Int
      scanString = go 0
          where go a [] = a
                go a (x:xs) | 0 <= sc && sc <= 9 = go (10*a+sc) xs
                            | otherwise = 0
                    where sc = scanChar x
      

      【讨论】:

        【解决方案4】:

        因此,您受到问题规范的限制,即最外层问题的类型为 String -&gt; Int,但这并不意味着您的辅助函数 scanChar 不能返回 Maybe Int

        那么让我们看看这样做:

        scanChar :: Char -> Maybe Int
        scanChar c
          | 48 <= fromEnum c && fromEnum c <= 57 = Just $ (fromEnum c) - fromEnum '0'
          | otherwise = Nothing
        

        现在,使用另一个答案中的方法:

        scanString :: String -> Int
        scanString = go 0
            where go a [] = a
                  go a (x:xs) = case (scanChar x) of
                                  Nothing -> 0
                                  Just d -> go (10*a + d) xs
                      where sc = scanChar x
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-07-20
          • 2011-10-02
          • 2011-03-06
          • 1970-01-01
          • 2018-07-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多