有一个字符串列表但需要一个整数列表

时间:2012-09-28 19:20:40

标签: list haskell

我有一个包含字符串的列表,我需要将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]ordChar -> Int,但我如何阅读[Char] -> [Int]?经过所有这些试验和唯一的错误,我不需要过滤掉最后一个新行吗?

有什么建议吗?

4 个答案:

答案 0 :(得分:1)

如果您使用ord,则类型匹配,但这不是您想要的,因为ord会给您 ascii值,而不是数值:ord 553,而不是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表示您输出了5Nothing表示存在问题,没有输出。

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)

阅读mapfilter的文档。这非常重要。  在你的情况下

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 comprehensionc <- cs基本上依次为每个字符分配c个字符。 c /= '\n'过滤掉了行尾的情况(无论它出现在哪里 - 它都不必在最后)。 ord c将值包含在最终列表中。

这可以使用filtermap来表达,但是一旦习惯了,列表理解会更加方便。

改进版可能会使用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"

fmapcharsToInts应用于readFile的结果,上述代码相当于:

getTxt = do
    chars <- readFile "foo.txt"
    return $ charsToInts chars

[外部评论:

您可以使用列表理解来进一步减少它:

getTxt :: IO [Int]
getTxt = do
    chars <- readFile "foo.txt"
    return [read [d] | d <- chars]

请注意,虽然顶级函数的类型注释通常是个好主意,但在这种情况下它是必需的(除非您将注释放入函数体中)。那是因为“读”否则不知道你想要什么类型。 ]