Haskell中的字数统计程序

时间:2012-04-19 08:20:46

标签: haskell

我正在考试为即将到来的考试而考试,在完成几个问题后,我遇到了一个我无法解决的问题。

它需要一个函数,它将接受一个String(或[Char])并返回一个字符串中英文单词数量的Int。它表示isWord是一个假设函数,它接受一个String并返回一个布尔值,具体取决于该单词是true还是false。 单词必须连续,从左到右。给出的例子是“catalogre”。所以“cat”,“at”,“catalog”,“ogre”和“log”,函数应该返回5。

wordsInString :: [Char] -> Int
wordsInString [] = 0
wordsInString x
    | isWord (take 1 x)
    | isWord (take 2 x)

保险杠只是显示我的想法,显然它不会起作用。

这就是我的开始,我想我可以使用take函数并一次递增每个字母,然后将起始字母向下移动到[],但我不是'确定如何正确实现该递归。如果有人有任何想法或可以告诉我一个方法,那就太好了。

5 个答案:

答案 0 :(得分:7)

如果您知道如何区分单词和非单词,则可以使用initstails获取所有可能候选人的列表:

> :m +Data.List
> concatMap inits $ tails "catalogre"
["","c","ca","cat","cata","catal","catalo","catalog","catalogr","catalogre","","a","at","ata","atal","atalo","atalog","atalogr","atalogre","","t","ta","tal","talo","talog","talogr","talogre","","a","al","alo","alog","alogr","alogre","","l","lo","log","logr","logre","","o","og","ogr","ogre","","g","gr","gre","","r","re","","e",""]

答案 1 :(得分:4)

该问题陈述有点模糊。我将做一些未明确说明的假设 - 一个单词可以是另一个单词的前缀,并且每次重复的单词都会计数。

然后,为了解决这样的问题,将其分解为部分。你已经完成了一些这方面的工作,但你似乎没有跟进代码。 Haskell的一个强大功能是您的代码结构通常会遵循您的思想结构。

因此,您已经明确决定要生成所有适当的子字符串进行测试,然后计算结果。让我们首先将其放入代码中。

wordCount :: String -> Int
wordCount = length . findWords

findWords :: String -> [String]
findWords = filter isWord . makeSubstrings

makeSubstrings :: String -> [String]
makeSubstrings xs = undefined -- hmm, this isn't clear yet

确定。这是一个起点。它归结为问题的核心。你打算如何测试所有候选子串呢?

嗯,你的问题已经显示了必要的想法。只需将它们分解成足够小的碎片就可以看到它们是如何做到的。你提到想要从字符串中的每个起始位置做一些事情。那么如何编写一个函数,从每个位置开始返回字符串,并结束?这似乎是合乎逻辑的第一步。

-- for the input "foo", this should return the list ["foo", "oo", "o", ""]
tails :: String -> [String]
tails = undefined -- I'll leave this one up to you

名称的选择不是任意的。有一个功能在Data.List中已经完成了这个功能,但你应该自己实现它,只是为了看看它是如何完成的。

但是你清楚地看到你需要查看那些的所有前缀,你的想法是为了拍摄。因此,编写另一个函数来生成字符串的所有前缀。这也存在于Data.List inits中,但请再次尝试自己编写。

-- for the input "foo", this should return the list ["", "f", "fo", "foo"]
inits :: String -> [String]
inits = undefined - again, this is up to you

而且,对于mapconcat,这些与实现makeSubstrings所需的部分相加,正如其他答案所示。希望我能真正传达一种如何推理必要步骤的感觉,以及如何使用这些步骤来构建代码。

答案 2 :(得分:2)

您正在从Data.List中寻找subsequences函数。

阅读the libraries that come with GHC,特别是基础,这是一个好主意。即使您不允许在考试中使用这些功能,阅读源代码仍然有用,有时也很有启发性(按照类型签名右侧的“源”链接)。


编辑:评论是正确的,Matvey的答案也是如此。你可以不接受我的回答,而是接受Matvey的。

答案 3 :(得分:1)

allWordsInString :: [Char] -> [[Char]]
allWordsInString = filter isWord . concat . map tails . inits
--                                 ^^^^^^^^^^^^^^^^^^ or, concatMap tails

wordsInString :: [Char] -> Int
wordsInString = length . allWordsInString

我会建议这样的事情,因为知道你给定字符串中的英文单词可能会很有趣。

(.)是功能组合。 concat :: [[a]] -> [a]展开列表,例如concat [[1,2], [], [3] == [1,2,3]inits返回给定列表的所有可能的初始前缀tails,后缀相同。 filter :: (a -> Bool) -> [a] -> [a]最终获取谓词,列表,并返回仅包含满足谓词的元素的列表。

答案 4 :(得分:0)

这是另一种解决方案,它不使用任何花哨的Haskell功能,而不是连接列表,计算列表的长度,获取列表的尾部 - 以及递归。

这个想法是这样的:

  1. 首先编写一个函数candidatesWithLength :: Int -> String -> [String],它给出一个项长度和一些字符串,然后生成一个包含该长度的所有项的列表,这样它的行为如下:

    > candidatesWithLength 3 "Foo"
    ["Foo"]
    > candidatesWithLength 2 "Foo"
    ["Fo", "oo"]
    > candidatesWithLength 1 "Foo"
    ["F", "o", "o"]
    
  2. 然后,使用上面的candidatesWithLength函数,编写一个函数candidates :: String -> [String],它产生给定字符串的所有“候选”(潜在的单词)。该函数只是构建一个长列表,所有长度为1的候选者插入长度为2的候选者,加上长度为3的候选者,依此类推。它的行为如下:

    > candidates "Foo"
    ["Foo", "Fo", "oo", "F, "o", "o"]
    
  3. 如果你有这个,你可以使用返回列表中的现有filter函数,以便跳过给定isWord函数产生错误的所有内容,如下所示:< / p>

    > filter isWord (candidates "catalogre")
    ["catalog", "ogre", "cat", "log", "at"]
    
  4. 以下是两种方法candidatesWithLengthcandidates的实现,它们没有使用太多花哨的功能:

    candidatesWithLength :: Int -> String -> [String]
    candidatesWithLength len s
        | len > (length s) = []
        | otherwise        = go s (length s - len + 1)
        where go _ 0 = []
              go s' movesLeft = take len s' : go (tail s') (movesLeft - 1)
    
    candidates :: String -> [String]
    candidates s = go (length s)
        where go 0 = []
              go itemLength = candidatesWithLength itemLength s ++ go (itemLength - 1)