在Haskell中增长数组

时间:2011-09-08 03:38:17

标签: arrays haskell

我想在Haskell中实现以下(命令性)算法:

给定一系列对[(e0,s0),(e1,s1),(e2,s2),...,(en,sn)],其中“e”和“s”部分都是自然的数字不一定不同,在每个时间步骤中随机选择该序列的一个元素,假设(ei,si),并且基于(ei,si)的值,构建新元素并将其添加到序列中。

如何在Haskell中有效地实现这一点?随机访问的需要会使列表变得不好,而据我所知,一次添加一个元素的需要会使数组变得不好。

提前致谢。

5 个答案:

答案 0 :(得分:12)

我建议使用Data.SetData.Sequence,具体取决于您的需要。后者特别为您提供对数索引查找(与列表的线性相反)和O(1)在任一端附加。

答案 1 :(得分:3)

“虽然需要一次添加一个元素会使数组变得不好”从算法来看,似乎你想要一个动态数组(又名矢量,数组列表等),它已经摊销了O(1)时间附加元素。我不知道它的Haskell实现,并且它不是一个非常“功能”的数据结构,但绝对有可能在Haskell中以某种状态monad实现它。

答案 2 :(得分:1)

如果你知道你将需要多少总元素,那么你可以创建一个这样大小的数组,首先是“稀疏”,然后根据需要你可以在其中放置元素。 下面的内容可用于表示这个新数组:

data MyArray = MyArray (Array Int Int) Int

(最后一个Int表示数组中使用了多少个元素)

答案 3 :(得分:1)

如果您真的需要停止并开始调整大小,您可以考虑使用simple-rope包以及StringLike实例来使用Vector之类的内容。特别是,这可能适用于从大型阵列开始并且对相对较少的添加感兴趣的场景。

也就是说,将单个元素添加到绳索的块中可能仍会导致大量复制。您将需要尝试您的具体案例,但您应该准备使用可变向量,因为您可能不需要纯粹的中间结果。

如果您可以一次性构建数组并且只需要您描述的索引行为,那么类似下面的内容就足够了,

import Data.Array.IArray

test :: Array Int (Int,Int)
test = accumArray (flip const) (0,0) (0,20) [(i, f i) | i <- [0..19]]
  where f 0 = (1,0)
        f i = let (e,s) = test ! (i `div` 2) in (e*2,s+1)

答案 4 :(得分:0)

从伊万姆那里得到一张纸条,我认为套装是这样做的。

import Data.Set as Set
import System.Random (RandomGen, getStdGen)

startSet :: Set (Int, Int)
startSet = Set.fromList [(1,2), (3,4)] -- etc. Whatever the initial set is

-- grow the set by randomly producing "n" elements.
growSet :: (RandomGen g) => g -> Set (Int, Int) -> Int -> (Set (Int, Int), g)
growSet g s n | n <= 0    = (s, g)
              | otherwise = growSet g'' s' (n-1)
    where s' = Set.insert (x,y) s
          ((x,_), g')  = randElem s g
          ((_,y), g'') = randElem s g'

randElem :: (RandomGen g) => Set a -> g -> (a, g)
randElem = undefined

main = do
    g <- getStdGen
    let (grownSet,_) = growSet g startSet 2
    print $ grownSet -- or whatever you want to do with it

这假定randElem是从集合中选择随机元素的有效,可定义的方法。 (我问this SO question关于这种方法的有效实现)。在编写此实现时我意识到的一件事是它可能不适合您的需要,因为Set s不能包含重复元素,并且我的算法无法为列表中多次出现的配对赋予额外的权重。