第23章:列表、陣列和雜湊表

夢飛發表於2017-03-13

很多程式語言中,陣列和雜湊表常常當作標準的資料結構被廣泛使用。而在Haskell中,由於資料是很多抽象的核心,所以很多函式庫提供了大量不同特性的資料結構(immutable and mutable)。

1.列表 immutable

  • Haskell中常當作迴圈控制結構,底層表示是一叉樹或單連結串列
  • 列表是盒子堆砌的
  • 列表所具有的其他資料結構沒有的特性

    +O(1)的 : 操作

    +O(1)的tail操作和O(m)的splitAt操作

    +惰性列表使得共享變得容易

    +O(n)的length操作 +O(m)的++、!!與更新操作,m是左側列表的長度

  • 不要在需要隨機訪問和大量拼接的場合使用列表,以免遍歷列表所帶來的消耗

  • Data.List
  • Data.List.Split

2.陣列

一般來說,陣列只是被用作存放資料的一個連續記憶體容器;陣列的一個最重要的功能是根據下標實現O(1)時間的索引;array 函式庫中不可變的IArray和可變的MArray型別類,用來實現不同的下標型別的Ix型別類;它是Haskell中標準實現的一部分,很多其他的基礎函式庫都依賴它,如containers, stm。

  • Ix型別類

    class (Ord a) => Ix a where
        {-# MINIMAL range, (index | unsafeIndex), inRange #-}
    
    
    
    -- | The list of values in the subrange defined by a bounding pair.
    range               :: (a,a) -> [a]
    -- | The position of a subscript in the subrange.
    index               :: (a,a) -> a -> Int
    -- | Like 'index', but without checking that the value is in range.
    unsafeIndex         :: (a,a) -> a -> Int
    -- | Returns 'True' the given subscript lies in the range defined
    -- the bounding pair.
    inRange             :: (a,a) -> a -> Bool
    -- | The size of the subrange defined by a bounding pair.
    rangeSize           :: (a,a) -> Int
    -- | like 'rangeSize', but without checking that the upper bound is
    -- in range.
    unsafeRangeSize     :: (a,a) -> Int
    

    +如果元祖的元素都是Ix的例項型別的話,那麼元組本身也是Ix的例項

    +array中表示多維陣列的方式:使用元組作為陣列下標

    range ((1,2),(3,6))
    range ((1,'a'),(3,'d'))
    
  • 建立陣列的方法

    array :: Ix i     
    => (i, i)    --These bounds are the lowest and highest 
    -> [(i, e)]    --a list of associations of the form (index, value). 
    -> Array i e
    
    
    listArray :: Ix i => (i, i) -> [e] -> Array i e
    -- Construct an array from a pair of bounds and a list of values in index order.
    
    
    accumArray :: Ix i
    => (e -> a -> e)    --accumulating function
    -> e                 --initial value
    -> (i, i)            --bounds of the array
    -> [(i, a)]            --association list
    -> Array i e
    
    
    --例如
    a = array (1,100) ((1,1) : [(i, i * a!(i-1)) | i <- [2..100]])
    
  • Data.Array模組中提供的陣列是盒裝的

  • Data.Array.Unboxed中的UArray型別
  • Data.Array.IArray模組:操作不可變陣列的函式,IArray為盒裝陣列Array和非盒裝陣列UArray提供了共同的介面
  • Data.Array.MArray: MArray為ST單子和IO單子中的可變陣列提供了共同的介面

    class (Monad m) => MArray a e m where
        getBounds      :: Ix i => a i e -> m (i,i)
        newArray       :: Ix i => (i,i) -> e -> m (a i e)
        newArray_      :: Ix i => (i,i) -> m (a i e)
    
  • Haskell中,所有的可變操作都包裹在IO/ST單子中

  • 轉換不可變陣列和可變陣列

    freeze :: (Ix i, MArray a e m, IArray b e) => a i e -> m (b i e)
    thaw :: (Ix i, IArray a e, MArray b e m) => a i e -> m (b i e)
    
  • 盒裝不可變陣列型別Array,它是Functor Foldable 和 Traversable 的例項型別

  • Hackage上其他提供連續記憶體儲存的資料結構 vector repa accelerate

3.雜湊表 (和python中的詞典類似)

  • 雜湊表同意有不可變和可變兩類
  • 不可變雜湊表主要由unordered-containers函式庫提供,底層實現是hash-trie,包括盒裝的惰性求值版本和非盒裝的嚴格求值版本(較常用)
  • 雜湊表每次插入操作需要提供一個鍵 key 和 一個值 value
  • Data.HashMap.Strict
  • Hashable型別類
  • hashtables函式庫

相關文章