manatee-core-0.0.1: Manatee/Toolkit/General/List.hs
-- Author: Andy Stewart <lazycat.manatee@gmail.com>
-- Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
--
-- Copyright (C) 2010 Andy Stewart, all rights reserved.
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
{-# LANGUAGE TransformListComp #-}
module Manatee.Toolkit.General.List where
import Control.Arrow
import Control.Monad
import Data.List
import Data.Maybe
import Data.Monoid
import Manatee.Toolkit.General.Basic
-- | Return element of list with given index.
(?!) :: [a] -> Int -> Maybe a
[] ?! _ = Nothing
xs ?! n
| n < 0
= Nothing
| n >= length xs
= Nothing
| otherwise
= listToMaybe . drop n $ xs
-- | Intersect element with list from end, don't including front or middle element.
-- Return null if haven't intersection element.
intersectEnd :: Ord a => [a] -> [a] -> [a]
intersectEnd xs ys = reverse $ intersectFront (reverse xs) (reverse ys)
-- | Intersect element with list from front, don't including end or middle element.
-- Return null if haven't intersection element.
intersectFront :: Ord a => [a] -> [a] -> [a]
intersectFront xs ys = [x | (x,y) <- zip xs ys, then takeWhile by x == y]
-- | Delay list with given start index.
delay :: Int -> [a] -> [Int]
delay n (_:xs) = n : delay (n + 1) xs
delay _ _ = []
-- | Index of list
listIndex :: [a] -> [Int]
listIndex = delay 0
-- | Pair with list index.
pairPred :: [a] -> [(a, Int)]
pairPred xs = zip xs $ listIndex xs
-- | Different two list, and two lists must have same length.
-- otherwise throw a error.
different :: Ord a => [a] -> [a] -> [a]
different (x:xs) (y:ys)
| x == y = different xs ys
| otherwise = x : different xs ys
different [] [] = []
different _ _ = error "different: lists not the same length!"
-- | Do action when list not empty.
unlessNull :: [a] -> IO () -> IO ()
unlessNull = unless . null
-- | not . null
has :: [a] -> Bool
has = not . null
-- | Head monad list.
headM :: Monad m => m [a] -> m a
headM = liftM head
-- | Last monad list.
lastM :: Monad m => m [a] -> m a
lastM = liftM last
-- | ConcatM.
concatM :: Monad m => m [a] -> m [a] -> m [a]
concatM = liftM2 (++)
-- | Replace n'th element (count from 0) in `xs` to `x`
replaceAt :: Int -> [a] -> a -> [a]
replaceAt n xs x = take n xs ++ x : drop (n + 1) xs
-- | Split list with given condition.
splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith _ [] = []
splitWith f xs = fst tuple : splitWith f (dropWhile f $ snd tuple)
where tuple = break f xs
-- | concatMapM.
concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
concatMapM f xs = liftM concat $ forM xs f
-- | Like find, but works with monadic computation instead of pure function.
-- In expression `find FUNCTION list`, if FUNCTION is "IO Bool", you can use
-- `findM FUNCTION list` to instead.
findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a)
findM _ [] = return Nothing
findM f (x:xs) = ifM (f x) (return $ Just x) (findM f xs)
-- | Apply two monad function with list.
-- And return new monad tuples list.
apply2M :: Monad m => [a] -> (a -> m b) -> (a -> m c) -> m [(b, c)]
apply2M xs f g = forM xs (\x -> zipM' (f x) (g x))
-- | Apply two function with list.
-- And return new tuples list.
apply2 :: [a] -> (a -> b) -> (a -> c) -> [(b, c)]
apply2 xs f g = map (f &&& g) xs
-- | Partition list.
partitionM :: Monad m => (a -> m Bool) -> [a] -> m ([a], [a])
partitionM _ [] = return ([], [])
partitionM f (x:xs) =
liftM2 mappend (ifM (f x) (return ([x], [])) (return ([], [x]))) (partitionM f xs)
-- | Like `init`, but accept empty list.
init_ [] = []
init_ xs = init xs
-- | Like `foldl1`, but accept empty list.
foldl1_ _ [] = []
foldl1_ f xs = foldl1 f xs
-- | Find next element.
findNext :: (a -> Bool) -> [a] -> Maybe a
findNext x = listToMaybe . drop 1 . dropWhile (not . x)
-- | Find next cycle.
findNextCycle :: (a -> Bool) -> [a] -> Maybe a
findNextCycle _ [] = Nothing
findNextCycle f list =
case dropWhile (not . f) list of
[] -> Nothing -- `Nothing` when element not found in list
[_] -> Just $ head list -- Get head when element at last of list
ls -> listToMaybe $ drop 1 ls -- Otherwise get next element in list
-- | Find previous cycle.
findPrevCycle :: (a -> Bool) -> [a] -> Maybe a
findPrevCycle _ [] = Nothing
findPrevCycle f list
| length dropList == length list
= Nothing -- `Nothing` when element not found in list
| null dropList
= Just $ last list -- Get last when element at first position of list
| otherwise
= Just $ last dropList -- Otherwise get previous element in list
where dropList = takeWhile (not . f) list
-- | Delete at.
deleteAt :: Int -> [a] -> [a]
deleteAt _ [] = []
deleteAt i xs
| i < 0
= []
| i >= length xs
= []
| otherwise
= (\(a, b) -> a ++ tail b) (splitAt i xs)
-- | Get last one.
getLast :: [a] -> Maybe a
getLast [] = Nothing
getLast xs = Just $ last xs
-- | Get first one.
getFirst :: [a] -> Maybe a
getFirst [] = Nothing
getFirst xs = Just $ head xs
-- | Zip with list index.
zipWithIndex :: [a] -> (a -> Int -> c) -> [c]
zipWithIndex xs f = zipWith f xs [0..]
-- | Zip with list index.
zipWithIndexM :: Monad m => [a] -> (a -> Int -> m c) -> m [c]
zipWithIndexM xs f = zipWithM f xs [0..]
-- | Zip with list index.
zipWithIndexM_ :: Monad m => [a] -> (a -> Int -> m c) -> m ()
zipWithIndexM_ xs f = zipWithM_ f xs [0..]
-- | Like 'concatMap', but don't concat last one.
addMap :: ([a] -> [a]) -> [[a]] -> [a]
addMap _ [] = []
addMap _ [x] = x
addMap f xs = concatMap f (init xs) ++ last xs
-- | Like 'insert' but just insert unique element.
insertUnique :: Ord a => a -> [a] -> [a]
insertUnique x xs
| x `elem` xs = xs
| otherwise = insert x xs