【问题标题】:Word count program in HaskellHaskell 中的字数统计程序
【发布时间】:2012-04-19 08:20:45
【问题描述】:

我正在复习过去的考试,为即将到来的考试做准备,在完成了几个问题后,我遇到了一个我无法解决的问题。

它需要一个函数,该函数将接受一个字符串(或 [Char])并返回一个包含字符串中英文单词数量的 Int。它说 isWord 是一个假设函数,它接受一个字符串并根据单词是真还是假返回一个布尔值。 单词必须排成一行,从左到右。给出的例子是“目录”。所以“cat”、“at”、“catalog”、“ogre”和“log”,函数应该返回5。

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

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

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

【问题讨论】:

    标签: haskell


    【解决方案1】:

    如果您知道如何区分单词和非单词,您可以使用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",""]
    

    【讨论】:

    • 可能与nub结合使用,取决于是否应计算“banana”中的两个“an”。
    • $ :: (a -> b) -> a -> b 是右关联函数应用:f $ g $ h x = f (g (h x))
    • concatMapconcat 应用于map 的结果:concatMap f xs = concat $ map f xs
    • 哦,你可以把这个打到inits <=< tails
    【解决方案2】:

    那个问题陈述有点含糊。我将做一些没有明确说明的假设——一个词可以是另一个词的前缀,并且每次重复的词都算在内。

    然后,要解决这样的问题,请将其分解为多个部分。你已经做了一点,但你似乎没有跟进它的代码。 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.Listinits 中,但再次尝试自己编写。

    -- 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 的部分,正如其他答案所示。希望我能够真正传达出一种感觉,即如何推理必要的步骤,以及如何使用这些步骤来构建您的代码。

    【讨论】:

      【解决方案3】:

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

      阅读the libraries that come with GHC 是个好主意,尤其是base。即使您在考试中不被允许使用这些功能,阅读源代码仍然很有用,有时也很有启发性(点击类型签名右侧的“Source”链接)。


      编辑:cmets 是对的,Matvey 的回答也是如此。您可以不接受我的回答而接受 Matvey 的回答。

      【讨论】:

      • 我也这么认为,但是子序列不一定是连续的,他只需要连续的子序列。
      • 这不是subsequences - 它只需要连续的子序列。
      • subsequences "hhi",例如其中,"hi" 将有两个结果(一个是第一次出现 h,一个是第二次出现),最终计算出两个单词而不是一个单词。
      • 基本上是我需要的,除了subsequences "abc" 会返回像"ac" 这样不允许的东西。
      【解决方案4】:
      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] 最后接受一个谓词,一个列表,并返回一个仅包含满足谓词的元素的列表。

      【讨论】:

        【解决方案5】:

        这是另一个解决方案,它不使用任何花哨的 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 函数产生错误的所有内容,如下所示:

          > filter isWord (candidates "catalogre")
          ["catalog", "ogre", "cat", "log", "at"]
          

        下面是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)
        

        【讨论】:

          猜你喜欢
          • 2015-02-04
          • 1970-01-01
          • 2012-04-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多