module HopList (HopList, toList, fromList, hopTail, index, hopLookup, hopListTests) where import Data.List as List import Tests -- | -- A data structure that is almost, but not exactly, completely unlike a skip list. -- Strictly speaking, skip lists are probabilistic data structures over sorted elements. -- This HopList implementation just allows O( log n ) access to elements of a haskell list. -- Like a skip list, it uses a stack of parallel arrays to quickly traverse a list. -- It supports infinite lists. -- -- The HopList looks something like this: -- -- 00 -> 16 -- 00 -> 04 -> 08 -> 12 -> 16 -> 20 -- 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 -- data HopList a = HopStack { hop_up :: HopList a, hop_right :: HopList a } | HopNode { hop_down :: !(HopList a), hop_right :: HopList a } | HopElem [a] hopListFactor :: Integer hopListFactor = 16 toList :: HopList a -> [a] toList (HopElem xs) = xs toList (HopStack _ right) = toList right toList (HopNode down _) = toList down fromList :: [a] -> HopList a fromList xs = HopStack { hop_up=fromList_up (HopElem xs), hop_right=HopElem xs } fromList_up :: HopList a -> HopList a fromList_up param@(HopElem xs) = seq param $ HopNode { hop_down=param, hop_right=fromList_up (HopElem (genericDrop hopListFactor xs)) } fromList_up param@(HopNode _ _) = seq param $ HopNode { hop_down=param, hop_right=fromList_up ((hop_rights param) `genericIndex` hopListFactor) } fromList_up param@(HopStack _ _) = hop_up param hop_rights :: HopList a -> [HopList a] hop_rights param = iterate hop_right param -- | -- Answers the rest of a HopList starting from the specified index. -- ((fromList xs) `hopTail` 5) is equivalent to (drop 5 xs). -- hopTail :: HopList a -> Integer -> [a] hopTail hl i = hopTail_ hl 1 i hopTail_ :: HopList a -> Integer -> Integer -> [a] hopTail_ (HopElem xs) 1 i = genericDrop i xs hopTail_ (HopElem _) _ _ = error "Depth of a HopElem is always 1" hopTail_ param@(HopNode _ _) depth i = hopTail_ (hop_down $ head $ genericDrop (i `div` depth) $ hop_rights param) (depth `div` hopListFactor) (i `mod` depth) hopTail_ param@(HopStack _ _) depth i = let next_depth = depth * hopListFactor in if next_depth < i then hopTail_ (hop_up param) next_depth i else hopTail_ (hop_right param) depth i -- | -- Answers the element at the specified index. ((fromList xs) `index` 5) -- is equivalent to (xs !! 5). -- index :: HopList a -> Integer -> a index hl i = head $ hopTail hl i -- | -- As index, but returns in a monad if the element is available -- or fails if it is beyond the end of the list. -- hopLookup :: Monad m => HopList a -> Integer -> m a hopLookup hl i = case (hopTail hl i) of [] -> fail ("no element at index " ++ (show i)) xs -> return $ head xs exampleHopListInfinite :: HopList Int exampleHopListInfinite = fromList [0,2..] exampleHopListFinite :: HopList Int exampleHopListFinite = fromList [0,2..2000] hopListTests :: [TestCase] hopListTests = [hopListTestZeroIndex,hopListTestSmallIndex,hopListTestLargeIndex,hopListTestOutOfBoundsIndex] hopListTestZeroIndex :: TestCase hopListTestZeroIndex = test "hopListTestZeroIndex" ((exampleHopListInfinite `hopLookup` 0) == Just 0) hopListTestSmallIndex :: TestCase hopListTestSmallIndex = test "hopListTestSmallIndex" ((exampleHopListInfinite `hopLookup` 5) == Just 10) hopListTestLargeIndex :: TestCase hopListTestLargeIndex = test "hopListTestLargeIndex" ((exampleHopListInfinite `hopLookup` 500000) == Just 1000000) hopListTestOutOfBoundsIndex :: TestCase hopListTestOutOfBoundsIndex = test "hopListTestOutOfBoundsIndex" ((exampleHopListFinite `hopLookup` 500000) == Nothing)