【问题标题】:Haskell Assignment - direction needed to split a String into wordsHaskell 赋值 - 将字符串拆分为单词所需的方向
【发布时间】:2012-03-28 12:45:36
【问题描述】:

几周前我们开始了一篇关于 Haskell 的论文,并且刚刚收到我们的第一个任务。我知道 SO 不喜欢家庭作业问题,所以我不会问如何去做。相反,如果有人能以此推动我朝着正确的方向前进,将不胜感激。鉴于这可能不是一个具体的问题,在讨论/社区 wiki 中是否更合适?

问题:标记一个字符串,即:“Hello, World!” -> [“你好”,“世界”]

来自 Java 背景,我不得不忘记所有关于通常方法的事情。问题是我对 Haskell 仍然一无所知。这是我想出的:

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

第一个明显的错误是什么? 谢谢。

【问题讨论】:

  • Stack Overflow 确实喜欢家庭作业问题,只要你适当地标记它们并展示你已经尝试过的东西(这是一种努力,即使被误导了)。这个没问题。
  • 在您的 Java 背景中,您还没有完全忘记的一件事是,变量在 Haskell 中是不可变的。您似乎希望表达式 token l 将修改 l。不会的。

标签: haskell


【解决方案1】:

第一个明显的错误是

tokenize l = token l ++ tokenize l

(++) :: [a] -&gt; [a] -&gt; [a] 附加两个相同类型的列表。由于token :: String -&gt; String(和type String = [Char]),从该行推断出的tokenize 的类型是tokenize :: String -&gt; String。 你应该在这里使用(:) :: a -&gt; [a] -&gt; [a]

该行的下一个错误是,在递归调用中,您再次传递了相同的输入 l,因此您有一个无限递归,始终执行相同的操作而没有更改。您必须从递归调用的参数的输入中删除第一个标记(以及更多标记)。

另一个问题是您的token 假设输入以字母数字字符开头。

您还需要一个函数来确保您传递给token 的内容的条件。

【讨论】:

  • 我会在早上回来讨论这个问题。凌晨2点不好。 +1 虽然。
【解决方案2】:

这行导致一个无限列表(这没关系,因为 Haskell 是惰性的,所以该列表只是“按需”构建的),因为它是重复的,参数没有变化:

tokenize l = token l ++ tokenize l

我们可以想象当 tokenize 被调用时发生了什么:

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>

【讨论】:

  • 如果我在递归调用之前dropthe length of the token from l + 1会起作用吗?
【解决方案3】:

正如其他人已经指出你的错误,只是一点提示:虽然你已经发现了非常有用的 takeWhile 函数,但你应该看看 span,因为这在这里可能会更有帮助。

【讨论】:

    【解决方案4】:

    这里面有一些感觉类似于解析器单子的东西。但是,由于您是 Haskell 的新手,您还不太可能了解解析 monad 的工作原理(或在代码中使用它们)。为您提供基础知识,请考虑您想要什么:

    tokenize :: String -> [String]
    

    这需要一个字符串,将其分解成更多部分,并生成与输入字符串中的单词对应的字符串列表。我们如何表示这一点?我们要做的是找到一个处理单个字符串的函数,并在第一个空格符号处将该字符串添加到单词序列中。但是然后你必须处理剩下的东西。 (即字符串的其余部分。)例如,假设您要标记化:

    棕狐跳了

    你先拔出“The”,然后继续处理“brown fox jumped”(注意第二个字符串开头的空格)。您将递归地执行此操作,因此自然需要递归函数。

    突出的自然解决方案是在您累积到目前为止已标记化的一组字符串的位置获取一些东西,继续咀嚼当前输入直到您点击空格,然后也累积您的' 已经在当前字符串中看到(这会导致您在实现中主要使用内容,然后偶尔反转内容)。

    【讨论】:

      【解决方案5】:

      你的练习对我来说似乎有点挑战性,所以我决定解决它只是为了自我训练。这是我想出的:

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

      【讨论】:

        猜你喜欢
        • 2011-06-12
        • 2014-10-21
        • 1970-01-01
        • 1970-01-01
        • 2014-06-09
        • 1970-01-01
        • 2022-01-18
        • 2011-10-23
        • 2011-11-03
        相关资源
        最近更新 更多