【问题标题】:Is there a built-in function to get all consecutive subsequences of size n of a list in Haskell?是否有内置函数可以获取 Haskell 中列表的所有大小为 n 的连续子序列?
【发布时间】:2014-07-06 20:08:31
【问题描述】:

例如,我需要一个函数:

gather :: Int -> [a] -> [[a]]
gather n list = ???

gather 3 "Hello!" == ["Hel","ell","llo","ol!"].

我有一个可行的实现:

gather :: Int-> [a] -> [[a]]
gather n list = 
    unfoldr 
        (\x -> 
            if fst x + n > length (snd x) then 
                Nothing 
            else 
                Just 
                    (take 
                        n 
                        (drop 
                            (fst x)
                            (snd x)), 
                    (fst x + 1, snd x))) 
        (0, list)

但我想知道语言中是否已经为此内置了一些东西?我扫描了 Data.List 但什么也没看到。

【问题讨论】:

    标签: haskell


    【解决方案1】:

    你可以使用tails:

    gather n l = filter ((== n) . length) $ map (take n) $ tails l
    

    或使用takeWhile 代替filter

    gather n l = takeWhile ((== n) . length) $ map (take n) $ tails l
    

    编辑:您可以按照 cmets 中的建议删除从 tails 返回的列表的最后一个 n 元素来删除过滤步骤:

    gather n = map (take n) . dropLast n . tails
      where dropLast n xs = zipWith const xs (drop n xs)
    

    【讨论】:

    • 我会使用 takeWhile 而不是 filter,但这是一个相当适度的优化。
    • 这在语义上似乎是正确的,但在操作上有点粗糙:检查每个子列表的长度似乎工作量太大。有一个标准的技巧可以说“获取列表中除了最后一个n 元素之外的所有元素”,即\xs -> zipWith const xs (drop n xs);也许你可以在这里使用这个技巧。
    • @DanielWagner - 感谢您的建议,这样更干净。
    【解决方案2】:

    由于zipping的属性,可以自动安排尾巴的掉落,

    import Data.List (tails)
    
    g :: Int -> [a] -> [[a]]
    g n = foldr (zipWith (:)) (repeat []) . take n . tails
    

    否则一个简单的transpose . take n . tails 就足够了。测试:

    前奏曲数据列表> g 3 [1..10]
    [[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8],[ 7,8,9],[8,9,10]]
    Prelude Data.List> 转置。取 3 。尾巴 $ [1..10]
    [[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8],[ 7,8,9],[8,9,10],[9,10],[10]]


    (edit 2018-09-16:) zipping 的使用可以在更高层次上表达,用traverse ZipList:

    g :: Int -> [a] -> [[a]]
    g n = getZipList . traverse ZipList . take n . tails
    

    【讨论】:

    • 我非常喜欢两者的原因是它们同时处理字符串和 Int 列表。调用参数可以改进,但这并不重要。如果没有额外的逻辑,我的会做 Int 但不是字符串。 .... gv no sz = zipWith enumFromTo [1..no] [sz..] > > > > gv 3 4 用于 3 个四元素列表的列表。
    • 这里的重点是重新排列给定的列表,而不是重新创建它。 zipWith enumFromTo 创建模拟结果的整数列表,但是如果我们必须重新排列 sort ([1,4..20] ++ [1,5..30]) 的结果怎么办?重新排列忽略了元素的本质,只是操纵 列表结构,而不管它包含什么。就像列表是一串盒子(每个盒子里都有一些元素),我们正在使用盒子,而不是使用它们 in 的内容。 (框不是实际保存值,而是保存指向这些值的指针 - 每个框都保存一个指针)。
    • 这就是Haskell中"parametricity"的意思,当我们说“[a]是一个parametric类型,一个'something'的'list' i>,不管那个东西是什么” - something 是参数,a,类型为[] a,即[a]。因此,当我们重新排列一个列表时,我们创建了新的“列表结构”,即新的盒子链,将新指针 指向与原始盒子中相同的值。就像在 Lisp 中一样。
    • 在 Lisp 中,可以使用 set-car!set-cdr! 更改框的指针。在 Haskell 中并非如此。但从概念上讲,这个模型可以防止在重新排列时过度复制值,如果我们说我们有值实际上 in 的盒子(新链将在其盒子中保存值的副本),这将是不可避免的。当然,在 Haskell 中,由于其引用透明性,复制的值无法与指向相同值的新指针(具有正常的 Haskell 值)区分开来,除了 性能 影响(内存大小、速度, GC 数量(垃圾收集)等)。
    • [#t,#t,#t] Racket forwent set-car 等。正如您所说,它违反了 Ref Transparency 和不变性。列表是我现在看到的必需参数。递归函数使用tail。取&长度。我比我的最后一个递归更喜欢以下内容,它可以处理任何列表或字符串,非常感谢。 . . g2 sz ls= [取sz t|t=sz]
    猜你喜欢
    • 1970-01-01
    • 2015-03-10
    • 2013-02-21
    • 2019-08-07
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    • 2019-05-01
    相关资源
    最近更新 更多