我有一个包含字符串的列表,我需要将Char解构为Char,并将其作为整数放入列表中,但是我被类型阻碍了
我所拥有的是一个读入monad的txt文件:
getTxt = do
y <- readFile "foo.txt"
return y
foo
仅包含此内容:
"1234567890\n"
然后我认为我的序列很接近,但是我得到了这个列表:
["1","2","3","4","5","6","7","8","9","0"] :: [[Char]]
但我需要[Integer]
。 ord
将Char -> Int
,但我如何阅读[Char] -> [Int]
?经过所有这些试验和唯一的错误,我不需要过滤掉最后一个新行吗?
有什么建议吗?
答案 0 :(得分:1)
如果您使用ord
,则类型匹配,但这不是您想要的,因为ord
会给您
ascii值,而不是数值:ord 5
是53
,而不是5
。你可以减去
48得到数字,然后将数字滚动到一个数字,但它会
更容易使用库函数。最直接的选择是read
:
getInt :: IO Integer
getInt = do
y <- readFile "foo.txt"
return (read (takeWhile (/='\n') y))
与linked answer一样,
这里最好的解决方案是使用reads
。
reads
找到可能匹配的列表,
作为(match,remainingstring)
对,
这对你很有用,因为它会自动将换行符留在剩下的字符串中,
*Main> reads "31324542\n" :: [(Integer,String)]
[(31324542,"\n")]
让我们用它:
findInt :: String -> Maybe Integer
findInt xs = case reads xs of -- have a look at reads xs
((anint,rest):anyothers) -> Just anint -- if there's an int at the front of the list, just return it
_ -> Nothing -- otherwise return nothing
Maybe
是一种方便的数据类型,可以让您在不崩溃程序或进行异常处理的情况下发生故障。
Just 5
表示您输出了5
。 Nothing
表示存在问题,没有输出。
addTen :: FilePath -> IO ()
addTen filename = do
y <- readFile filename
case findInt y of
Just i -> putStrLn ("Added 10, got "++show (i+10))
Nothing -> putStrLn ("Didn't find any integer at the beginning of " ++ filename)
这给了你:
*Main> addTen "foo.txt"
Added 10, got 1234567890
如果您只想要字符所代表的整数,可以将import Data.Char
放在文件的顶部并执行
ordzero = ord '0' -- handy constant, 48, to unshift the ascii code to a digit.
getInts :: FilePath -> IO [Int] -- ord gives the smaller Int, not Integer
getInts filename = do
y <- readFile filename
return [ord achar - ordzero | achar <- takeWhile isDigit y]
只要它们是数字,就会获取字符串y
的字符,
然后找到他们的ord
,减去ord '0'
(即48)将'4'
变成4
等。
答案 1 :(得分:0)
阅读map
和filter
的文档。这非常重要。
在你的情况下
integersFromFile :: String -> IO [Int]
integersFromFile filename = map digitToInt <$> readFile filename
答案 2 :(得分:0)
我不确定我明白你在说什么,但是我认为幻觉生活所暗示的版本是列表理解......
do cs <- readFile "foo.txt"
return [ord c | c <- cs, c /= '\n']
这有点作弊 - 它假设文件只包含数字和行尾,并且只是在任何地方删除任何行尾字符。
说明 - 这是list comprehension。 c <- cs
基本上依次为每个字符分配c
个字符。 c /= '\n'
过滤掉了行尾的情况(无论它出现在哪里 - 它都不必在最后)。 ord c
将值包含在最终列表中。
这可以使用filter
和map
来表达,但是一旦习惯了,列表理解会更加方便。
改进版可能会使用isDigit
(来自Data.Char)来检查字符。还有Maybe方法可以跟踪列表中是否存在无效字符,因此您可以稍后检查并报告这些标记,也可以将其过滤掉。
答案 3 :(得分:0)
所以你想要一个具有这种类型的函数:
charsToInts :: [Char] -> [Int]
我们可以通过将问题分解为更小的问题来解决这个问题。首先,我们需要一个将单个Char
转换为String
的函数:
charToString :: Char -> String
charToString c = [c]
...然后我们需要一个将String
转换为Int
的函数:
stringToInt :: String -> Int
stringToInt = read
...然后我们编写这两个函数来获取将Char
转换为Int
s的函数:
charToInt :: Char -> Int
charToInt = stringToInt . charToString
现在,我们可以使用Char
解除该功能以处理整个map
列表:
charsToInts :: [Char] -> [Int]
charsToInts = map charToInt
......我们已经完成了!
我为了示范目的采取了非常冗长的道路。在我自己的代码中,我通常会将所有这些定义内联如下:
charsToInts :: [Char] -> [Int]
charsToInts = map (read . singleton)
where singleton x = [x]
要在代码中使用stringsToInts
,您只需编写:
getTxt :: IO [Int]
getTxt = fmap charsToInts $ readFile "foo.txt"
fmap
将charsToInts
应用于readFile
的结果,上述代码相当于:
getTxt = do
chars <- readFile "foo.txt"
return $ charsToInts chars
[外部评论:
您可以使用列表理解来进一步减少它:
getTxt :: IO [Int]
getTxt = do
chars <- readFile "foo.txt"
return [read [d] | d <- chars]
请注意,虽然顶级函数的类型注释通常是个好主意,但在这种情况下它是必需的(除非您将注释放入函数体中)。那是因为“读”否则不知道你想要什么类型。 ]