【问题标题】:haskell words from alphabet of a given length给定长度的字母表中的haskell单词
【发布时间】:2020-11-17 10:56:15
【问题描述】:

我有这个函数可以生成一个包含最小长度为 0 和最大长度为 n 的所有单词的列表,等于函数的输入:

import Data.List

words :: Int -> String -> [String]
words 0 alph = [[]]
words n alph = words (n-1) alph ++ [ ch:w | w <-words (n-1) alph, ch <- alph]

当我运行它时,输出如下:

> words 3 "AB"
["","A","B","A","B","AA","BA","AB","BB","A","B","AA","BA","AB","BB","AA","BA","AB","BB","AAA","BAA","ABA","BBA","AAB","BAB","ABB","BBB"]

这里的问题是,在这个例子中,有一些单词重复,尤其是长度为 2 的单词(“AA”是 3 次)。你能看出我在我的函数中做错了什么吗,或者你知道如何解决它吗?

【问题讨论】:

  • 列表理解中的 words (n-1) alph 不仅包含长度为 n-1 的单词,而且长度更小,因为您如何定义 words

标签: haskell


【解决方案1】:

这是因为列表解析中的 words (n-1) alph 不仅会产生长度为 n-1 的单词,还会产生 n-2n-3 等,因为这是您定义 words 函数的方式。

最好创建一个仅生成长度为 n 的单词的辅助函数,然后在构造长度不超过 n 的字符串的额外函数中使用它:

words :: Int -> String -> [String]
words 0 alph = [[]]
words n alph = [ ch:w | w <-words (n-1) alph, ch <- alph]

wordsUpTo :: Int -> String -> [String]
wordsUpTo n alph = concatMap (flip words alph) [0 .. n]

但是words已经存在,这只是replicateM :: Applicative m = &gt; Int -&gt; m a -&gt; m [a]的一个特例,所以我们可以这样写:

import Control.Monad(replicateM)

wordsUpTo :: Int -> String -> [String]
wordsUpTo n alph = [0 .. n] >>= (`replicateM` alph)

这将产生:

Prelude Control.Monad> wordsUpTo 3 "AB"
["","A","B","AA","AB","BA","BB","AAA","AAB","ABA","ABB","BAA","BAB","BBA","BBB"]

【讨论】:

    【解决方案2】:

    列表的Applicative 实例有效地计算了叉积,

    > (,) <$> ["A", "B"] <*> ["C", "D"]
    [("A","C"),("A","D"),("B","C"),("B","D")]
    

    可以用(++)而不是(,)连接的元素:

    > (++) <$> ["A", "B"] <*> ["C", "D"]
    ["AC","AD","BC","BD"]
    

    如果你反复应用这个操作,你会得到你想要的字符串:

    > (++) <$> ["A", "B"] <*> [""] -- base case
    ["A","B"]
    > (++) <$> ["A", "B"] <*> ["A","B"]
    ["AA","AB","BA","BB"]
    > (++) <$> ["A", "B"] <*> ["AA","AB","BA","BB"]
    ["AAA","AAB","ABA","ABB","BAA","BAB","BBA","BBB"]
    

    您要重复的功能是((++) &lt;$&gt; ["A", "B"] &lt;*&gt;) 部分,从现在开始我们将其称为f

    > f = ((++) <$> ["A", "B"] <*>)
    

    这个重复的应用程序被iterate 函数捕获,该函数重复地将一个函数应用程序的输出作为下一个函数应用程序的输入。

    > take 3 $ iterate f [""]
    [[""],["A","B"],["AA","AB","BA","BB"]]
    

    我们希望将结果连接到一个列表中:

    > take 7 $ concat $ iterate f [""]
    ["","A","B","AA","AB","BA","BB"]
    

    所以所有组合只是

    allWords alph = concat $ iterate f [""]
      where f = ((++) <$> alph <*>)
    

    要获得最大长度的元素,我们可以

    1. 使用takeWhile (\x -&gt; length x &lt;= n),或
    2. 使用take (2^(n+1) - 1)(给定项目的生成顺序,给定长度的所有字符串都出现在更长的字符串之前,我们可以计算给定最大长度的字符串总数)

    所以我们可以定义任何一个

    words n = takeWhile p . allWords
      where p x = length x < 4
    

    words n = take n' . allWords
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      • 2014-05-04
      • 2014-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-16
      相关资源
      最近更新 更多