除非性能很關鍵,否則我建議使用位矢量表示法來表示這樣的項目。正如你發現的那樣,隨機訪問單個位在打包形式時是一件很痛苦的事情,但Data.Vector
爲這類任務提供了豐富的功能。
import Data.Bits
import qualified Data.Vector as V
type BitVector = V.Vector Bool
unpack :: (Bits a) => a -> BitVector
unpack w = V.generate (bitSize w) (testBit w)
pack :: (Bits a) => BitVector -> a
pack v = V.ifoldl' set 0 v
where
set w i True = w `setBit` i
set w _ _ = w
mkPermutationVector :: Int -> V.Vector Int
mkPermutationVector d = V.generate (2^d) b
where
b i | i < 2^(d-1) = 2*i
| otherwise = let i' = i-2^(d-1)
in 2*i'+1
permute :: Int -> BitVector -> BitVector
permute d v = V.backpermute v (mkPermutationVector d)
請注意,這是如何讓你通過密切轉錄數學描述來指定排列。這大大減少了錯誤發生的可能性,並且比編寫代碼的代碼更令人愉快。
當您例如向量(以10爲基數)測試:現在
*Main> import Data.Word
*Main Data.Word> let permute16 = pack . permute 4 . unpack :: Word16 -> Word16
*Main Data.Word> permute16 43690
65280
,通過移動到位向量作爲你的表現,你使用的Haskell類型,這樣會失去很多,你得到了什麼免費作爲Num
實例。但是,您始終可以爲您的表示實施Num
操作;這裏是一個開始:
plus :: BitVector -> BitVector -> BitVector
plus as bs = V.tail sums
where
(sums, carries) = V.unzip sumsAndCarries
sumsAndCarries = V.scanl' fullAdd (False, False) (V.zip as bs)
fullAdd (_, cin) (a, b) = ((a /= b) /= cin
, (a && b) || (cin && (a /= b)))
您也可以找到勒Erkok的sbv
包是有用的,但我不知道它暴露一樣便利backpermute
功能爲您的特定問題。
更新:我認爲這是一個有趣的問題來回答,所以我繼續充實碼出位的庫:bit-vector。
http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Bits.html#t:Bits – 2011-09-04 10:33:39