【问题标题】:How to generate a list of all possible strings from shortest to longest如何生成从最短到最长的所有可能字符串的列表
【发布时间】:2017-03-14 22:37:24
【问题描述】:

我需要使用数字和字母生成无限的字符串列表。第一个字符串应该简单地是“a”,然后是“b”到“z”,然后是“0”到“9”,然后是“aa”、“ab”等。

我可以很容易地生成只有一个字符的那些,但它会变得更加复杂。

【问题讨论】:

  • 有趣但令人困惑的方式:[1..] >>= (`replicateM` "abcdefghijklmnopqrstuvwxyz012346789")
  • 请注意,这在技术上是不可能的。您只能生成所有有限字符串(或所有有限字符串加上几乎没有散布的无限列表。)

标签: haskell list-comprehension


【解决方案1】:

所以,假设我们已经有了所有可能字符串的列表:

 allStrings :: [String]
 allStrings = ...

给定allStrings,我们如何创建另一个包含所有可能字符串的列表?

 alsoAllStrings :: [String]

让我们将每个可能的字符串视为单个字符前缀和字符串后缀

 alsoAllStrings = [ c : s 

字符串后缀要么是空字符串,要么是所有可能字符串列表的成员。

                  | s <- "" : allStrings

单个字符前缀在'a''z''0''9'

                  , c <- ['a'..'z'] ++ ['0'..'9']
                  ]

(这是使用列表推导 - 我们也可以使用 concatMap 来做同样的事情:

alsoAllStrings = concatMap (\s -> map (:s) $ ['a'..'z'] ++ ['0'..'9']) $ "" : allStrings

)

现在让我们回到最初的问题。我们如何找到allStrings

在大多数语言中,我们做不到 - 这是一个无限的字符串列表,程序永远不会完成。但由于 Haskell 是惰性的,因此只生成我们实际使用的 allStrings 就很酷。

这让我们做的一件令人惊讶的事情是,我们可以根据alsoAllStrings 定义allStrings

 allStrings = alsoAllStrings

或者,更可能的是,就其本身而言。

 allStrings = [ c : s | s <- "" : allStrings, c <- ['a'..'z'] ++ ['0'..'9'] ]

这称为核心递归数据——根据自身定义的数据。 在 ghci 中尝试一下:

 ghci> let allStrings = [ c : s | s <- "": allStrings, c <- ['a'..'z'] ++ ['0'..'9'] ]
 ghci> take 100 allStrings
 ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","aa","ba","ca","da","ea","fa","ga","ha","ia","ja","ka","la","ma","na","oa","pa","qa","ra","sa","ta","ua","va","wa","xa","ya","za","0a","1a","2a","3a","4a","5a","6a","7a","8a","9a","ab","bb","cb","db","eb","fb","gb","hb","ib","jb","kb","lb","mb","nb","ob","pb","qb","rb","sb","tb","ub","vb","wb","xb","yb","zb","0b","1b"]

它起作用(并且不仅仅是无限循环)的原因是它在使用自身之前定义了自身的一部分。我们在allStrings 中包含的第一个元素是空字符串"" 的单个字符扩展。因此,当我们开始迭代 allStrings 的元素以用作后缀时,前几个元素 allStrings 已经定义并可用。而且我们处理的后缀越多,allStrings 的元素就越多被定义并可用作后缀。

这与核心递归定义斐波那契数的常见haskellism非常相似:

 fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

如果 corecursion 需要一段时间来绕开你的脑袋,请不要感到惊讶。不过,值得努力去理解。

【讨论】:

    【解决方案2】:

    这可以通过获取长度为 1,2,3... 的字符串的所有组合,然后将它们连接在一起来完成。

    我从一个组合函数开始,它返回具有给定字符和给定长度的所有字符串列表:

    combos :: [Char] -> Int -> [String]
    

    第一种情况很简单,把输入的字符变成列表即可:

    combos chars 1 = map (:[]) chars
    

    下一个案例我们想要 'aa', 'ab', 'ac'... 到 'zx', 'zy', 'zz'。这与map ("a" ++) ["a", "b", "c"...] ++ map ("b" ++) ["a","b",...] 直到map ("z" ++) ["a","b"...] 相同。

    ["a".."z"]combos chars 1 相同。该函数可以写成:

    combos chars 2 = concatMap (\front -> map (front ++) (combos chars 1)) (combos chars 1)
    

    下一个案例我们想要 'aaa', 'aab', ... 'zzy', 'zzz'。这实际上与连击 2 非常相似,只是我们使用 combos chars 2 而不是 combos chars 1 作为前面:

    combos chars 3 = concatMap (\front -> map (front ++) (combos chars 1)) (combos chars 2)
    

    (注意combos chars 1 不会改变,它只是给我们一个字符串列表以追加到末尾。)

    看到这里的模式了吗?现在可以将所有 n 概括为:

    combos :: [Char] -> Int -> [String]
    combos chars 1 = map (:[]) chars
    combos chars n = concatMap (\front -> map (front ++) (combos chars 1)) $ combos chars (n - 1)
    

    最后,要获得所有字符串的无限列表,只需将组合的所有结果与所需字符和所有长度连接在一起即可:

    allCombos :: [String]
    allCombos = concatMap (combos (['a'..'z'] ++ ['0'..'9'])) [1..]
    

    示例输出:

    > take 100 allCombos
    ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","aa","ab","ac","ad","ae","af","ag","ah","ai","aj","ak","al","am","an","ao","ap","aq","ar","as","at","au","av","aw","ax","ay","az","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","ba","bb","bc","bd","be","bf","bg","bh","bi","bj","bk","bl","bm","bn","bo","bp","bq","br","bs","bt","bu","bv","bw","bx","by","bz","b0","b1"]
    

    【讨论】:

    • 鉴于combosreplicateMconcatMap(=&lt;&lt;),你在我的评论中得到了解决方案:)
    • 使用 list monad 处理 monad 函数是我还没有掌握的东西 - 我对那个代码有点只读(我了解它们是如何工作的,但通常不会想到这样写)。我仍然发现filterM (const [True, False]) 是任何语言中最令人惊叹的代码之一。
    • @nanothief:你应该和他们一起玩——有很多乐趣。如果您想要一个不错的挑战,请编写一个简单的 Prolog 解释器。 (Prolog 依赖于回溯和不确定性,这正是列出的模型。)
    【解决方案3】:

    我想出了以下解决方案:

    -- Gives us the concatenated cartesian product of two lists of strings
    strCartProd :: [String] -> [String] -> [String]
    strCartProd xs ys = [x ++ y | x <- xs, y <- ys]
    
    -- Create initial list of strings (map chars to 1-char strings)
    s :: [String]
    s = map (:[]) $ ['a'..'z'] ++ ['0'..'9']
    
    -- Iterate and concatenate the resulting lists
    result :: [String]
    result = id =<< iterate (strCartProd s) [""]
    

    【讨论】:

      【解决方案4】:

      这可以通过 (Control.Monad) 中的预定义函数 replicateM 轻松完成。

      string = concatMap (\n -> replicateM n (['a'..'z']++['0'..'9'])) [1..]
      

      【讨论】:

        猜你喜欢
        • 2019-05-19
        • 1970-01-01
        • 2012-02-28
        • 1970-01-01
        • 2016-07-05
        • 2014-08-30
        • 2013-04-21
        • 2019-05-10
        • 2019-07-18
        相关资源
        最近更新 更多