在Haskell中,是否可以使用模式来匹配长度?

时间:2018-07-29 15:46:39

标签: haskell grouping

我有一个函数,它将获取一个列表并返回指定长度的子列表的列表:

*Main> neighbors 3 [1,2,3,4]
[[1,2,3],[2,3,4]]

neighbors :: Int -> [a] -> [[a]]
neighbors n xs =
    let ys = take n xs
        zs = drop 1 xs
    in if (length ys < n)
       then []
       else ys : neighbors n zs

虽然它可以工作,但对我来说看起来并不特别“ Haskell-y”,因此我认为可能会有模式匹配来完成相同的事情。在那儿?或者,也许我正在错过一种不同的默认方法?我研究了各种拆分库,https://wiki.haskell.org/Let_vs._Where为我提供了使该功能完全起作用的工具。

2 个答案:

答案 0 :(得分:3)

由于可以在运行时传递值,所以此时编写模式将很奇怪:通常将模式转换为某些程序逻辑。除非您编写某种首先生成这种模式的解释程序,否则实际上不可能基于 value 构造模式。

但是我们确实可以改善程序,例如:

neighbors :: Int -> [a] -> [[a]]
neighbors n l = go l (drop (n-1) l)
    where go l@(_:lt) (_:xs) = take n l : go lt xs
          go _ _ = []

工作原理如下,我们定义了一个辅助函数go,该函数在 same 列表上进行迭代,但是在两个位置:第二个列表是 n-1 在第一个位置之前。如果仍有足够的元素来执行take,则此“ 检查”。因此,我们将gol称为drop (n-1) l

如果第二个列表为非空,则我们知道存在take n的“最后”项(并且该位置位于n-1的前面)。因此,在这种情况下,我们返回take n l,并且前进两个指针。从第二个列表用尽的那一刻起,我们知道切片不再可行,因此我们停止发射元素。

优点是,它将在无限列表上运行,因为我们从不调用列表上的length(或任何其他需要运行到列表末尾的函数),而发出下一个切片本身由于take n导致 O(n),但是检查是否必须发射下一个切片仅花费 O(1)。因此,如果我们仅对第 k 个元素感兴趣,则需要花费 O(k + n)时间。

不使用显式递归,我们可以使用zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]tails :: [a] -> [[a]]

neighbors :: Int -> [a] -> [[a]]
neighbors n l = zipWith (const . take n) (tails l) (drop (n-1) l)

答案 1 :(得分:2)

您不能直接在长度上进行图案匹配,但是可以对其进行后处理。没错,您的“如果长度”位不是非常Haskell-y。如果我是你,我只会产生最多 个N元素的所有尾巴,然后最后过滤掉散乱的人群。

neighbors :: Int -> [a] -> [[a]]
neighbors n xs = filter (\x -> length x == n) $ go xs
    where go [] = []
          go (y:ys) = take n (y:ys) : go ys

这里的我的go基本上是您的neighbors,没有“ if length”位。然后,我过滤掉所有太短的元素。现在,这更好,但是我们仍然可以做得更好。 Data.List模块提供了tails,这将使我们在这种情况下可以消除所有的显式递归。

import Data.List

neighbors :: Int -> [a] -> [[a]]
neighbors n = filter (\x -> length x == n) . map (take n) . tails

tails返回给定列表的所有尾部的列表。让我们来看一个例子。

-- neighbors 3 [1, 2, 3, 4]
tails                               <==> [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4], []]
map (take n) . tails                <==> [[1, 2, 3]   , [2, 3, 4], [3, 4], [4], []]
filter (...) . map (take n) . tails <==> [[1, 2, 3]   , [2, 3, 4]]

现在,我们将问题分为三个漂亮的子问题(产生尾巴,取前N个元素,然后消除散乱子),每个子问题都由几个标准库函数解决。