将元素添加到列表的子列表中

时间:2017-12-08 17:24:15

标签: haskell

我有以下代码:

let numbers = [[1],[2],[3],[4]]

我想将“3”附加到列表([3])的索引2处的子列表中,如下所示:

(numbers !! 2) ++ [3]

得到这个:

numbers = [[1],[2],[3,3],[4]]

但我很困惑:P有什么想法吗?

4 个答案:

答案 0 :(得分:1)

其他方式是通过拍摄和放弃

{{1}}
  1. 取第2个数组 - > [[1],[2]]
  2. 找到目标数组,[3]找到它 - > [[3] ++ [3]]返回[[3,3]]
  3. drop 3 first array - > [[4]]

    [[1],[2]] ++ [[3] ++ [3]] ++ [[4]] = [[1],[2],[3,3],[4] ]

答案 1 :(得分:0)

如果您难以编写递归更新函数,则可以将列表结构转换为具有zip的合成索引数组

zip [0..] [[1],[2],[3],[4]]

并根据索引编写concat版本,就像使用索引数组的其他语言一样

appendAt n a = map (\(i,x) -> if i==n then x++[a] else x) . zip [0..]

然后,

appendAt 2 3 numbers

会给你想要的结果。

答案 2 :(得分:0)

正如chi已经评论过,在现代Haskell中对结构进行此类更新的最佳方法是使用 lense combinators ,具体来说,ix

Prelude Control.Lens> let numbers = [[1],[2],[3],[4]]
Prelude Control.Lens> numbers & ix 2 %~ (++[3])
[[1],[2],[3,3],[4]]

(不要害怕镜头。主lens包很大且依赖性很大,但其大部分杀手功能,包括ix,也包含在兼容和更小microlens package。)

答案 3 :(得分:0)

正如其他人所说,在Haskell中,您通常不会修改数据结构。相反,你将它拆开,引入你的修改,并将它重新组合在一起,以创建一个包含变化的新数据结构。

为了更改特定的列表元素,我发现splitAt函数对于拆分列表很有用。例如,如果将列表拆分为索引2,则会生成以下一对列表:

> splitAt 2 numbers
( [[1],[2]] , [[3],[4]] )  -- spaces added to make it clearer

请注意,您要更改的元素是第二个列表的第一个元素。您可以使用Haskell的模式匹配来方便地处理该对:

> let (a, x:b) = splitAt 2 numbers
> a          -- initial part of list
[[1],[2]]
> x          -- element to change
[3]
> b          -- rest of list
[[4]]
> a ++ x : b              -- put list back together unchanged
[[1],[2],[3],[4]]
> a ++ (x ++ [3]) : b     -- put list together w/ modification
[[1],[2],[3,3],[4]]

如果将其转换为Haskell函数,它可能看起来像:

appendSublist :: Int -> [a] -> [[a]] -> [[a]]
appendSublist idx y lst
  = let (a, x:b) = splitAt idx lst
    in  a ++ (x ++ y) : b

你会像这样使用:

> appendSublist 2 [3] numbers
[[1],[2],[3,3],[4]]

这个基本模式:

let (a, x:b) = splitAt idx lst in a ++ (...replacement for x...) : b

值得记住,因为它非常有用。

最终,您可能希望学习使用lens库进行此类操作。镜头更简洁,更灵活(即,适用于各种事物,而不仅仅是特定的列表项目),但是有一个非常大的学习曲线,你可以从@ leftaroundabout的答案猜测。