Haskell按索引更新子列表

时间:2019-01-05 22:42:32

标签: haskell

尝试学习Haskell,最近我几次遇到这种简单的模式:

让我们说我有一个列表,我想更新子列表中的所有元素,例如从索引i到j。

我能想到的所有解决方案都非常糟糕,就像下面的例子一样:

import qualified Data.Sequence as S

increment :: Int -> Int -> S.Seq Int -> S.Seq Int
increment i j sq | i <= j = increment (i + 1) to (S.adjust (+1) i sq)
                 | otherwise = sq

我想我用清单会更糟。

有人知道一些简单的方法吗?我尝试过搜索,还查看了标准库(Data.Array,Data.Vector等),但是它的编写方式使我的眼睛有点流血,我需要一些人的建议

3 个答案:

答案 0 :(得分:3)

  

我尝试过搜索,还查看了标准库(Data.Array,Data.Vector等),但是它的编写方式使我的眼睛有点流血,我希望得到一些人的建议

是的,直接使用Data.Array可以解决涉及索引的问题。对于不可变数组,可以使用accum函数使用(index, value)对列表更新数组元素以指定索引和值。

例如,假设我们需要将索引2到4的数组[1,2,3,4,5,6,7,8,9,10]的元素加1。

首先,从列表[1,2,3,4,5,6,7,8,9,10]构造一个数组:

testArray = let xs = [1..10] in listArray (0, length xs-1) xs

并将accum函数应用于数组:

increment' i j ary = accum (+) ary (zip [i..j] (repeat 1))

请注意,如果(zip [i..j] (repeat 1))给出以下内容,则i = 2 and j = 4会构建一个配对对列表:

[(2, 1), (3, 1), (4, 1)]

pair的第一个值是数组的索引,第二个是(+)的第二个参数,accum检索特定索引的值并将(+1)应用于该值,即正是我们想要的。要对其进行测试:

elems $ increment' 2 4 testArray

给予

[1,2,4,5,6,6,7,8,9,10]

否则,如果您只想用新值替换数组的旧值,而不必关心旧值是什么。 accum函数也可以在这种情况下应用。例如,假设将索引2到4的元素替换为0

accum (flip const) testArray (zip [2..4] (repeat 0))

答案 1 :(得分:2)

一种更有效的方法是在两个端点处拆分序列,在中间部分使用fmap,然后将这些部分重新结合在一起。

increment :: Int -> Int -> S.Seq Int -> S.Seq Int
increment i j sq = sq0 S.>< fmap (+ 1) sq1 S.>< sq2
  where
    (sq01, sq2) = S.splitAt (j + 1) sq
    (sq0, sq1) = S.splitAt i sq01

答案 2 :(得分:0)

我们需要从功能上进行思考。如果我们看一下:

S.adjust :: Int -> (a -> a) -> S.Seq a -> S.Seq a

需要一个索引来调整,应用要进行的调整以及一个序列,然后返回一个新序列。要调整一堆元素,我们调整第一个,然后将第二个调整到第一个调整的结果,依此类推。首先,我们可以列出我们要进行的所有调整:

adjustments :: [S.Seq Int -> S.Seq Int]
adjustments = [ S.adjust i (+1) | i <- [lower..upper] ]

(请注意:这是功能列表)。然后我要在种子序列的下一个之后应用一个。因此,如果列表最终被

adjustments = [inc1, inc2, inc3]

我们想要得到

inc1 (inc2 (inc3 seq0))

其中seq0是初始传入序列。这种方法有意义吗?这足以让您入门吗?