我试图在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 :(得分:3)
我认为这里的主要问题是你让scanChar :: Char -> Int
为零字符('0'
)以及其他字符返回零。因此,scanString
必须包含额外的逻辑,这使得它更加复杂。
因此,我们可以通过返回scanChar
来清除-1
(或者我们可以让它返回Maybe Int
并让它返回Nothing
,无论您如何准确指定它,关键是尝试将检查逻辑封装在一个函数中,这样我们就不必再关心它了)。例如:
scanChar :: Char -> Int
scanChar c | '0' <= c && c <= '9' = fromEnum c - fromEnum '0'
| otherwise = -1
现在我们可以在scanChar
中封装所有数字解析逻辑。现在我们仍然需要实现scanString :: String -> 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
答案 1 :(得分:1)
因此,您受限于最外层问题类型为String -> Int
的问题的规范,但这并不意味着您的帮助函数scanChar
无法返回{{1} }。
让我们看看这样做:
Maybe Int
现在,在另一个答案中使用该方法:
scanChar :: Char -> Maybe Int
scanChar c
| 48 <= fromEnum c && fromEnum c <= 57 = Just $ (fromEnum c) - fromEnum '0'
| otherwise = Nothing
答案 2 :(得分:1)
为什么不
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
使用readMaybe
simple solution here。
答案 3 :(得分:0)
您可以将以下内容转换为一个带有一个字符串参数的函数。
这个函数只接受字符串中的数字并将它们打包成另一个字符串然后转换。 [read [d | d <- "12a", elem d "1234567890"] :: Int] !! 0
收益率12