PLEAC-Haskell | ||
---|---|---|
Prev |
module Pleac where import Prelude hiding (($),(^),(.),(!!),map,take,lookup,drop,splitAt,reverse,filter,takeWhile,dropWhile,null,foldl) import Common -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- module Common ( module Common , module Regexp , module System , module Complex , module Char , module Numeric -- Monad , foldM -- List , elemIndex, elemIndices, find, findIndex, findIndices, nub, nubBy, delete, deleteBy, (\\), deleteFirstsBy, union, unionBy, intersect, intersectBy, intersperse, transpose, partition, group, groupBy, inits, tails, isPrefixOf, isSuffixOf, mapAccumL, mapAccumR, sort, sortBy, insert, insertBy, maximumBy, minimumBy, genericLength, genericTake, genericDrop, genericSplitAt, genericIndex, genericReplicate, zip4, zip5, zip6, zip7, zipWith4, zipWith5, zipWith6, zipWith7, unzip4, unzip5, unzip6, unzip7, unfoldr -- ,module Prelude ) where ---------------------------------------------------------------------------------------------------- import Prelude hiding (($),(^),(.),(!!),map,take,lookup,drop,splitAt,reverse,filter,takeWhile,dropWhile,null,foldl) import List hiding ((!!),map,take,lookup,drop,splitAt,reverse,filter,takeWhile,dropWhile,null,insert,union,intersect,delete,partition,foldl,length) import Random hiding (split) import Monad hiding (join,when,unless) import Regexp import System import Complex -- seems ghc need it for chr,ord, readHex, readOct import Char import Numeric --ghc import Time --ghc import Locale --import Trace ---------------------------------------------------------------------------------------------------- substr start len insert s = if start >= 0 then s!![0..start-1] ++ insert ++ s!![start+len..] -- [0.. -1] -> [] else s!![0..(length s + start -1)] ++ insert ++ s!![start+len.. -1] --substr s start len insert = take s start ++ insert ++ s!![start+len..] substr2 start insert s = substr start (if start >= 0 then (length s - start) else -start) insert s data Unpack_option = Grab Int | Forward Int | Backward Int | GrabAll deriving Show unpack :: [Unpack_option] -> [a] -> [[a]] unpack opt s = opt.foldl aux (0,[]).snd.reverse --(snd $ reverse) (foldl aux (0,[]) opt) where aux (n,l) (Grab i) = (n+i, s!![n..n+i-1] : l) aux (n,l) (Forward i) = (n+i, l) aux (n,l) (Backward i) = (n-i, l) aux (n,l) GrabAll = (length s + 1, s!![n..] : l) -- could do a foldr so that no need reverse but need reverse opt -- could to a str2Unpack_option so that str2Unpack_option "A5 A*" => [Grab 5,GrabAll] ---------------------------------------------------------------------------------------------------- -- use http://www.dcs.gla.ac.uk/~meurig/regexp/ for substS, ... -- TODO, a subst that take group subst re f s = let matchresult = searchS re [] s in if matchedAny matchresult then beforeMatch matchresult ++ f (wholeMatch matchresult) ++ afterMatch matchresult else s gsubst re f s = let matchresult = searchS re [] s in if matchedAny matchresult then beforeMatch matchresult ++ f (wholeMatch matchresult) ++ (gsubst re f (afterMatch matchresult)) else s -- in perl, =~ do 2 things, first it returns a bool and bind $i -- in haskell we are forced to do it in 2 steps (=~) s re = let matchresult = searchS re [] s in matchedAny matchresult (==~) s re = s =~ ("^"++s++"$") -- the Regexp lib require regexp of the form " var@(...) var2@(...)" i dont -- like put name on this, prefer anonymous as in perl, that is "(...) (...)" -- TODO, handle |, for example when match .(..)|.(..) i would like to return a list of one element -- TODO, handle ?: of perl match re s = let (max,re2) = re.foldl (\(i,acc) c -> if c == '(' then (i+1,acc++" "++show i++"@(") else (i,acc++[c]) ) (1,[]) matchresult = s.searchS re2 [] -- there is a pb cos in match "(\\w+)=(.*)" "MAIL=", we dont get ["MAIL",""] -- but only ["MAIL"], the lib does not handle well empty match subs = allSubexps matchresult subs2 = if (subs.length > 0 && subs.last.fst /= show (max-1)) ||(subs.length == 0 && max > 1) then subs++[(show (max-1),"")] else subs subs3 = subs2.foldl (\(i,acc) (j,str)-> if show i == j then (i+1, acc++[str]) else ((read j)+1, acc++replicate ((read j)-i) ""++[str]) ) (1,[]) in subs3.snd -- cant make match and gmatch the same type, cos one give [] and gmatch give [[]] gmatch re s = let (max,re2) = re.foldl (\(i,acc) c -> if c == '(' then (i+1,acc++" "++show i++"@(") else (i,acc++[c]) ) (1,[]) matchresult = s.searchS re2 [] in if matchedAny matchresult then (match re s):(gmatch re (afterMatch matchresult)) else [] --myl = "cava et toi".match "(a..) et (t)oi" --myl = match "(\\w+)" "pad tty etrre" --myl = match "(\\w+)=" "PAD=/home/pad" --myl = match "(\\w+)=(.*)" "PAD_MAIL=" pattern_matches ((re, v):xs) s = if s =~ re then v else pattern_matches xs s ---------------------------------------------------------------------------------------------------- -- an imitation of the '?' C operator, could even define (:-) = (:) and then have bool ? v1 :- v2 (?) True (a,b) = a (?) False (a,b) = b (==>) a b = (a,b) -- TODO, would like to overload . to behave as >>> when monad, but hard to do (a -> (a->b) -> b) does -- not unify with m a -> (a -> b) -> m b, and want that ? -- it is cool that record get autogenerated func, => we could do object.record_selector -- consistency (.) o f = f o -- object notation for object fan, use it if function have more than one parameter or -- if you want make an "oo-line" (ex o.words.reverse.unwords) (.>) m f = m >>= (\o -> return (f o)) -- <=> >>> (°) f g x = f (g x) (f $ g) x = (g ° f) x next_char c = chr (ord c + 1) show_float i f = showFFloat (Just i) f "" fixpoint f d | f d == d = d | otherwise = fixpoint f (f d) times = replicate join e [] = [] join e xs = foldr1 (\a x -> a ++ e ++ x) xs -- join e [] = [] -- join e [x] = x -- join e (x:xs) = x ++ e ++ join e xs -- ex: join " " ["bi","te","di","ck"] == "bi te di ck" -- string only split --split e xs = splitaux e [] xs --splitaux e a xs | xs.length < e.length = [a ++ xs] -- | take (e.length) xs == e = a:(splitaux e [] (drop (e.length) xs)) -- | otherwise = splitaux e (a++[head xs]) (tail xs) -- better split, handle regexp split re xs = let matchresult = searchS re [] xs in if matchedAny matchresult then (beforeMatch matchresult):(split re (afterMatch matchresult)) else [xs] --test=split "\\s+" "toto titi tia to" break_char c f s = let (a,b) = break (== c) s in if b == [] then a else f a (tail b) upcase xs = map toUpper xs downcase xs = map toLower xs capitalize (x:xs) = toUpper x : xs tonum x = fromInteger x hex ('0':('x':s)) = fst (head (readHex s)) -- TODO do some check oct ('0':('o':s)) = fst (head (readOct s)) -- TODO do some check tohex i = "0x"++aux i where aux i = if i == 0 then "" else aux (i `div` 16)++((['0'..'9']++['A'..'F'])!![i `mod` 16]) tooct i = "0o"++aux i where aux i = if i == 0 then "" else aux (i `div` 8)++(['0'..'7']!![i `mod` 8]) unique :: (Eq a) => [a] -> [a] -- dont know why but forced to put the type unique = nub insert_at :: Int -> [a] -> [a] -> [a] insert_at i toadd xs = let (a,b) = splitAt i xs in a++toadd++b replace :: [(Int, a)] -> [a] -> [a] replace assoc xs = assoc.foldl (\a (i,e) -> let (x,y) = splitAt i a in x++[e]++y ) xs shift (x:xs) = (x,xs) pop xs = (init xs, last xs) -- more efficient let (a,[b]) = splitAt (xs.length - 1) xs in (a,b) foldl_index f acc xs = foldl f acc (zip [1..] xs) complex_i = 0 :+ 1 (<=>) a b = compare a b --debug expr = trace ("Debug:"++show expr++"\n") expr ---------------------------------------------------------------------------------------------------- infixl 1 >>> (>>>) monad f = monad >>= (return ° f) eval x = x >>= (putStrLn ° show) action `when` bool = if bool then action else return () action `unless` bool = action `when` (not bool) cat file = readFile file >>> lines contents = getContents >>> lines (<>) = do l <- argv if (l == []) then contents else foldM (\a e -> cat e >>> (a++)) [] l -- i know it is just poor code, but haskell force me -- TODO, dont work, seems monad are lazy too, dont understand io in haskell anymore :( -- if do {l <- exec "ps ux"; l2 <- exec "who"; l.mapM putStrLn} print the contents of l2 !! exec s = do _ <- system (s++" > /tmp/file") cat "/tmp/file" rand :: (Int, Int) -> IO Int -- force to do that to resolve overloading :( rand = getStdRandom ° randomR stdrand :: IO Double stdrand = getStdRandom (randomR (0,1)) srand val = setStdGen (mkStdGen val) -- in haskell we have getEnv::String -> String, but not getEnvs :( env :: IO (Assoc String String) env = exec "env" >>> (map (\s -> let [a,b] = s.match "(\\w+)=(.*)" in (a,b)) $ fromList) -- env = getEnvironment -- for ghc argv = getArgs just (Just x) = x just _ = error "Common:just" exit i = primExitWith i usage s = putStrLn ("usage:"++s) >> exit 1 -- printf a la danvy -- type fmt a = ---------------------------------------------------------------------------------------------------- --ghc now = do cl <- getClockTime --ghc return (toUTCTime cl) --ghc localtime = toCalendarTime . toClockTime --ghc strftime fmt time = formatCalendarTime defaultTimeLocale fmt time --ghc nulltime = TimeDiff {tdYear=0, tdMonth=0, tdDay=0, tdHour=0, tdMin=0, tdSec=0, tdPicosec = 0} ---------------------------------------------------------------------------------------------------- -- Collects e [e] where ... -- Collects e (e -> Bool) where ... -- Collects Char BitSet where ... -- (Hashable e, Collects a ce) Collects e (Array Int ce) -- type Assoc a b = [(a,b)] -- -- -- this class support only collection of kind/1 => :( cos assoc dont match this criteria -- -- note that some new type variable are introduced (something impossible with ocaml oo, can not -- -- define a list object and then a fold, cos the type of the accumulator is new -- -- can do it in caml/C++ by making fold an external function of the class -- class Collection1 c where -- member1 :: Eq e => e -> c e -> Bool -- need Eq e, otherwise at instance declaration, when member = elem, pb (WHY?) -- cmap1 :: (a -> b) -> c a -> c b -- insert1 :: e -> c e -> c e -- instance Collection1 [] where -- member1 = elem -- cmap1 = map -- insert1 = (:) -- -- instance Collection1 Assoc ..., is not possible -- -- -- class Collection2 e c where, useless constructor class do the same job -- edison is cool, but they dont use depedencies => cant make a superclass Collection of Assoc and Sequence -- morover, there are too many classes, i prefer one class Collection, and then instance OrdSet,Bag,OrdBag,... -- edison use constructor class => if a type is not of the form c a, then it does not work -- as for the Functor class, this is a pb cos you cant make an instance Functor Assoc, cos Assoc is of kind/2 -- TODO, to allow default null, need Eq ce =>, mais apres foldl need des Eq [b] => -- must cut Collection in section CollectionEq, Collection class Collection e ce | ce -> e where -- constructors empty :: ce single :: e -> ce insert :: e -> ce -> ce fromList :: [e] -> ce fromList xs = xs.foldl (flip insert) empty copy :: Int -> e -> ce -- union, intersect, difference :: ce -> ce -> ce -- tabulate ? -- destructors -- simple delete :: e -> ce -> ce delete_all:: e -> ce -> ce -- not relevant for Set -- predicate based filter :: (e -> Bool) -> ce -> ce partition:: (e -> Bool) -> ce -> (ce,ce) -- observers toList :: ce -> [e] size :: ce -> Int -- member :: e -> ce -> Bool -- null :: ce -> Bool -- null ce = ce == empty -- iterators -- concatMap, cfoldr + 1, reducer* ? foldl:: (a -> e -> a) -> a -> ce -> a each :: (e -> IO ()) -> ce -> IO () -- find each -- zip unzip zippWith -- map:: (e -> b) -> ce -> cb -- cant define as is, would like a depedency constraint -- such as ce+b->cb -- can be put outside and use insert/observers -- TODO make AssocOrd class, OrdColl, ... -- goal: Set,Bag -- implementation: list,ordlist, (if odererd => can use bintree), okasaki optimised null es = es == empty member e es = e `elem` (es.toList) -- TODO, could make union more cool, to allow each time different collection -- could allow to do in place of a.insert e, [e]+a union :: (Collection a c) => c -> c -> c -- to force the same collection type union a b = b.foldl (flip insert) a intersect :: (Collection a c, Eq a) => c -> c -> c -- to force the same collection type intersect a b = a.foldl (\a e -> if e `member` b then a.insert e else a) empty difference :: (Collection a c, Eq a) => c -> c -> c -- to force the same collection type difference a b = a.foldl (\a e -> if e `member` b then a else a.insert e) empty class Collection e ce => Sequence e ce where -- constructors cons,snoc :: e -> ce -> ce append :: ce -> ce -> ce append = union -- destructors --simple lview,rview :: ce -> (e,ce) lhead,rhead :: ce -> e ltail,rtail :: ce -> ce -- int based take,drop :: Int -> ce -> ce splitAt :: Int -> ce -> (ce,ce) subseq :: Int -> Int -> ce -> ce takeWhile,dropWhile:: (e -> Bool) -> ce -> ce splitWhile :: (e -> Bool) -> ce -> (ce,ce) -- misc reverse :: ce -> ce -- concat :: [ce] -> ce; not [ce] but ce ce => how ? -- update, inbounds, adjust, [map|foldl|foldr]WithIndex -- instance [a], Queue [a], okasaki optimised -- TODO, make too strong condition, Eq b for example, could make keyss outside the class class (Eq a, Eq b, Collection (a,b) cab) => Association a b cab | cab -> a, cab -> b where keys :: cab -> [a] values:: cab -> [b] has_key:: a -> cab -> Bool has_key k xs = k `elem` (xs.keys) delete_key :: a -> cab -> cab each_key :: (a -> IO ()) -> cab -> IO () keyss :: b -> cab -> [a] keyss v cab = cab.toList.filter (snd $ (==v)).map fst has_value:: b -> cab -> Bool has_value v xs = v `elem` (xs.values) -- instance Assoc, okasaki optimised (bintree, ...) -- AssocMulti, key -> list map f [] = [] map f (x:xs) = f x: map f xs cmap f = toList ° map f ° fromList -- cant make in the type that this must be the same collection class Indexable i ca a | ca -> a where (!~) :: ca -> i -> Maybe a -- lookupM (!) :: ca -> i -> a -- lookup, to be more esthetic do s!(0) rather than s!0 (!) xs i = (xs!~i).just -- lookupWithDefaul (!!) :: ca -> [i] -> [a] (!!) xs is = map (\i -> xs ! i) is -- seems you can not define with infix notation default class value ------------------------------- instance (Num b,Ord b) => Indexable b [a] a where -- to allow s![1..] -- note: a 'try (...) with' would be more efficient that constantly check for array bound -- note: we stop as soon as we have a big index => xs ! [1000, 2,3] if length xs < 1000 will give [] -- (to handle infinite list) -- TODO allow s!![-4..] (!!) xs [] = [] (!!) xs (i:is) | i >= (fromInt (length xs)) = [] -- handle infinite list | i < 0 = xs!((fromInt (length xs)) + i) : xs!!is | otherwise = xs!(i) : xs!!is -- (x:_) !~ 0 = Just x (x:_) !~ n | n < 1 && n >= 0 = Just x -- allow s!3.2 (_:xs) !~ n | n>0 = xs !~ (n-1) _ !~ _ = Nothing instance Collection a [a] where insert = (:) foldl f z [] = z foldl f z (x:xs) = foldl f (f z x) xs filter p xs = [ x | x <- xs, p x ] each = mapM_ toList = id fromList = id -- default fromList dont respect the order empty = [] instance Sequence a [a] where reverse = foldl (flip (:)) [] -- this can be dangerous, cos insert for an Association is replace/add (key,val) whereas -- for a list with simply add, and so use Assoc if you want to choose -- same for each, each will print all the pair, even the obsolete instance (Eq a,Eq b) => Association a b [(a,b)] where keys xs = xs.map fst delete_key k xs = xs.filter (\(a,b) -> a /= k) each_key f = keys $ each f ------------------------------- data SetList a = SetList [a] deriving Show instance Eq a => Collection a (SetList a) where insert e (SetList xs) = SetList (e:(xs.filter (/= e))) toList (SetList xs) = xs instance Eq (SetList a) => Default_val (SetList a) where null_val = SetList [] instance Eq a => Eq (SetList a) where (SetList xs) == (SetList ys) = (xs.all (`elem` ys)) && (xs.length == ys.length) ------------------------------- data Assoc a b = Assoc [(a,b)] deriving Eq instance (Show a, Show b) => Show (Assoc a b) where show (Assoc xs) = xs.map (\(a,b) -> a^" => "^b).unlines instance Eq a => Collection (a,b) (Assoc a b) where empty = Assoc [] insert (k,v) (Assoc xs) = Assoc ((k,v):xs.filter ((/= k) ° fst)) -- [ (k',v') | (k',v') <- xs, k /= k']) toList (Assoc xs) = xs each f (Assoc xs) = xs.each f foldl f z (Assoc xs) = xs.foldl f z instance (Eq a,Eq b) => Association a b (Assoc a b) where keys (Assoc xs) = xs.map fst values (Assoc xs) = xs.map snd instance Eq a => Indexable a (Assoc a b) b where (!~) (Assoc []) k = Nothing (!~) (Assoc ((x,y):xys)) k | k==x = Just y | otherwise = (Assoc xys)!~k -- to do assoc with default value to allow -- from.update (name,+1), better than -- from.insert(name,1+((from!~name ||| (Just 0)).just)) data AssocDefault a b = AssocDefault (b,[(a,b)]) deriving Eq instance (Show a, Show b) => Show (AssocDefault a b) where show (AssocDefault (b,xs)) = xs.map (\(a,b) -> a^" => "^b).unlines instance (Eq a,Default_val b) => Collection (a,b) (AssocDefault a b) where empty = AssocDefault (null_val,[]) insert (k,v) (AssocDefault (b,xs)) = AssocDefault (b,((k,v):xs.filter ((/= k) ° fst))) -- [ (k',v') | (k',v') <- xs, k /= k']) toList (AssocDefault (b,xs)) = xs each f (AssocDefault (b,xs)) = xs.each f foldl f z (AssocDefault (b,xs)) = xs.foldl f z instance (Eq a,Default_val b) => Association a b (AssocDefault a b) where keys (AssocDefault (b,xs)) = xs.map fst instance Eq a => Indexable a (AssocDefault a b) b where (!~) (AssocDefault (b,xs)) k = (Assoc xs)!~ k -- TODO, could put update in classe Association ? update k f h@(AssocDefault (b,xs)) = h.insert(k, (h!~k ||| Just b).just.f) ---------------------------------------------------------------------------------------------------- -- make tuple indexable, oveload ! to work with all, but does not work -- instance Indexable Int (a,b) a where -- (!~) (a,b) 0 = Just a -- instance Indexable Int (a,b) b where -- (!~) (a,b) 1 = Just b ---------------------------------------------------------------------------------------------------- -- matrix, there exist some implementation on the web, TODO dont do list but fixed size vector => -- could make compile-time check, ex *, we must have row_size a = column_size b -- TODO, could be made a class of FinitMap, and that we could have a generic accessor newtype Matrix a = Matrix [[a]] -- prefer a newtype, cos dont want to allow + on every list of list deriving (Eq,Show) -- newtype force you to redefine the deriving stuff row_size (Matrix m) = m.length column_size (Matrix m) = m.head.length (%%) (Matrix m) (i,j) = m!i!j -- could so straessen, this is very inefficient cos we dont have 0(1) access to element -- TODO, use list comprehension, cf hugs demo instance Num a => Num (Matrix a) where ma * mb | ma.column_size == mb.row_size = Matrix aux where aux = [0..ma.row_size -1] .foldl (\acc i -> acc++[ [0..mb.column_size -1] .foldl (\acc j -> acc++[ [0..ma.column_size-1] .foldl (\acc k -> acc+ (ma%%(i,k) * (mb %%(k,j)))) 0] ) [] ] ) [] ---------------------------------------------------------------------------------------------------- class Eq a => Default_val a where null_val :: a is_null :: a -> Bool is_null = (== null_val) -- not really logical, cos we dont really need to force Default_val a, we dont care about a --instance Default_val a => Default_val [a] where null_val = [] -- and need provide extra instance --instance Default_val Char where null_val = 'a' -- need -98 option, still dont know why we need to put the constraint Eq [a] for working instance Eq [a] => Default_val [a] where null_val = [] instance Eq (Maybe a) => Default_val (Maybe a) where null_val = Nothing -- instance Num b => Default_val b where null_val = 0; cant do that :( hugs say it overlaps -- but it is not cos [a] is not member of the Num class instance Default_val Int where null_val = 0 instance Default_val Float where null_val = 0.0 -- would like to do that, but not possible cos Num is a class, not a type --instance Default_val (Num a) where null_val = 0 -- v1 = (2::Int) &&& "ee" -- "ee" -- need the type annotation :( -- v2 = 0.0 &&& "ee" -- "" -- overloading on return value (&&&):: (Default_val a, Default_val b) => b -> a -> a a &&& b = if a.is_null then null_val else b (|||):: Default_val a => a -> a -> a a ||| b = if a.is_null then b else a ---------------------------------------------------------------------------------------------------- -- class Show a => Showbis a where -- showbis :: a -> String -- showbis x = show x -- cant do instance Showbis [Char] where showbis x = x -- cos we cant specialise so far in haskell, we specialise "chacun son tour" => -- first [] then Char => to handle that, do as in the class Show, make 2 func -- (as you may have note, show "123" => "123" and show '1' => '1', strange, might expect -- ['1','2','3'] cos String = [Char] -- in fact, show [a] is not a map show !! cf Prelude to understand the trick -- they introduce another func in the class definition to allow specialisation for [a] -- showbisList :: [a] -> String -- showbisList [] = "[]" -- showbisList (x:xs) = "[" ++ join "," (map show (x:xs)) ++ "]" --instance Showbis a => Showbis [a] where showbis = showbisList --instance Num a => Showbis a where showbis = show -- the beautfile one, but hugs/ghc do special case with show; when -- do 1 ~~ 2 then he wants (1::Int) ~~ (2::Int) :(( -- (~~) a b = showbis a ++ showbis b -- the ugly one, if you have a type that when show put a " at begin and end, then pb !! -- but as it is rarely the case, no matter, same for \ update_anti_slash [] = [] update_anti_slash [x] = [x] update_anti_slash ('\\':x:xs) = (case x of 'n' -> '\n' 't' -> '\t' x -> x ) : update_anti_slash xs update_anti_slash (x:xs) = x : update_anti_slash xs del_enclose :: String -> String del_enclose xs | length xs <= 1 = xs | head xs == '"' && last xs == '"' = xs.tail.init | otherwise = xs (^) a b = (update_anti_slash ° del_enclose ° show) a ++ (update_anti_slash ° del_enclose ° show) b -- (^) a b = let op = update_anti_slash ° del_enclose ° show in op a ++ op b -- dont WORK !! AMAZING infixl 1 ^ |