几周前,我们开始了一篇关于Haskell的论文,刚刚收到了我们的第一份作业。我知道SO不喜欢家庭作业问题,所以我不会问怎么做。相反,如果有人能用这个问题把我推向正确的方向,我将不胜感激。鉴于这可能不是一个特定的问题,它在讨论/社区wiki中更合适吗?
问题:标记一个字符串,即:"你好,世界!"->["你好","世界"]
来自Java背景,我不得不忘记一切关于这方面的常规方法。问题是,我对哈斯克尔仍然一无所知。这就是我想到的:
module Main where
main :: IO()
main = do putStrLn "Type in a string:n"
x <- getLine
putStrLn "The string entered was:"
putStrLn x
putStrLn "n"
print (tokenize x)
tokenize :: String -> [String]
tokenize [] = []
tokenize l = token l ++ tokenize l
token :: String -> String
token [] = []
token l = takeWhile (isAlphaNum) l
第一个明显的错误是什么?非常感谢。
第一个明显的错误是
tokenize l = token l ++ tokenize l
(++) :: [a] -> [a] -> [a]
追加两个相同类型的列表。由于token :: String -> String
(和type String = [Char]
),从该行推断出的tokenize
的类型是tokenize :: String -> String
。您应该在此处使用(:) :: a -> [a] -> [a]
。
这一行中的下一个错误是,在递归调用中,您再次传递相同的输入l
,因此您有一个无限递归,总是做同样的事情而不做更改。您必须从递归调用的参数输入中删除第一个标记(以及更多标记)。
另一个问题是token
假定输入以字母数字字符开头。
您还需要一个函数来确保传递给token
的条件。
这一行产生了一个无限列表(这是可以的,因为Haskell是懒惰的,所以列表只能"按需"构建),因为它是重复的,参数没有变化:
tokenize l = token l ++ tokenize l
当标记化被称为:时,我们可以可视化正在发生的事情
tokenize l = token l ++ tokenize l
= token l ++ (token l ++ tokenize l)
= token l ++ (token l ++ (token l ++ tokenize l))
= ...
为了阻止这种情况的发生,您需要将参数更改为tokenize
,以便它合理地重复出现:
tokenize l = token l ++ tokenize <something goes here>
正如其他人已经指出你的错误一样,只需一点提示:虽然你已经发现了非常有用的takeWhile
函数,但你应该看看span
,因为这在这里可能会更有帮助。
这里面有一些类似于解析器monad的东西。然而,由于您是Haskell的新手,您不太可能完全理解解析monad的工作原理(或在代码中使用它们)。为了给你基本的东西,考虑一下你想要什么:
tokenize :: String -> [String]
这会获取一个字符串,将其压缩成更多的片段,并生成与输入字符串中的单词相对应的字符串列表。我们如何代表这一点?我们想要做的是找到一个处理单个字符串的函数,在空白的第一个符号处,将该字符串添加到单词序列中。但是然后你必须处理剩下的东西。(例如,字符串的其余部分。)例如,假设您想标记化:
棕色狐狸跳过
您首先拉出"The",然后继续处理"brown fox jump"(注意第二个字符串开头的空格)。您将递归地执行此操作,因此自然需要一个递归函数。
突出的自然解决方案是,在累积到目前为止已经标记的一组字符串的地方,不断地咀嚼当前输入,直到你碰到空白,然后也累积你在当前字符串中看到的内容(这导致了一种实现,你主要考虑内容,然后偶尔反转内容)。
你的练习对我来说有点挑战,所以我决定解决它只是为了自我训练。以下是我的想法:
import Data.List
import Data.Maybe
splitByAnyOf yss xs =
foldr (ys acc -> concat $ map (splitBy ys) acc) [xs] yss
splitBy ys xs =
case (precedingElements ys xs, succeedingElements ys xs) of
(Just "", Just s) -> splitBy ys s
(Just p, Just "") -> [p]
(Just p, Just s) -> p : splitBy ys s
otherwise -> [xs]
succeedingElements ys xs =
fromMaybe Nothing . find isJust $ map (stripPrefix ys) $ tails xs
precedingElements ys xs =
fromMaybe Nothing . find isJust $ map (stripSuffix ys) $ inits xs
where
stripSuffix ys xs =
if ys `isSuffixOf` xs then Just $ take (length xs - length ys) xs
else Nothing
main = do
print $ splitBy "!" "Hello, World!"
print $ splitBy ", " "Hello, World!"
print $ splitByAnyOf [", ", "!"] "Hello, World!"
输出:
["Hello, World"]
["Hello","World!"]
["Hello","World"]