我想编写一个函数来获取字符串中的第一个整数,如果整数位于字符串的开头,后面跟空格或没有。
例如," 12"和#12; 12个孩子"将是有效的字符串,但" 12kids"," a12"无效。
这是我的功能:
getFirstInteger :: String -> Int
getFirstInteger [] = error "No integer"
getFirstInteger str
| dropWhile (Char.isNumber) str == [] = read str :: Int
| Char.isSpace $ head $ dropWhile (Char.isNumber) str = read (takeWhile (Char.isNumber) str) :: Int
| otherwise = error "No integer found"
问题是如果字符串实际上是一个整数,head
将引发异常,这就是第一个条件存在的原因。这个问题有没有优雅的解决方案?
答案 0 :(得分:4)
getFirstInteger :: String -> Int
getFirstInteger = read . head . words
words
会将String
拆分为Strings
列表,这些列表最初由空格分隔。 head
将取第一个(或error
如果原始字符串为空),read
将像往常一样解析字符串(如果第一个单词不是&#39,则error
; ta有效Int
)。
但是,我更喜欢在空error
或无法解析的String
上使用import Text.Read (readMaybe)
getFirstInteger :: String -> Maybe Int
getFirstInteger [] = Nothing
getFirstInteger xs = readMaybe . head . words $ xs
的变体,例如
listToMaybe
有人可以使用Data.Maybe
中的import Data.Maybe (listToMaybe)
import Text.Read (readMaybe)
import Control.Monad ((>=>))
getFirstInteger :: String -> Maybe Int
getFirstInteger = listToMaybe . words >=> readMaybe
完全无点地写这个,但这可能是一种过度杀伤力:
$(document).on("keyup", "input", function(event) {
// If enter is pressed then hide keyboard.
if(event.keyCode == 13) {
$("input").blur();
}
});
答案 1 :(得分:3)
如果要在不使用解析器组合库或Text.Read中的任何机制的情况下解析字符串,请查看函数break
和span
:
span :: (a -> Bool) -> [a] -> ([a], [a])
break :: (a -> Bool) -> [a] -> ([a], [a])
好处是这两个函数不仅返回它们匹配的内容,还返回字符串的其余部分,允许您继续解析。
解决您的问题:
import Data.Char
getFirstInteger :: String -> Maybe Int
getFirstInteger str
let (digs, rest1) = span isDigit str
endsok = case rest1 of
[] -> True
(c:_) -> c == ' '
in
if not (null digs) && endsok
then Just (read digs)
else Nothing -- either no digits or doesn't end properly
此版本不允许使用前导减号。下一个版本允许整数的可选前导减号:
getFirstInteger' str =
let (minus,rest1) = span (=='-') str
(digs, rest2) = span isDigit rest1
endsok = case rest2 of
[] -> True
(c:_) -> c == ' '
in
if length minus <= 1 && not (null digs) && endsok
then Just (read (minus ++ digs))
else Nothing
是的 - 这不会在输入错误时尽早终止。我主要将其作为如何将调用链接到span
和break
的示例提供。
答案 2 :(得分:2)
使用reads
。例如:
type Unit = String
readUnit :: String -> Maybe (Int, Maybe Unit)
readUnit s = case reads s of -- the integer is at the beginning of the string and...
(n, ' ':unit):_ -> Just (n, Just unit) -- is followed by space...
(n, "" ):_ -> Just (n, Nothing) -- or nothing.
_ -> Nothing
在ghci:
> readUnit "12"
Just (12,Nothing)
> readUnit "12 kids"
Just (12,Just "kids")
> readUnit "12kids"
Nothing
> readUnit "a12"
Nothing
但是,要记住一些小的考虑因素。 read
可能不会像您想要的那样限制语法;例如,以下答案可能会让您大吃一惊:
> readUnit " ((-0x5)) kids"
Just (-5,Just "kids")
您可能还想删除单元中的多余空间;例如,您可以将上面的第一个子句更改为
(n, ' ':unit):_ -> Just (n, Just (dropWhile isSpace unit))
或类似的。作为此主题的最终变体,请注意虽然Read
的标准实例永远不会返回包含reads
中多个元素的列表,但从技术上讲,某些用户提供的类型可能会这样做。因此,如果您曾使用reads
来解析Int
以外的类型,您可能要么要求明确的解析,要么在决定做什么之前考虑所有解析;上面的代码假设第一个解析与任何解析一样好。