如何实现“单词”功能

时间:2013-12-09 22:23:22

标签: string haskell words

在Haskell库中有一个名为“words”的函数,它接受一个字符串并返回一个列表,其中的元素是字符串中的单词。基本上它删除空格并存储单词。例如:

words "Hello there mister person guy" == ["Hello", "there", "mister", "person", "guy"]

现在我们被要求自己实施。到目前为止,我已经尝试了两天,我真的无法想出一个解决方案。我试过在线查找,但实际上找不到任何东西。所以我请你帮忙。

请记住,虽然我是Haskell的初学者,因此不熟悉高级概念,所以如果可能的话,请在解释中详细说明:)。

6 个答案:

答案 0 :(得分:4)

嗯,解决这个问题的方法(或者说真正的大多数问题)就是把它分解成更小的问题,你可以将它们的解决方案结合起来解决你的问题,然后分别解决这些问题。有a standard function from Data.List that you can use to solve this problem

span :: (a -> Bool) -> [a] -> ([a], [a])
     应用于谓词span和列表p

xs返回一个元组,其中第一个元素是满足xs元素的最长前缀(可能为空) p和第二个元素是列表的其余部分。

所以一个可能的策略是:

  1. 使用span将列表分成两部分:不包含空格的最大初始前缀,其余部分
  2. 递归地将此策略应用于字符串的其余部分(忽略任何初始空格)。
  3. 如果您不允许在课堂上使用库函数,那么我要做的就是编写我自己的库函数版本。

答案 1 :(得分:2)

听起来像是家庭作业,所以我不想只写答案,但是......

一些提示 -

  1. 首先编写您的类型签名。在编写函数之前,请在签名上使用Hoogle查看是否存在有用的东西。

  2. 你会想要使用递归(即 - 一旦你找到一个单词,你可以将它从字符串的其余部分拆分,然后在余数上重新应用你的单词函数。)

  3. 记住字符串只是Haskell中的列表,因此请查看Data.List函数并查看可能有用的内容。

答案 2 :(得分:2)

  

基本上它会移除空格并存储单词

我知道您对该陈述的意图,但应该更准确地说明为

  

它将字符串拆分为空格并返回单词列表

这给我们提供了一个非常具体的案例。这个问题归结为找到空间。简单的方法是使用takeWhile

myWords :: String -> [String]
myWords "" = []
myWords text = takeWhile (/= ' ') text : ???

我不会给你完整的解决方案,但这应该可以帮助你开始。你需要弄清楚???的位置。此外,这与words函数不完全相同,因为它还处理重复的空格:

> words "This  is   a test"
["This","is","a","test"]

所以你必须弄清楚如何做到这一点。

答案 3 :(得分:0)

这是基本的想法,不应该太难以把它放在代码上:

  1. 首先从字符串中删除所有前导空格字符
  2. 从字符串的开头累积字符,直到找到空格。这将是第一个字。
  3. 使用字符串的剩余部分返回第1步。

答案 4 :(得分:0)

一开始,你可以在案例/情境中思考,当你遍历一个清单时:

import Data.Char (isSpace)

noWord (x:xs)
  | isSpace x = noWord xs
  -- is still space, do nothing and check next.
  | not (isSpace x) = isWord [x] xs
  -- is a new word - apply isWord. 

noWord [] = []
  -- if the fun ends with space.


isWord akku (x:xs) 
  | not (isSpace x) = isWord (akku ++ [x]) xs
  -- is still a word, so add x to the word and continue.
  | isSpace x = akku : noWord xs
  -- is no more a word, save the word and do some noWord's.

isWord akku [] = akku : []
  -- when list is empty, give the cached word back and end the list.


myWords = noWord
  -- start with no word.


main = print $ myWords " hell  oh world! "
-- try it

给你:

["hell","oh","world!"]

答案 5 :(得分:-1)

势在必行的解决方案

你可以像命令式语言一样使用Haskell,使用if构造和变量赋值。您可以使用递归代替循环。这导致了以下(幼稚)解决方案:

(警告:Haskell大师,请你把目光移开)

wordsRecursive input =
    wordsRecursive2 [] [] input
    where
        wordsRecursive2 words currentWord input =
            if (length input == 0)
            then
                currentWord : words
            else 
                let (i:is) = input in
                if (i == ' ') 
                then
                    wordsRecursive2 (currentWord : words) [] is
                else 
                    wordsRecursive2 words (i:currentWord) is

这段代码很难看。它有许多明显的问题,但主要问题是它使用递归。有一种说法是“递归是函数式编程的GOTO”。所以在可能的情况下,我们应该用一些更高级别的函数替换递归。

使用折叠

的解决方案

在这种情况下,我们可以简单地使用折叠。

wordsFold input = (\ (word, words) -> word:words) $  foldl wordsFold2 ([], []) input
    where
        wordsFold2 (currentWord, words) ' ' = ([], currentWord : words)
        wordsFold2 (currentWord, words) i   = (i : currentWord, words)

此函数使用折叠而不是递归。折叠将“wordsFold2”应用于输入字符串中的每个字符,以及一些中间结果。中间结果是返回每个先前应用的wordsFold2。在这种情况下,它由(部分)当前单词和已找到(完整)单词的列表组成。初始中间结果由空的当前单词和已找到的单词的空列表组成。

在每个步骤中,函数wordsFold2执行以下几个

  1. 将当前单词添加到已找到的单词列表中
  2. 或者它将当前字符添加到(不完整的)当前字词
  3. 它使用哪个选项取决于当前字符的值。

    折叠操作的结果是最后的中间结果。部分(\ (word, words) -> word:words)然后将最后一个单词添加到单词列表中。

    运动

    哦,顺便说一句,因为你应该做自己的功课,我已经为你做了一个练习。我的解决方案将结果“倒退”。你必须稍微调整一下才能得到正确的结果:-P