我有一个问题。从元组的文件列表中读取有什么解决方案吗?取决于内容? 我知道,如果我需要读取整数,我会做类似的事情:
toTuple :: [String] -> [(Int,Int)]
toTuple = map (\y -> read y ::(Int,Int))
但是在文件中我可以有这种元组(int,int)或(char,int)。有什么方法可以做到这一点好吗?
我试图首先找到这个标志“'”。如果是,那么阅读字符,但由于某种原因它不起作用。
[编辑]
为了对元组起作用,我给元组赋予字符串,然后我用空格符号分割行。
输入示例:
Case 1 : ["(1,2)", "(1,3)" ,"(3,4)" ,"(1,4)"]
Case 2 : ["('a',2)", "('b',3)", "('g',8)", "('h',2)", "('r',4)"]
答案 0 :(得分:4)
试试两个并选择成功:
readListWithMaybe :: Read a => String -> [String] -> Maybe [a]
readListWithMaybe s ss = fmap (: map read ss) (readMaybe s)
toTuple :: [String] -> Either [(Int, Int)] [(Char, Int)]
toTuple [] = Left []
toTuple (s:ss) = fromJust $ readListWithMaybe s ss `choose` readListWithMaybe s ss
这是一个效率更高(且不安全)的版本:
toTuple
在toTuple :: [String] -> Maybe (Either [(Int, Int)] [(Char, Int)])
toTuple ss = readListMaybe ss `choose` readListMaybe ss
readListMaybe
readListMaybe :: Read a => [String] -> Maybe [a]
readListMaybe = mapM readMaybe
太严格了:
mapM
sequence
是根据(>>=)
定义的,Maybe
是ss
对{{1}} monad严格定义的。此外,对{{1}}的提及也需要很长时间。第二个版本没有这些问题。
答案 1 :(得分:2)
正如我所说,考虑使用解析库可能是一个好主意,如果手头的任务变得有点复杂。
首先,您可以获得错误消息,如果您决定切换到自我声明的数据类型,它仍然很容易适用(当然稍作修改)。
同样从ByteString
切换到Text
(这两者都优于使用String
)只是(不)评论4行
如果你没有兴趣使用它,这是一些例子。
我会在今天晚些时候解释它 - 因为我现在必须离开。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Attoparsec.ByteString.Char8
import Data.ByteString.Char8 as X
-- import Data.Attoparsec.Text
-- import Data.Text as X
main :: IO ()
main = do print <$> toTuples $ X.unlines ["(1,2)","(1,3)","(3,4)","(1,4)"]
print <$> toTuples $ X.unlines ["('a',2)","('h',2)","('r',4)"]
print <$> toTuples $ X.unlines ["('a',2)","(1,3)","(1,4)"] --works
print <$> toTuples $ "('a',2)" -- yields Right [Right ('a',2)]!!
print <$> toTuples $ "(\"a\",2)" -- yields Right []!!
toTuples = parseOnly (myparser `sepBy` skipSpace :: Parser [Either (Int,Int) (Char,Int)])
where myparser :: Parser (Either (Int,Int) (Char,Int))
myparser = eitherP (tupleP decimal decimal)
(tupleP charP decimal)
charP = do char '\''
c <- notChar '\''
char '\''
return c
tupleP :: Parser a -> Parser b -> Parser (a, b)
tupleP a b = do char '('
a' <- a
skipSpace
char ','
skipSpace
b' <- b
char ')'
return (a',b')
Parser
是一个monad,所以它带有do
- 符号,它使我们能够以这种非常方便的形式编写tupleP
函数。同样适用于charP
- 我们描述了在attoparsec
库给出的原语中要解析的内容
它的内容类似于
如果你可以非正式地写下解析器,你很可能在编写haskell代码的过程中,唯一要做的就是找到库中的原语或者写一些辅助函数,如tupleP
。
一个好处是Parsers
(monad)很好地组合,所以我们得到了我们想要的解析器eitherP (tupleP ..) (tupleP ..)
。
在print <$>..
行中发生的唯一魔力是Either
是一个仿函数,使用<$>
或fmap
的每个函数都使用Right
Either
s。
最后要注意的是sepBy
返回一个列表 - 所以在解析失败的情况下,如果你想看到失败的使用sepBy1
,我们仍会得到一个空列表!