我是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]]
答案 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)
。我们基本上有两个需要涵盖的案例:
y
出现在x
前面。然后我们可以使用y : xss
。y
出现在x
之后的某个地方。因此,我们只需将其插入列表的其余部分,并确保x
是map (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)
。这会将y1
和y2
作为第一个元素,后跟尾部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
添加到列表中,并将其作为参数提供给差异列表d
。 d
会将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]]
所以这大约是三个阶段。
(x:) <$> tails xs
就是将(x:)
函数应用于tails xs
函数的所有元素。因此tails [1,2,3]
将返回[[1,2,3],[2,3],[3],[]]
,我们将应用fmap
以<$>
指定的内嵌形式。这将是zipWith
函数的第三个参数。(inits xs)
的[[],[1],[1,2],[1,2,3]]
将成为zipWith
的第二个参数。zipWith (++)
显然会通过连接列表元素来压缩两个列表列表。因此我们也可以使用applicative function functors表达相同的功能,如下所示;
spread :: a -> [a] -> [[a]]
spread x = zipWith (++) <$> inits <*> fmap (x:) . tails
在这种情况下,我们fmap
zipWith (++)
函数的[[a]] -> [[a]] -> [[a]]
类型超过inits
,然后将其应用于fmap (x:) . tails
。
它可以获得更多的免费,但阅读变得更加复杂(至少对我而言)。在我看来,这是最好的。