String haskell从字符串中删除空格,并将每个单词分隔在一个列表中
借助于,我可以成功地删除字符串中的空白,但是在我的例子中,我还需要将单词分开,并将它们全部放在一个列表中,如下面的示例所示 输入 “包含多个\n空间的\t字符串。” 将输出 [“A”,“String”,“with”,“many”,“spaces.” 我可以输出这个String haskell从字符串中删除空格,并将每个单词分隔在一个列表中,string,haskell,char,whitespace,removing-whitespace,String,Haskell,Char,Whitespace,Removing Whitespace,借助于,我可以成功地删除字符串中的空白,但是在我的例子中,我还需要将单词分开,并将它们全部放在一个列表中,如下面的示例所示 输入 “包含多个\n空间的\t字符串。” 将输出 [“A”,“String”,“with”,“many”,“spaces.” 我可以输出这个 使用以下代码 > splitWords :: String -> [String] > splitWords [] =[] > splitWords as =splitWord "" as > sp
使用以下代码
> splitWords :: String -> [String]
> splitWords [] =[]
> splitWords as =splitWord "" as
> splitWord _ [] = []
> splitWord word ('\n':as) = word : splitWord "" as
> splitWord word ('\t':as) = word : splitWord "" as
> splitWord word (' ':as) = word : splitWord "" as
> splitWord word (a:as) = splitWord (word ++ [a]) as
因为我正在尝试学习haskell,所以不使用其他库的解决方案将是理想的 你需要自己做吗?如果没有,请使用
单词
的定义如下:
words :: String -> [String]
words s = case dropWhile Char.isSpace s of
"" -> []
s' -> w : words s''
where (w, s'') = break Char.isSpace s'
编辑:不使用Data.String函数
data Parser a = P { parser :: String -> Maybe (String, a) } deriving Functor
instance Monad Parser where
return = pure
p >>= f = P (\input -> case parse p input of
Just (rest, x) -> parse (f x) rest
_ -> Nothing)
你离得不远
首先,您缺少输出中的最后一个单词。
您可以通过将行splitWord.[]=[]
更改为splitWord[]=[word]
来解决这个问题
下一个问题是添加到列表中的空字符串。您需要过滤掉它们(我制作了一个顶级函数来演示):
使用此功能:
splitWord word [] = addIfNotEmpty word []
splitWord word ('\n':as) = addIfNotEmpty word $ splitWord "" as
splitWord word ('\t':as) = addIfNotEmpty word $ splitWord "" as
splitWord word (' ':as) = addIfNotEmpty word $ splitWord "" as
splitWord word (a:as) = splitWord (word ++ [a]) as
还有塔达!它起作用了。但是等等,我们还没完呢
整理
让我们从拆分单词开始。这里没什么可做的,但我们可以使用:
接下来,请注意,对于每种类型的空间,动作都是相同的。让我们消除重复:
splitWord word (c:cs)
| c `elem` " \t\n" = addIfNotEmpty word $ splitWord "" cs
| otherwise = splitWord (word ++ [c]) cs
我在这里使用elem
检查下一个字符是否是空格,可以说有更好的方法
最终结果:
splitWords :: String -> [String]
splitWords = splitWord ""
splitWord :: String -> String -> [String]
splitWord word [] = addIfNotEmpty word []
splitWord word (c:cs)
| c `elem` " \t\n" = addIfNotEmpty word $ splitWord "" cs
| otherwise = splitWord (word ++ [c]) cs
addIfNotEmpty :: String -> [String] -> [String]
addIfNotEmpty s l = if s == "" then l else s:l
我们需要的是一个解析器。这只是一个以字符串作为输入并返回数据结构作为输出的函数。我将向您展示一种以“combinator”样式创建解析器的简化方法。这意味着我们将用更小的解析器构建我们想要的解析器(通过组合它们)
这不是最好或最有效的方法,但它将演示该技术。而且它不需要任何库
我们将从语言杂注开始,以减少一些样板:
{-# LANGUAGE DeriveFunctor #-}
现在,让我们创建一个数据类型来表示解析函数
data Parser a = P { parser :: String -> Maybe (String, a) } deriving Functor
instance Monad Parser where
return = pure
p >>= f = P (\input -> case parse p input of
Just (rest, x) -> parse (f x) rest
_ -> Nothing)
基本上,解析器是数据包装器下面的一个函数。它的工作方式是将字符串作为输入,如果其条件与字符串开头的字符匹配,则它将使用这些字符,创建a
类型的数据,并返回一个仅包含未使用的输入和新项的。但是,如果条件失败,则只返回Nothing
我们将为解析器类型实现Applicative和Monad,然后我们将能够使用do表示法。这是Haskell(IMHO)最酷的特性之一。我们不会使用应用程序
,但我们需要实例来实现Monad。(尽管Applicative本身就非常棒。)
接下来,我们需要一种创建“基本”解析器的方法。我们将创建一个函数,该函数接受一个Char
谓词,并返回一个解析器,该解析器将接受传递该谓词的单个字符
satisfy :: (Char -> Bool) -> Parser Char
satisfy p = P (\input -> case input of
(x:xs) | p x -> Just (xs, x) -- success!
_ -> Nothing -- failure :(
我们还有很多其他方法可以操作解析器,但我们将坚持解决给定的问题。接下来我们需要的是一种重复解析器的方法。这就是while
功能的用武之地。它将使用一个解析器生成a
类型的项,并重复它,直到失败,将结果累积到一个列表中
while :: Parser a -> Parser [a]
while p = P (\input -> case parse p input of
Nothing -> Just (input, [])
Just (rest, x) -> parse (fmap (x:) (while p)) rest)
我们差不多完成了。我们将创建谓词来区分空白和非空白
isWhitespace c = c == ' ' || c == '\t' || c == '\n'
isNotWhiteSpace = not . isWhitespace
好,现在我们来看看do符号有多棒。首先,我们为单个单词创建一个解析器
word :: Parser String
word = do
c <- (satisfy isNotWhitespace) -- grab the first character
cs <- while (satisfy isNotWhitespace) -- get any other characters
while (satisfy isWhitespace) -- eat the trailing whitespace
return (c:cs)
最后,试试看
main :: IO ()
main = do
let input = " A \t String with many\nspaces."
case parse splitWords input of
Nothing -> putStrLn "failed!"
Just (_, result) -> putStrLn . show $ result
这就是我在ghci中得到的:
λ main
["A","String","with","many","spaces."]
请参阅用空格替换逗号分隔符我本应该更具体一些,但我正在寻找一种不使用库的解决方案。是的,我想尝试并避免使用库!嗨,谢谢你的精彩回复!我有一个关于你在那里尝试做的eta降低的问题。我认为如果有空列表的情况会很好,但是当我尝试这样做时,它会说它有不同数量的参数,比如这个splitWords[]=[]。有没有办法将该大小写包含在splitWords中?@user6386278空列表不需要大小写。我也不相信这会很好,主要是令人困惑splitWords
采用字符串作为参数,而不是列表
。它可以工作,因为String
相当于[Char]
,但它会混淆事物。因此,使用“
或[]
调用splitWords
会产生相同的结果,但使用字符串更为一致。如果调用splitWords“”
,它将依次调用splitWord“”
,这将与splitWord[]
匹配。请注意,我自己犯了一个错误,使用了[]
,其中“
更合适。
splitWords :: Parser [String]
splitWords = do
while (satisfy isWhitespace) -- eat up any leading whitespace
while word
main :: IO ()
main = do
let input = " A \t String with many\nspaces."
case parse splitWords input of
Nothing -> putStrLn "failed!"
Just (_, result) -> putStrLn . show $ result
λ main
["A","String","with","many","spaces."]