Haskell Assignment-将字符串拆分为单词所需的方向



几周前,我们开始了一篇关于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"]

最新更新