1

我有一个里面有一个字符串的列表,我需要用 Char 解构 Char 并将其作为 Integer 放入一个列表中,但是我被这些类型所阻碍

我有一个 txt 文件,我读入 monad:

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]?在所有这些试验和唯一的错误之后,我不需要最后过滤掉最后的新行吗?

有什么建议么?

4

4 回答 4

1

如果您使用ord,则类型匹配,但这不是您想要的,因为ord给您的是 ascii 值,而不是数值:ord 5is 53, not 5。您可以减去 48 来获得数字,然后将数字累加成一个数字,但使用库函数会更容易。最直接的选择是read

getInt :: IO Integer
getInt = do
    y <- readFile "foo.txt"
    return (read (takeWhile (/='\n') y))

链接的答案一样,这里最好的解决方案是使用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等。

于 2012-09-28T21:33:00.360 回答
0

阅读map和的文档filter。它非常重要。在你的情况下

integersFromFile :: String -> IO [Int]
integersFromFile filename = map digitToInt <$> readFile filename 
于 2012-09-28T20:11:04.113 回答
0

我不确定我理解你在说什么,但我认为幻觉生活所暗示的版本是列表理解......

do cs <- readFile "foo.txt"
   return [ord c | c <- cs, c /= '\n']

这有点作弊 - 它假定文件将只包含数字和行尾,并且只要出现任何行尾字符就会去掉它们。

解释 - 这是一个列表理解c <- cs基本上c依次分配每个字符。过滤掉带有 line-end的c /= '\n'案例(无论它发生在哪里 - 它不必在最后)。ord c给出要包含在最终列表中的值。

这可以使用filterand来表达map,但是一旦你习惯了它,列表推导式会更方便。

改进的版本可能会使用isDigit(来自Data.Char)来检查字符。也许还有一种方法可以跟踪列表中是否存在无效字符,因此您可以稍后检查并报告这些标记或将它们过滤掉。

于 2012-09-28T20:40:06.543 回答
0

所以你想要一个具有这种类型的函数:

charsToInts :: [Char] -> [Int]

我们可以通过将问题分解为更小的问题来解决这个问题。首先,我们需要一个将 single 转换为 a 的Char函数String

charToString :: Char -> String
charToString c = [c]

...然后我们需要一个将 a 转换为 a 的String函数Int

stringToInt :: String -> Int
stringToInt = read

...然后我们组合这两个函数以获得将Chars 转换为Ints 的函数:

charToInt :: Char -> Int
charToInt = stringToInt . charToString

现在,我们可以使用以下方法提升该函数来处理整个Chars列表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]

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

于 2012-09-28T21:43:41.237 回答