在每个位置创建包含新元素的列表列表

时间:2017-08-31 08:18:39

标签: list haskell insert

我是haskell世界的新手,我想知道如何在haskell中列表的每个位置插入一个值,并返回包含每个位置值的子列表。例如:

insert' :: a -> [a] -> [[a]]
insert' a [] = [[a]]
insert' a list = ??

获得类似的内容:

insert' 7 [1,2,3] = [[7,1,2,3],[1,7,2,3],[1,2,7,3],[1,2,3,7]]

3 个答案:

答案 0 :(得分:8)

insert' :: a -> [a] -> [[a]]
insert' y [] = [[y]]
insert' y xss@(x:xs) = (y : xss) : map (x :) (insert' y xs)

虽然空列表案例很自然,但让我们来看看insert' y xss@(x:xs)。我们基本上有两个需要涵盖的案例:

  1. y出现在x前面。然后我们可以使用y : xss
  2. y出现在x之后的某个地方。因此,我们只需将其插入列表的其余部分,并确保xmap (x:)的第一个元素。

答案 1 :(得分:0)

虽然@delta's answer肯定更优雅,但这里是差异列表的解决方案。如果我们在列表x的每个位置插入元素ys = [y1,y2,...,yn],我们第一次将其作为头插入,这意味着我们可以构建x : ys

。对于结果列表的第二个元素,我们要构建一个列表[y1,x,y2,...,yn]。我们可以像y1 : x : y2s那样执行此操作。下一个列表都将具有结构y1 : ...

问题是:我们如何编写一个递归结构来跟踪我们想要将元素放在头部的事实。我们可以使用一个函数:我们从函数id开始。如果我们现在调用id (x:ys),那么我们当然会生成列表(x:ys)

然而,我们可以基于id函数构造一个新函数id2 = \z -> id (y1:z)。因此,此函数将y1放在列表的头部,然后添加我们称之为id2的列表作为尾部。接下来我们可以构建id3 = \z -> id2 (y2:z)。这会将y1y2作为第一个元素,后跟尾部z

因此我们可以将其置于以下递归格式中:

insert' :: a -> [a] -> [[a]]
insert' x = go id
    where go d [] = [d [x]]
          go d ys@(yh:yt) = (d (x : ys)) : go (d . (yh :)) yt

因此我们将insert'重定向到go,其中初始差异列表只是id函数。每次我们检查是否已到达给定列表的末尾。如果是这种情况,我们返回基类:我们在差异列表上调用[x](作为尾部),从而构建一个列表,我们将x作为最后一个元素附加。

如果我们还没有到达最后一个元素,我们将首先发出d (x : ys):我们将x添加到列表中,并将其作为参数提供给差异列表dd会将y1 : y2 : ... : yk添加到我们插入x的位置。此外,我们在列表的尾部递归go (d . (yh :)) yt:我们因此构造一个新的差异列表,我们将(yh :)作为列表的尾部插入。因此,我们使用一个参数生成一个新函数:yh元素之后的尾部。

此函数产生预期结果:

*Main> insert' 4 []
[[4]]
*Main> insert' 4 [1,2,5]
[[4,1,2,5],[1,4,2,5],[1,2,4,5],[1,2,5,4]]
*Main> insert' 7 [1,2,3]
[[7,1,2,3],[1,7,2,3],[1,2,7,3],[1,2,3,7]]

答案 2 :(得分:0)

你也可以这样做;

import Data.List

spread :: a -> [a] -> [[a]]
spread x xs = zipWith (++) (inits xs) ((x:) <$> tails xs)

*Main> spread 7 [1,2,3]
[[7,1,2,3],[1,7,2,3],[1,2,7,3],[1,2,3,7]]
*Main> spread 7 []
[[7]]

所以这大约是三个阶段。

  1. (x:) <$> tails xs就是将(x:)函数应用于tails xs函数的所有元素。因此tails [1,2,3]将返回[[1,2,3],[2,3],[3],[]],我们将应用fmap<$>指定的内嵌形式。这将是zipWith函数的第三个参数。
  2. 将返回(inits xs)
  3. [[],[1],[1,2],[1,2,3]]将成为zipWith的第二个参数。
  4. zipWith (++)显然会通过连接列表元素来压缩两个列表列表。
  5. 因此我们也可以使用applicative function functors表达相同的功能,如下所示;

    spread :: a -> [a] -> [[a]]
    spread x = zipWith (++) <$> inits <*> fmap (x:) . tails
    

    在这种情况下,我们fmap zipWith (++)函数的[[a]] -> [[a]] -> [[a]]类型超过inits,然后将其应用于fmap (x:) . tails

    它可以获得更多的免费,但阅读变得更加复杂(至少对我而言)。在我看来,这是最好的。