从字符串中删除空格并将每个单词放在列表中,haskell
Removing whitespace from a string and putting each word separated in a list, haskell
感谢Remove white space from string,我可以成功地删除字符串中的空格,但在我的例子中,我还需要将单词分开并将它们全部放在一个列表中,如下例所示。
输入
" A \t String with many\nspaces."
会输出
["A","String","with","many","spaces."]
我可以输出这个
["","A","","","","String","with","many"]
使用以下代码
> 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,所以不使用其他库的解决方案将是理想的!
需要自己动手吗?如果不是,请使用 Data.String.words
.
λ words " A \t String with many\nspaces."
["A","String","with","many","spaces."] :: [String]
words
定义为:
words :: String -> [String]
words s = case dropWhile Char.isSpace s of
"" -> []
s' -> w : words s''
where (w, s'') = break Char.isSpace s'
编辑: 未使用 Data.String 函数。
你离得不远。
首先,您遗漏了输出中的最后一个词。
您可以通过将行 splitWord _ [] = []
更改为 splitWord word [] = [word]
.
来解决该问题
下一个问题是添加到列表中的空字符串。你需要过滤掉它们(我做了一个顶层函数来演示):
addIfNotEmpty :: String -> [String] -> [String]
addIfNotEmpty s l = if s == "" then l else s:l
使用此功能:
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
太棒了!有用。但是等等,我们还没有完成!
整理中
让我们从 splitWords
开始。这里没什么可做的,但我们可以使用 eta-reduction:
splitWords :: String -> [String]
splitWords = splitWord ""
接下来,请注意对于每种类型的 space,操作都是相同的。让我们删除重复项:
splitWord word (c:cs)
| c `elem` " \t\n" = addIfNotEmpty word $ splitWord "" cs
| otherwise = splitWord (word ++ [c]) cs
我在这里使用 elem
来检查下一个字符是否是 space,可以说有更好的方法来做到这一点。
最终结果:
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
我们需要的是解析器。这只是一个将字符串作为输入,returns 数据结构作为输出的函数。我将向您展示一种创建 "combinator" 样式的解析器的简化方法。这意味着我们将从较小的解析器(通过组合它们)构建我们想要的解析器。
这不是执行此操作的最佳或最有效的方法,但它将演示该技术。而且它不需要任何库!
我们将从语言编译指示开始,以减少一些样板文件:
{-# LANGUAGE DeriveFunctor #-}
现在让我们创建一个数据类型来表示解析函数。
data Parser a = P { parser :: String -> Maybe (String, a) } deriving Functor
基本上,解析器是数据包装器下面的一个函数。它的工作方式是它将一个字符串作为输入,如果它的条件匹配字符串开头的字符,那么它将使用这些字符,创建类型为 a
和 return 的数据Just
包含未使用的输入和新项目。但是,如果条件不合格,那么它只是 returns Nothing
.
我们将为我们的解析器类型实现 Applicative 和 Monad,然后我们将能够使用 do 表示法。这是 Haskell(恕我直言)最酷的功能之一。我们不会使用 Applicative <*>
,但我们需要实例来实现 Monad。 (尽管 Applicative 本身就很棒。)
instance Applicative Parser where
pure x = P (\input -> Just (input, x))
f <*> p = do
f' <- f
p' <- p
return $ f' p'
Monad 需要的关键操作是绑定 (>>=
),它获取第一个解析器的结果并将其提供给 return 第二个解析器的函数。这是组合解析器的最方便的方法。它让我们可以累积(或丢弃)结果,而无需通过解析器函数手动线程化输入。
instance Monad Parser where
return = pure
p >>= f = P (\input -> case parse p input of
Just (rest, x) -> parse (f x) rest
_ -> Nothing)
接下来我们需要一种创建 "primitive" 解析器的方法。我们将创建一个接受 Char
谓词和 return 的解析器的函数,该解析器将接受传递谓词的单个字符。
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-notation 有多棒。首先我们为单个单词创建一个解析器。
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)
我们终于可以实现我们真正想要的解析器了!
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
这是我在 ghci 中得到的:
λ main
["A","String","with","many","spaces."]
感谢Remove white space from string,我可以成功地删除字符串中的空格,但在我的例子中,我还需要将单词分开并将它们全部放在一个列表中,如下例所示。
输入
" A \t String with many\nspaces."
会输出
["A","String","with","many","spaces."]
我可以输出这个
["","A","","","","String","with","many"]
使用以下代码
> 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,所以不使用其他库的解决方案将是理想的!
需要自己动手吗?如果不是,请使用 Data.String.words
.
λ words " A \t String with many\nspaces."
["A","String","with","many","spaces."] :: [String]
words
定义为:
words :: String -> [String]
words s = case dropWhile Char.isSpace s of
"" -> []
s' -> w : words s''
where (w, s'') = break Char.isSpace s'
编辑: 未使用 Data.String 函数。
你离得不远。
首先,您遗漏了输出中的最后一个词。
您可以通过将行 splitWord _ [] = []
更改为 splitWord word [] = [word]
.
下一个问题是添加到列表中的空字符串。你需要过滤掉它们(我做了一个顶层函数来演示):
addIfNotEmpty :: String -> [String] -> [String]
addIfNotEmpty s l = if s == "" then l else s:l
使用此功能:
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
太棒了!有用。但是等等,我们还没有完成!
整理中
让我们从 splitWords
开始。这里没什么可做的,但我们可以使用 eta-reduction:
splitWords :: String -> [String]
splitWords = splitWord ""
接下来,请注意对于每种类型的 space,操作都是相同的。让我们删除重复项:
splitWord word (c:cs)
| c `elem` " \t\n" = addIfNotEmpty word $ splitWord "" cs
| otherwise = splitWord (word ++ [c]) cs
我在这里使用 elem
来检查下一个字符是否是 space,可以说有更好的方法来做到这一点。
最终结果:
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
我们需要的是解析器。这只是一个将字符串作为输入,returns 数据结构作为输出的函数。我将向您展示一种创建 "combinator" 样式的解析器的简化方法。这意味着我们将从较小的解析器(通过组合它们)构建我们想要的解析器。
这不是执行此操作的最佳或最有效的方法,但它将演示该技术。而且它不需要任何库!
我们将从语言编译指示开始,以减少一些样板文件:
{-# LANGUAGE DeriveFunctor #-}
现在让我们创建一个数据类型来表示解析函数。
data Parser a = P { parser :: String -> Maybe (String, a) } deriving Functor
基本上,解析器是数据包装器下面的一个函数。它的工作方式是它将一个字符串作为输入,如果它的条件匹配字符串开头的字符,那么它将使用这些字符,创建类型为 a
和 return 的数据Just
包含未使用的输入和新项目。但是,如果条件不合格,那么它只是 returns Nothing
.
我们将为我们的解析器类型实现 Applicative 和 Monad,然后我们将能够使用 do 表示法。这是 Haskell(恕我直言)最酷的功能之一。我们不会使用 Applicative <*>
,但我们需要实例来实现 Monad。 (尽管 Applicative 本身就很棒。)
instance Applicative Parser where
pure x = P (\input -> Just (input, x))
f <*> p = do
f' <- f
p' <- p
return $ f' p'
Monad 需要的关键操作是绑定 (>>=
),它获取第一个解析器的结果并将其提供给 return 第二个解析器的函数。这是组合解析器的最方便的方法。它让我们可以累积(或丢弃)结果,而无需通过解析器函数手动线程化输入。
instance Monad Parser where
return = pure
p >>= f = P (\input -> case parse p input of
Just (rest, x) -> parse (f x) rest
_ -> Nothing)
接下来我们需要一种创建 "primitive" 解析器的方法。我们将创建一个接受 Char
谓词和 return 的解析器的函数,该解析器将接受传递谓词的单个字符。
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-notation 有多棒。首先我们为单个单词创建一个解析器。
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)
我们终于可以实现我们真正想要的解析器了!
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
这是我在 ghci 中得到的:
λ main
["A","String","with","many","spaces."]