如何使用takeTill直到Haskell中的tab或换行符?(布尔表达式的问题)

如何使用takeTill直到Haskell中的tab或换行符?(布尔表达式的问题),haskell,boolean-expression,attoparsec,Haskell,Boolean Expression,Attoparsec,我正在写我的第一个Haskell程序。该程序解析普通的CSV文件,但我遇到了许多问题,这无疑是由于我对语法缺乏经验造成的 目前,代码成功地解析了一条记录,但在最后一条记录上,解析器占用了换行符,因此不会处理后续行上的记录 我建议的解决方案是在我的fieldData规范中添加一个检查,以检查“takeTill tab或newline”,但我不知道如何做到这一点 当前代码: fieldData = takeTill (== '\t') 尝试: fieldData = takeTill (== '\

我正在写我的第一个Haskell程序。该程序解析普通的CSV文件,但我遇到了许多问题,这无疑是由于我对语法缺乏经验造成的

目前,代码成功地解析了一条记录,但在最后一条记录上,解析器占用了换行符,因此不会处理后续行上的记录

我建议的解决方案是在我的fieldData规范中添加一个检查,以检查“takeTill tab或newline”,但我不知道如何做到这一点

当前代码:

fieldData = takeTill (== '\t')
尝试:

fieldData = takeTill (== '\t' || '\n') -- wrong, something about infix precedence
fieldData = takeTill (== ('\t' || '\n')) -- wrong, type error
fieldData = takeTill ((== '\t') || (== '\n')) -- wrong, type error
fieldData x = takeTill ((x == '\t') || (x == '\n')) -- wrong, type error
fieldData x = takeTill x ((x == '\t') || (x == '\n')) -- wrong, not enough arguments
我觉得我对如何在Haskell中构造布尔条件有一些基本的误解,希望得到帮助。例如,在ghci中,我可以让fun x=x='a'| | x='b',它可以很好地匹配不同的字符,所以在函数中使用它时,我显然遗漏了一些东西

或者,这是正确的方法吗?如果这不是解决问题的正确方法,我希望您能给我指点正确的方法

完整代码如下:

{- Parsing a tab-separated file using Attoparsec.
A record contains:
number\tname\tgenre\tabilities\tweapon\n

-}
import System.FilePath.Posix
import Data.Attoparsec.Char8
import Control.Applicative
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as C

data AbilitiesList = AbilitiesList String deriving Show

data PlayerCharacter = PlayerCharacter {
    id :: Integer,
    name :: String,
    genre :: String,
    abilities :: AbilitiesList,
    weapon :: String
} deriving Show

type Players = [PlayerCharacter]

fieldData = takeTill (== '\t')
tab = char '\t'

parseCharacter :: Parser PlayerCharacter
parseCharacter = do
    id <- decimal
    tab
    name <- fieldData
    tab
    genre <- fieldData
    tab
    abilities <- fieldData
    tab
    weapon <- fieldData
    return $ PlayerCharacter id (C.unpack name) (C.unpack genre) (AbilitiesList (C.unpack abilities)) (C.unpack weapon)

abilitiesFile :: FilePath
abilitiesFile = joinPath ["data", "ff_abilities.txt"]

playerParser :: Parser Players
playerParser = many $ parseCharacter <* endOfLine

main :: IO ()
main = B.readFile abilitiesFile >>= print . parseOnly playerParser

为此,您可能需要使用lambda:

takeTill (\x -> x == '\t' || x == '\n')
lambda函数是一个匿名的、一次性的内联函数。您可以像使用普通函数一样使用它们,只是它们不绑定到名称

您还可以定义一个函数

tabOrNL :: Char -> Bool
tabOrNL '\t' = True
tabOrNL '\n' = True
tabOrNL _    = False

-- Or equivalently

tabOrNL :: Char -> Bool
tabOrNL x = x == '\t' || x == '\n'
那你就可以这么做了

takeTill tabOrNL
如果您想真正喜欢,函数的应用程序实例可以在这里派上用场:

(<||>) :: Applicative f => f Bool -> f Bool -> f Bool
(<||>) = liftA2 (||)
infixr 2 <||>
那你就做吧

takeTill ((== '\t') <||> (== '\n'))
甚至

takeTill ((== '\t') <||> (== '\n') <||> (== ','))

通过这种方式,您可以完全避免lambda或helper函数,只需或组合几个谓词,就好像它们是值一样。您也可以使用=liftA2&&,但在这里它可能没有那么有用。

为此,您可能需要使用lambda:

takeTill (\x -> x == '\t' || x == '\n')
lambda函数是一个匿名的、一次性的内联函数。您可以像使用普通函数一样使用它们,只是它们不绑定到名称

您还可以定义一个函数

tabOrNL :: Char -> Bool
tabOrNL '\t' = True
tabOrNL '\n' = True
tabOrNL _    = False

-- Or equivalently

tabOrNL :: Char -> Bool
tabOrNL x = x == '\t' || x == '\n'
那你就可以这么做了

takeTill tabOrNL
如果您想真正喜欢,函数的应用程序实例可以在这里派上用场:

(<||>) :: Applicative f => f Bool -> f Bool -> f Bool
(<||>) = liftA2 (||)
infixr 2 <||>
那你就做吧

takeTill ((== '\t') <||> (== '\n'))
甚至

takeTill ((== '\t') <||> (== '\n') <||> (== ','))

通过这种方式,您可以完全避免lambda或helper函数,只需或组合几个谓词,就好像它们是值一样。您也可以使用=liftA2&&&执行类似的操作,但在这里它可能没有那么有用。

另一种解决方案是使用elem检查字符是否在列表中:

takeTill (`elem` "\t\n")

尽管我只建议在@bheklillr的解决方案中使用它来检查更多值的情况。

另一种解决方案是使用elem来检查字符是否在列表中:

takeTill (`elem` "\t\n")

尽管我只推荐使用@bheklillr的解决方案来检查更多值的情况。

我已经尝试使用lambda,但在换行之后,记录不会继续解析。然而,在这个阶段,似乎是另一个问题的另一个问题!我自己测试了它,它能够解析多个2行虚拟数据,你确定吗?也许你在上传这段代码后改变了什么?我发现了问题,以前我的最后一行是parseOnly parseCharacter而不是parseOnly playerParser,所以它显然只解析了一行而不是整个文件!我已经修改了密码,并接受了你详细而彻底的回答。谢谢。我已经尝试过使用lambda,但是记录在换行符之后不会继续解析。然而,在这个阶段,似乎是另一个问题的另一个问题!我自己测试了它,它能够解析多个2行虚拟数据,你确定吗?也许你在上传这段代码后改变了什么?我发现了问题,以前我的最后一行是parseOnly parseCharacter而不是parseOnly playerParser,所以它显然只解析了一行而不是整个文件!我已经修改了密码,并接受了你详细而彻底的回答。非常感谢。