String haskell从字符串中删除空格,并将每个单词分隔在一个列表中

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

借助于,我可以成功地删除字符串中的空白,但是在我的例子中,我还需要将单词分开,并将它们全部放在一个列表中,如下面的示例所示

输入

“包含多个\n空间的\t字符串。”

将输出

[“A”,“String”,“with”,“many”,“spaces.”

我可以输出这个

使用以下代码

> 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."]