-- -*- haskell -*- -- @@PLEAC@@_NAME -- @@SKIP@@ Haskell -- @@PLEAC@@_WEB -- @@SKIP@@ http://www.haskell.org/ -- @@PLEAC@@_INTRO -- @@SKIP@@ Many auxiliary functions can be found in appendix (Helpers). -- @@SKIP@@ You need hugs98 or ghc (tested with 5.002) and the external library: -- @@SKIP@@ http://www.dcs.gla.ac.uk/~meurig/regexp/ -- @@SKIP@@ The time section only work with ghc, s/--ghc // if you have ghc. -- @@SKIP@@ You need to enable multi-parameter class and functionnal depedencies -- @@SKIP@@ -98 with hugs, and -fglasgow-exts with ghc -- @@PLEAC@@_APPENDIX module Pleac where import Prelude hiding (($),(^),(.),(!!),map,take,lookup,drop,splitAt,reverse,filter,takeWhile,dropWhile,null,foldl) import Common ---------------------------------------------------------------------------------------------------- -- @@PLEAC@@_1.0 str = "\\n" -- two characters, \ and an n str2 = "Jon 'Maddog' Orwant" -- in haskell we can do string only with ", no single quote str3 = "\n" -- a "newline" character str4 = "Jon \"Maddog\" Orwant" -- literal double quotes -- no q/qq/qw/.. stuff in haskell -- no multiline, you can emulate it with ++ and \n, or unlines str5 = "This is a multiline here document\n" ++ "terminated by on a line by itself\n" -- @@PLEAC@@_1.1 s = "This is what you have" first = s!![0] -- "T" start = s!![5,6] -- "is" rest = s!![13..] -- "you have" las = s!![-1] -- "e" las2 = s.last -- 'e' end = s!![-4 .. -1] -- "have" piece = s!![-8 .. -5] -- "you" [lead,ss1,ss2,trailing] = s.unpack [Grab 2, Forward 3, Grab 1, Grab 2, GrabAll] -- Regexp are better for this, why introduce extra concept (unpack) [lead2,ss12,ss22,trailing2] = s.match "^(..)...(.)(..)(.*)" [lead3,ss13,ss23,trailing3] = s.match "^(.{2}).{3}(.)(.{2})(.*)" -- just to show how to use repetition fivers = s.unpack ((Grab 5).replicate (s.length `div` 5)) -- in haskell string == [Char] => no need to do unpack("A1" x length($string), $string) characters = s -- can't modify string in haskell s' = s.substr 5 2 "wasn't" -- "This wasn't what you have" s'' = s'.substr2 (-12) "ondrous" -- "This wasn't wondrous",note the () arount negative number :( s''' = s''.substr 0 1 "" -- "his wasn't wondrous", delete first character <=> tail s'''' = s'''.substr2 (-10) "" -- "his wasn'" ,delete last 10 characters <=> reverse . drop 10 . reverse m = if s!![-10 .. -1] =~ "have" then putStrLn "Pattern matches in last 10 characters" else return () m' = putStrLn "Pattern matches in last 10 characters" `when` (s!![-10 .. -1] =~ "have" ) -- cant do as short as in perl cos cant use affection in haskell news = s!![0..5].(substS "is" "at" [Global_Match]) ++ s!![6..] -- "That at what you have" a1 = "make a hat" a1' = a1!![-1] ++ a1!![1..(a1.length -2)] ++ a1!![0] -- "take a ham" b1 = "to be or not to be" b2 = b1.unpack [Forward 6, Grab 6] -- "or not" [b3,c3] = b1.unpack [Forward 6, Grab 2, Backward 5, Grab 2] -- ["or","be"] cut2fmt xs = xs.foldl aux (1,[]).snd.(GrabAll:).reverse where aux (n,l) i = (i,(Grab (i - n)) : l) fmt = cut2fmt [8,14,20,26,30] -- [Grab 7,Grab 6,Grab 6,Grab 6,Grab 4,GrabAll] -- @@PLEAC@@_1.2 v3 = "a" ||| "b" -- -> "a" v4 = "" ||| "b" -- -> "b" -- force to put the type :( v5 = (2::Int) &&& "ee" -- -> "ee" v6 = (0::Int) &&& "ee" -- -> "" -- no x ||= y v3' = v3 ||| v4 dir = argv >>> (\x -> (x ||| ["/tmp"]).head) dir' = argv >>> ((||| ["/tmp"]) $ head) -- or even more cryptic -- @@PLEAC@@_1.3 -- no side effect in haskell => swap is a nosense -- @@PLEAC@@_1.4 i1 = ord 'a' c1 = chr 97 -- ascii = string.unpack("C*"), string = ascii.pack("C*"), no need in haskell, String = [Char] -- there is also packedString that are more efficient but less cool cos not list anymore ibm = "HAL".map next_char -- "IBM" -- @@PLEAC@@_1.5 s1 = "unique chars are: " ++ ("an apple a day".unique.sort) sum1 = ("sum is " ^ s1.map ord.sum).putStrLn -- TODO, script.hs HERE -- @@PLEAC@@_1.6 s2 = s1.reverse -- can do: s3 = (unwords . reverse . words) s1 -- s3 = (words $ reverse $ unwords) s1 s3 = s1.words.reverse.unwords s3' = s1.split " ".reverse.join " " -- not the same, cos words = split "\s+" s3''= s1.split "\\s+".reverse.join " " -- eval long_palindromes or long_palindromes >>= (mapM putStrLn) long_palindromes = cat "/usr/share/dict/words" >>> filter(\s -> s == s.reverse && s.length > 4) -- @@PLEAC@@_1.7 -- TODO, must do: \s if s == [] then [] else foldr1 .... ? must make a foldr1sure expand_tabs = split "\t" $ (foldr1 (\a b -> a ++ (times (8 - a.length `mod` 8) ' ') ++ b)) -- more efficient -- expand_tabs = fixpoint (break_char '\t' (\a b -> a ++ (' '.times (8 - a.length `mod` 8)) ++ b)) -- @@PLEAC@@_1.8 -- cant do eval in haskell, too meta, too reflexif s4 = "I am 17 years old".subst "\\d+" (\n -> show ((read n) * 2)) -- @@PLEAC@@_1.9 s5 = "bo beep".upcase s6 = s5.downcase s7 = s6.capitalize s8 = "thIS is a loNG liNE".words.map capitalize -- @@PLEAC@@_1.10 -- s9 = "I have " ++ (show (10+1)) ++ " guanacos." s9 = "I have "^10+1^" guanacos." -- @@PLEAC@@_1.11 -- cant put lines at beginning of line cos of layout pb -- Multi_line is to say that ^ match beginning of every line, not of the string multi = (substS "^\\s+" "" [Global_Match,Multi_Line] ° unlines) [ " This is a multiline here document", " terminated by nothing" ] -- @@PLEAC@@_1.12 -- in fact this fonction is wrong, cos it insert a space at the front of the sentence (must use a foldl1) wrap width s = let (all, line) = (words s).foldl (\ (all, line) w -> if (line++" "++w).length > width then (all++[line], w) else (all, line++" "++w) ) ([],"") in join "\n" (all++[line]) -- @@PLEAC@@_1.13 escaping = "Mom Said, \"Don't do that.\"" esc1 = escaping.gsubst "['\"]" ("\\"++) -- section, <=> (\x -> "\\" ++ x) esc3 = escaping.gsubst "[^A-Z]" ("\\"++) esc4 = escaping.gsubst "[^\\w]" ("\\"++) -- @@PLEAC@@_1.14 s10 = " titi".substS "^\\s+" "" [] s11 = " titi ".substS "\\s+$" "" [] trim = substS "^\\s+" "" [] ° substS "\\s+$" "" [] -- @@PLEAC@@_1.15 parse_csv s = let re = ["\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\",?" -- "toto,titi" ,"([^,]+),?" -- something that not start with " can not contain any , => go until next , ,"," ].join "|" xxs = s.gmatch re in xxs.map (\xs -> (xs!(0::Int)) ||| (xs!(2::Int))) testline = "XYZZY,\"\",\"O'Reilly, Inc\",\"Wall, Larry\",\"a \\\"glug\\\" bit,\",5,\"Error, Core Dumped\"" mparse = testline.parse_csv.foldM(\i x -> putStrLn (i^" : "^x) >>> (\_ -> i+1)) 0 -- @@PLEAC@@_1.16 -- from explanation from http://www.myatt.demon.co.uk/sxalg.htm soundex s = x:'-':((res++[0,0,0]).take 3.map show.concat) -- note that as haskell is lazy, this code is efficient where group = [("AEIOUYHW",0) ,("BFPV",1) ,("CGJKQSXZ",2) ,("DT",3) ,("L",4) ,("MN",5) ,("R",6) ] code x = group.find (\(letters,_) -> x `elem` letters).just.snd (x:xs) = s.upcase.substS "[^\\w]" "" [Global_Match] -- A consonant immediately following an initial letter from the same code group is ignored xs' = if xs.length > 0 && xs.head.code == x.code && x.code > 0 then xs.tail else xs trim xs = xs.dropWhile (\x -> x.code == 0) next_code xs = xs.trim.(\xs -> if length xs > 0 then xs.head.code else 0) aux xs = case trim xs of [] -> [] (x:xs) -> if x.code == xs.next_code then aux xs else x.code:aux xs res = aux xs' soundexes = ["holmes" -- H-452 ,"adomomi" -- A-350 -- 355 ,"vonderlehr" -- V-536 ,"ball" -- B-400 ,"shaw" -- S-000 ,"jackson" -- J-250 ,"scanlon" -- S-545 ,"saintjohn" -- S-532 ,"kingsmith" -- K-525 ,"booth-davis" -- B-312 ,"Knuth" -- K-530 ,"Kant" -- K-530 ,"Lloyd" -- L-300 ,"Ladd" -- L-300 ].map soundex -- TODO, not complete ($quota,$comment,$gcos,$expire) = getpw* data Passwd_entry = Passwd_entry {pw_name,pw_passwd,pw_comment,pw_dir,pw_shell::String, pw_uid,pw_gid::Int} deriving (Eq,Show) getpwent s = let [name1,passwd1,uid1,gid1,comment1,dir1,shell1] = s.split ":" in Passwd_entry {pw_name=name1,pw_passwd=passwd1,pw_uid=read uid1,pw_gid=read gid1, pw_comment=comment1,pw_dir=dir1,pw_shell=shell1} msoundex = do putStr "Lookup user: " user <- getLine cat "/etc/passwd" >>= each (\l -> let Passwd_entry {pw_name=name,pw_comment=comment} = l.getpwent [firstname,lastname] = comment.match "(\\w+)[^,]*\\b(\\w+)" soundex' xs = if xs == [] then "" else xs.soundex in if user.soundex' `elem` (map soundex' [name,firstname,lastname]) then putStrLn (name++": "++firstname++" "++lastname) else return () ) -- TODO, pstyle and psgrep ---------------------------------------------------------------------------------------------------- -- @@PLEAC@@_2.1 is_integer s = if s =~ "^[+-]?\\d+$" then putStrLn "is an integer" else putStrLn "is not" m0 s = do putStrLn "has non digits" `when` (s =~ "[^\\d]") -- no \D putStrLn "not a natural" `unless` (s =~ "^\\d+$") putStrLn "not an integer" `unless` (s =~ "^-?\\d+$") putStrLn "not an integer" `unless` (s =~ "^[+-]?\\d+$") putStrLn "not a decimal" `unless` (s =~ "^-?\\d+\\.\\d*$") putStrLn "not a float" `unless` (s =~ "^[+-]?\\d+((\\.\\d+([eE]-?\\d+)?)|([eE]-?\\d+))$") -- in haskell, regexp are first class, then can be appended, sometimes easier to read m0' s = let sign = "-" opt_sign = sign ++ "?" dec_number = "\\d+" integer = opt_sign ++ dec_number exp = "eE" ++ opt_sign ++ dec_number real = "(" ++ opt_sign ++ dec_number ++ "\\." ++ dec_number ++ exp ++"?" ++ ")" ++ "|" ++ "(" ++ opt_sign ++ dec_number ++ exp ++ ")" in do putStrLn "not a natural" `unless` (s ==~ dec_number) putStrLn "not an integer" `unless` (s ==~ integer) putStrLn "not a decimal" `unless` (s ==~ (integer ++ "\\." ++ dec_number)) putStrLn "not a float" `unless` (s ==~ real) m0test = m0 "124" -- @@PLEAC@@_2.2 equal_num n1 n2 nbdecimal = (abs (n1 - n2)) < 1 / (10 ^^ nbdecimal) eqnum = equal_num 10.001 10.002 4 -- with 3 give True cos 10.001 - 10.002 != 0.001 but 0.000999 wage = 536 -- $5.36/hour week = 40 * wage -- $214.40 weekwage = putStrLn ("One week's wage is: $"++show_float 2 (tonum week/100.0)) -- @@PLEAC@@_2.3 i = 2.3.round a2 = 0.255 sa2 = show_float 2 a2 mprinta = putStrLn ("Unrounded: "^a2^"\nRounded: "^sa2) -- TODO, i dont exactly produce the good ouput, negative number are not centered mprintfloat = do putStrLn "number\tint\tfloor\tceil" [3.3 , 3.5 , 3.7, -3.3].each(\n -> putStrLn ((show_float 1 n)^"\t"^ (n.round)^"\t"^ (n.floor)^"\t"^ (n.ceiling))) -- @@PLEAC@@_2.4 -- TODO? not exacly this, as 54 = 110 110 we dont see the pb of big endian bin2i xs = aux 1 (reverse xs) where aux power [] = 0 aux power (x:xs) = aux (2*power) xs+(if x == '0' then 0 else power) bin2i' xs = xs.reverse.map (\c -> read [c]).zipWith (\a b -> b*(2^^a)) [0..].sum i10 = bin2i' "0110110" -- 54 i2bin i = if i == 0 then "0" else aux i where aux i = if i == 0 then "" else aux (i `div` 2) ++ show (i `mod` 2) bin10 = i2bin 54 -- "110110" -- @@PLEAC@@_2.5 m1 = putStr "Infancy is: " >> [0..2].each(\e -> putStr (e^" ")) m2 = putStr "Infancy is: " >> [0..2].map show.unwords.putStr -- @@PLEAC@@_2.6 -- code from comp.lang.functionnal inttorom n = (concat ° reverse) (zipWith roman sets digits) where digits = reverse (map (\x -> fromInt (read [x])) (show n)) roman (i,v,x) n = case n of { 0 -> []; 1 -> [i]; 2 -> [i,i]; 3 -> [i,i,i]; 4 -> [i,v]; 5 -> [v]; 6 -> [v,i]; 7 -> [v,i,i]; 8 -> [v,i,i,i]; 9 -> [i,x] } sets = [('I','V','X'), ('X','L','C'), ('C','D','M'), ('M','?','?')] mroman = putStrLn ("Roman for "^15^" is :"^inttorom 15) -- @@PLEAC@@_2.7 ran = rand (25, 75) -- [25,75] chars = concat [['A'..'Z'],['a'..'z'],['0'..'9'],"!@$%^&*"] --password = [1..8].foldM (\a _ -> rand (0, chars.length -1) >>= (\i -> return (a++chars![i]))) "" --password = [1..8].mapM (\_ -> rand (0, chars.length -1) >>= (\i -> return (chars!!i))) password = [1..8].mapM (\_ -> rand (0, chars.length -1) >>> (chars!)) -- no join, string = [Char] :) hihi section :) -- @@PLEAC@@_2.8 randfixed = do srand 2 rand(1,10) -- @@PLEAC@@_2.9 -- uses a Perl module.., in haskell too you can provide your own random generator by -- playing with the StdGen type -- @@PLEAC@@_2.10 gaussian_rand = do r1 <- stdrand r2 <- stdrand let u1 = 2 * r1 - 1 u2 = 2 * r2 - 1 w = u1*u1 + u2*u2 in if w >= 1 then gaussian_rand else let w2 = sqrt ((-2 * log w)/w) in return [u2*w2, u1*w2] mean = 25 sdev = 2 salary = gaussian_rand >>> (\l -> l.head * (tonum sdev) + (tonum mean)) msalary = do r <- salary putStrLn ("You have been hired at $"^show_float 2 r) -- @@PLEAC@@_2.11 deg2rad d = d * pi / 180 rad2deg r = r * 180 / pi -- @@PLEAC@@_2.12 sin_val = sin pi cos_val = cos pi tan_val = tan pi asin_val = asin 1 acos_val = acos 1 -- @@PLEAC@@_2.13 log_e = log 10 log_10 = log10 100 -- 2 -- in haskell we dont have by default log10 but we have logBase log10 = logBase 10 answer = logBase 10 10000 mlog = putStrLn ("log10(10,000)="^answer) -- @@PLEAC@@_2.14 matrixa = Matrix[[3, 2, 3], [5, 9, 8]] matrixb = Matrix[[4, 7], [9, 3], [8, 1]] matrixc = matrixa * matrixb masize = matrixa.row_size mbsize = matrixa.column_size --TODO,transposed = matrixc.transpose --determined = matrixa.det -- @@PLEAC@@_2.15 ca = 3 :+ 5 cb = 2 :+ -2 mcomplex = putStrLn ("c = "^(ca*cb)) cc = ca*cb cd = 3 + 4*complex_i mcomplex2 = putStrLn ("sqrt("^cd^") = "^sqrt cd) -- @@PLEAC@@_2.16 i2 = hex "0x45" i3 = oct "0o45" m3 = do putStrLn "Gimme a number in decimal, octal, or hex: " s <- getLine let i = s.pattern_matches [("^0x", hex s),("^0o", oct s),("", read s)] in putStrLn (concat [show i," ",tohex i," ", tooct i,"\n"]) -- no printf :(( m3' = do putStrLn "Enter file permission in octal: " permissions <- getLine putStrLn ("The decimal value is "^permissions.oct) -- @@PLEAC@@_2.17 commify s = if s.length <= 3 then s else let (a,b) = splitAt (s.length -3) s in commify a ++ "," ++ b -- @@PLEAC@@_2.18 plurals i = if i > 1 then "s" else "" pluralies i = (i > 1) ? ("ies","y") hours = 2 century = 2 flies = 3 mplural = putStrLn ("It took "^hours^" hour"^(plurals hours)) mplural2 = putStrLn ("It took "^century^" centur"^(pluralies century)) -- TODO, the suffix program and prime factor ---------------------------------------------------------------------------------------------------- --ghc -- @@PLEAC@@_3.0 --ghc mtime1 = now >>= (\d -> putStrLn (calendarTimeToString d)) --ghc mtime2 = do d <- now --ghc putStrLn ("Today is day "^d.ctYDay^" of the current year") --ghc putStrLn ("Today is day "^d.ctDay^ " of the current month") --ghc --ghc --ghc -- @@PLEAC@@_3.1 --ghc (day,month,year) = (now >>> ctDay, now >>> ctMonth, now >>> ctYear) --ghc tl = now >>= localtime --ghc --ghc mtime3 = do date <- tl --ghc putStrLn ("The current date is "^date.ctYear^" "^date.ctMonth^" "^date.ctDay) --ghc --ghc mtime4 = do d <- now --ghc putStrLn (strftime "%Y-%m-%d" d) --ghc --ghc -- TODO 3.2, 3.3 --ghc --ghc -- @@PLEAC@@_3.4 --ghc add_sec sec d = d.toClockTime.addToClockTime (nulltime {tdSec= sec}) --ghc when_ = now >>> (\d -> d.add_sec 100) --ghc then_ = now >>> (\d -> d.add_sec (-100)) --ghc --ghc -- @@PLEAC@@_3.5, TODO, not complete --ghc bree = 361535725 --ghc nat = 96201950 --ghc --ghc differenc = bree - nat --ghc mtime5 = putStrLn ("there were "^differenc^"seconds between Nat and Bree") --ghc --ghc seconds = differenc `mod` 60 --ghc differenc' = (differenc - seconds) `div` 60 --ghc minutes = differenc' `mod` 60 --ghc differenc2 = (differenc' - minutes) `div` 60 --ghc hours = differenc2 `mod` 24 --ghc differenc3 = (differenc2 - hours) `div` 24 --ghc days = differenc3 `mod` 7 --ghc weeks = (differenc3 - days) `div` 7 --ghc --ghc -- (438 weeks, 4 days, 23:49:35) --ghc mtime6 = putStrLn ("("^weeks^" weeks, "^days^" days, "^hours^":"^minutes^":"^seconds^")") --ghc --ghc -- @@PLEAC@@_3.6 --ghc (monthday, weekday, yearday) = (now >>> ctDay, now >>> ctWDay, now >>> ctYDay) --ghc --ghc weeknum = now >>> (\d -> d.strftime "%U".read + 1) --ghc --ghc -- TODO 3.9 --ghc usec = now >>> ctPicosec -- is this ? --ghc --ghc -- TODO _3.10 --ghc -- have sleep::Int but not Double :( ---------------------------------------------------------------------------------------------------- -- @@PLEAC@@_4.0 single_level = [ "this", "that", "the", "other" ] -- can make nested list, but must all have the same depth -- @@PLEAC@@_4.1 a = ["quick", "brown", "fox"] a' = "Why are you teasing me?".words -- no multiline in haskell big_array = cat "pleac.hs" name1 = "toto" banner_scalar = "Speak, "^name1^" and welcome!" his_host = "www.haskell.org" host_info = exec ("host "^his_host) psinfo1 = exec ("ps $$") -- that's the new shell's $$ banner_array1 = ["Costs","only","$4.95"] banner_array2 = "Costs only $4.95".words banner_array3 = "Costs only $4.95".split " " -- @@PLEAC@@_4.2 commify_series :: [String] -> String commify_series [] = "" commify_series [x] = x commify_series xs = join ", " (init xs) ++ " and " ++ (last xs) -- commify_series ["cava", "etoi","ouais"] => "cava, etoi and ouais" array = ["red", "yellow", "green"] s12 = "I have "++commify_series array++" marbles" marray1 = putStrLn ("I have "^(concat array)^" marbles") -- I have redyellowgreen marbles marray2 = putStrLn ("I have "^(unwords array)^" marbles") -- I have red yellow green marbles -- @@PLEAC@@_4.3 -- grow/shring the array by assigning nil to past the end of array change_size n xs = if n <= xs.length then take n xs else xs++(null_val.replicate (n - xs.length)) change_val ix e xs = let xs' = if ix <= xs.length then xs else change_size ix xs in xs'!![0..ix-1]++[e]++xs'!![ix+1..] array2 = array.change_size 5 array3 = array.change_val 5 "toto" array4 = "toto".replicate 5 what_about_that_array x = "The array now has "^length x^"elements.\n"^ "The index of the last element is "^length x - 1^".\n"^ "Element .3 is`"^x!(3::Int)^"'.\n" s13 = "Crosby Stills Nash Young".words.what_about_that_array -- @@PLEAC@@_4.4 -- in hugs even this take time :), sort is insertion sort :) by default m4 = env >>= (\l -> l.keys.sort.each(\var -> (var ^"=" ^ (l!var)).putStrLn)) m5 = env >>= (\l -> l.each(\(var,val) -> (var^"="^val).putStrLn)) -- but non-sorted m6 = env >>= (\l -> l.toList.sortBy(\(a,_) (b,_) -> a <=> b).each(\(var,val) -> (var^"="^val).putStrLn)) -- @@PLEAC@@_4.5 -- not relevant in haskell since we have always kind of references -- @@PLEAC@@_4.6 users = exec "who" >>> map (words $ head) $ sort $ unique users2 = exec "who" >>> map (match "(\\w+)" $ head) $ sort $ unique -- cant do as short as ruby: puts("users logged in: .{commify_series(users)}") -- monad are too intrusive and we need to introduce an extra var l, :((( m10 = do l <- users putStrLn ("users logged in:"++(commify_series l)) -- @@PLEAC@@_4.7 l1 = [1..5] `difference` [2,5] -- could make [1..5] - [2,5], just have to make an instance Num instance (Eq a,Show a) => Num ([a]) where a + b = a `union` b (-) a b = a `difference` b -- we can write it like that (*) = intersect -- or even shorter -- make this instance is very dangerous, cos now typechecker not detect the error [show 1,show 2]++[0,0], WHY ? l1' = [1..5] - [2,5] -- @@PLEAC@@_4.8 l2 = [1..5] l3 = [2,5,7] l4 = l2 `union` l3 l4' = l2 + l3 l5 = l2 `intersect` l3 l5' = l2 * l3 l6 = difference (union l2 l3) (intersect l2 l3) l6' = (l2 + l3) - (l2 * l3) -- @@PLEAC@@_4.9 members = ["Times","Flies"] initiates = ["An", "Arrow"] newmembers = members ++ initiates newmembers2 = members.insert_at 2 ("Like":initiates) newmembers3 = members.replace [(0, "Fruit")] newmembers4 = members.replace [(2, "Fruit"),(3,"Banana")] -- @@PLEAC@@_4.10 reversed = [1..5].reverse array5 = [3,2,5,1] descending = array5.sort.reverse descending' = array5.sortBy (\a b -> b <=> a) descending2 = array5.sortBy (flip (<=>)) -- @@PLEAC@@_4.11 -- remove n elements from front of ary (shift n) (front,newarray) = array5.splitAt 1 -- remove n elements from the end of ary (pop n) (newarray',end') = array5.splitAt (array5.length - 1) shift2 (x:y:ys) = ((x,y),ys) -- shift2 = zip ... shift ... TODO pop2 xs = let (a,[x,y]) = splitAt (xs.length -2) xs in ((x,y),a) friends = "Peter Paul Mary Jim Tim".words ((this, that), newfriends) = friends.shift2 beverages = "Dew Jolt Cola Sprite Fresca".words (pair,newbeverages) = beverages.pop2 -- @@PLEAC@@_4.12 data Employee = Employee {category,name:: String, income::Int} deriving (Show,Eq) employees = [Employee {category="neuneu",name="ben",income=1000} ,Employee {category="neuneu",name="pixel",income=1000} ,Employee {category="engineer",name="pad",income=27000} ,Employee {category="engineer",name="guit",income=100000} ] -- can do also with classic assoc list -- employees = [("ben","neuneu1"),("pixel","neuneu2"),("pad","engineer"),("guit","engineer")] -- category = snd -- name = fst (Just highest_engineer) = employees.find(\employee -> employee.category == "engineer") s14 = "Highest paid engineer is: "++ highest_engineer.name -- @@PLEAC@@_4.13 bigs = [5..200].filter (>50) -- hihi section :) matching = exec "who" >>> filter (=~ "^gnat") engineers = [ x | x <- employees, category x == "engineer"] engineers' = employees.filter (\x -> category x == "engineer") secondary_assistance = employees.filter(\x -> x.income >= 26000 && x.income < 30000) -- @@PLEAC@@_4.14 -- normally you would have an array of Numeric (Float or -- Fixnum or Bignum), so you would use: sorted1 = [11,5,2,8].sort -- if you have strings representing Integers or Floats -- you may specify another sort method (not lexicographic ordering): sorted2 = ["11","5","1","8"].sort -- ["1","11","5","8"] sorted2' = ["11","5","1","8"].sortBy (\a b -> ((read a)::Int) <=> read b) -- ["1","5","8","11"] -- cant inline expression :( pidsorted = do ps <- exec "ps ux" myenv <- env ps!![1..] -- avoid the indication string: USER TTY .... .filter (=~ (myenv!"USER")) .map (words $ (!1)) .sortBy (\a b -> ((read a)::Int) <=> read b) .each putStrLn mkill = do putStr "Select a process ID to kill:" pids <- getLine (putStrLn "Exiting" >> exit 0) `unless` (pids =~ "\\d+") _ <- exec ("kill "^pids) _ <- exec ("sleep 2") exec ("kill -9 "^pids) -- @@PLEAC@@_4.15 unordered = [1,-3,2,6] ordered = unordered.sort precomputed = unordered.map (\e -> [abs e, e]) ordered_precomputed = precomputed.sortBy (\a b -> (a!0) <=> (b!0)) ordered' = ordered_precomputed.map (!1) ordered'' = unordered.map (\e -> [abs e,e]).sortBy (\a b -> (a!0) <=> (b!0)).map (!1) employee_sorted = employees.sortBy (\a b -> (a.name) <=> (b.name)) musers = cat "/etc/passwd" >>= (map (split ":" $ head) $ sort $ mapM putStrLn) musers' = cat "/etc/passwd" >>= (\x -> x.map (\l -> l.split ":".head).sort.each putStrLn) -- @@PLEAC@@_4.16 -- in haskell we can use infinite list circular = [1,2,3,4,5]++circular grab_and_rotate (x:xs) = (x, xs++[x]) mprocess1 = loop [1,2,3,4,5] where loop processes = do putStrLn ("Handling process"^process) system "sleep 1" loop newprocess return () -- forced, WHY? where (process, newprocess) = grab_and_rotate processes mprocess2 = loop circular where loop (x:xs) = do putStrLn ("Handling process"^x) system "sleep 1" loop xs return () -- forced, WHY? -- @@PLEAC@@_4.17 -- brute force permut [] = [] permut [x] = [[x]] permut (x:xs) = xs.permut.map (insert x).concat where insert x [] = [[x]] insert x (y:ys) = (x:y:ys) : map (y:) (insert x ys) fact 0 = 1 fact (n+1) = (n+1) * fact n randomizing xs = rand (0, xs.length.fact -1) >>> ((xs.permut)!) -- @@PLEAC@@_4.19 bigint = fact 500 -- on hugs, i have 1134 digits permute = permut ---------------------------------- #!/usr/bin/runhugs module Main where import Prelude hiding ((.)) -- usage: echo dog eat cat | ./script.hs permut [] = [] permut [x] = [[x]] permut (x:xs) = xs.permut.map (insert x).concat where insert x [] = [[x]] insert x (y:ys) = (x:y:ys) : map (y:) (insert x ys) fact 0 = 1 fact (n+1) = (n+1) * fact n (.) o f = f o -- object notation for object fan (°) f g x = f (g x) main = do line <- getLine line.words.permut.mapM_(putStrLn ° unwords) ---------------------------------- -- @@PLEAC@@_5.0 -- this choose for association the traditionnal assoc list representation age = [ ("Nat",24) ,("Jules",25) ,("Josh",17)] -- if you preprend the list with fromList, then you can decide what representaion to choose by typing age age':: Assoc String Int age' = fromList [ ("Nat",24) ,("Jules",25) ,("Josh",17)] age1:: Assoc String Int age1 = empty age2 = age1.insert ("Nat",24) age3 = age2.insert ("Jules",25) age4 = age3.insert ("Josh",17) food_color :: Assoc String String food_color = fromList [ "Apple" ==> "red" ,"Banana" ==> "yellow" ,"Lemon" ==> "yellow" ,"Carrot" ==> "orange" ] -- In haskell, you cannot avoid the double quoting while manipulatin hashes -- @@PLEAC@@_5.1 food_color' = food_color.insert ("Raspberry" ==> "pink") mfood_color = putStrLn ("Know foods: "^(unwords (food_color.keys))) -- @@PLEAC@@_5.2 -- does hash have a value for key ? mhash = ["Banana", "Martini"].mapM(\name -> putStrLn (name^" is a "^(food_color.has_key name ? ("food","drink"))) ) -- @@PLEAC@@_5.3 food_color2 = food_color.delete_key "Banana" -- @@PLEAC@@_5.4 mhash2 = food_color.each (\(food,color) -> putStrLn (food^" is "^color) ) mhash3 = food_color.each_key(\food -> putStrLn (food^" is "^(food_color!food)) ) -- IMO too (as gc), this demonstrates that OO style is by far more readable mhash4 = food_color.keys.sort.each(\food -> putStrLn (food^" is "^(food_color!food)) ) --------------------------------------- #!/usr/bin/runhugs -98 module Main where import Prelude hiding (($),(^),(.),(!!),map,take,lookup,drop,splitAt,reverse,filter,takeWhile,dropWhile,null,foldl,length) import Common -- TODO, very slow, try with ghc -- countfrom - count number of messages from each sender main = do lines <- (<>) lines.foldl(\from l -> if l =~ "^From: " then let [name] = l.match "^From: (.*)" in from.update name (+1) else from ) (empty::AssocDefault String Int) .toList.sortBy (\(_,a) (_,b) -> b <=> a) .each (\(a,b) -> putStrLn (b^": "^a)) --------------------------------------- -- @@PLEAC@@_5.5 -- You may use the not built-in 'show' (poor ruby) mhash5 = putStrLn (age4.show) -- Or do it the Cookbook way: mhash5' = age4.each (\(k,v) -> putStrLn (k^" => "^v)) -- Sorted by keys mhash6 = age4.toList.sortBy (\a b -> (a.fst) <=> (b.fst)).each(\(k,v) -> putStrLn (k^" => "^v)) -- Sorted by values mhash7 = age4.toList.sortBy (\a b -> (a.snd) <=> (b.snd)).each(\(k,v) -> putStrLn (k^" => "^v)) -- @@PLEAC@@_5.7 mttys = do who <- exec "who" who.foldl(\ttys l -> let (user:tty:_)= l.words in ttys.update tty (insert user) ) (empty::AssocDefault String (SetList String)) .toList.sortBy (\(a,_) (b,_) -> b <=> a) .each (\(a,b) -> putStrLn (a^": "^commify_series (b.toList))) -- @@PLEAC@@_5.8 surname:: Assoc String String surname = fromList ["Mickey" ==> "Mantle", "Babe" ==> "Ruth"] mhash8 = surname.keyss "Mantle".head.putStrLn invert_surname:: Assoc String String invert_surname = surname.toList.map (\(a,b) -> (b,a)).fromList --------------------------------------- #!/usr/bin/runhugs -98 module Main where import Prelude hiding (($),(^),(.),(!!),map,take,lookup,drop,splitAt,reverse,filter,takeWhile,dropWhile,null,foldl,length) import Common color :: Assoc String String color = fromList [ "Apple" ==> "red", "Banana" ==> "yellow", "Lemon" ==> "yellow", "Carrot" ==> "orange" ] -- foodfind - find match for food or color main = do args <- argv usage "foodfind food_or_color" `when` (args.null) let given = args.head in do putStrLn (given^" is a good with color "^(color!given)) `when` (color.has_key given) putStrLn (given^" is a color with food "^(color.keyss given.head)) `when` (color.has_value given) --------------------------------------- -- @@PLEAC@@_5.9 -- Sorted by keys mhash9 = age4.toList.sortBy (\a b -> (a.fst) <=> (b.fst)).each (\(k,v) -> putStrLn (k^" is "^v)) -- Sorted by values mhash10 = age4.toList.sortBy (\a b -> (a.snd) <=> (b.snd)).each (\(k,v) -> putStrLn (k^" is "^v)) -- Sorted by length of values mhash11 = food_color.toList.sortBy (\a b -> (a.snd.length) <=> (b.snd.length)).each (\(k,v) -> putStrLn (k^" is "^v)) -- @@PLEAC@@_5.10 drink_color:: Assoc String String drink_color = fromList ["Galliano" ==> "yellow", "Mai Tai" ==> "blue"] -- in haskell no clone, we do implictly always clone :) ingested_color = drink_color `union` food_color substance_color :: IO (Assoc String Int) substance_color = [food_color, drink_color] .foldM (\a i -> i.keys.foldM (\a k -> if a.has_key k then putStrLn ("Warning: "^k^" seen twice") >> (return a) else return (a.insert (k,1)) ) a ) empty -- @@PLEAC@@_5.11 common = food_color.keys * drink_color.keys this_not_that = food_color.keys - drink_color.keys -- @@PLEAC@@_5.12 -- no problem here, haskell handles any kind of object for key-ing -- TODO 5.13 AFAIK, not possible in Ruby -- @@PLEAC@@_5.14 count:: AssocDefault String Int count = ["toto","toto","toto","tata","tata","tutu"].foldl (\a s -> a.update s (+1)) empty --@PLEAC@@_5.15 father:: Assoc String String father = fromList ["Cain" ==> "Adam", "Abel" ==> "Adam", "Seth" ==> "Adam", "Enoch" ==> "Cain", "Irad" ==> "Enoch", "Mehujael" ==> "Irad", "Methusael" ==> "Mehujael", "Lamech" ==> "Methusael", "Jabal" ==> "Lamech", "Jubal" ==> "Lamech", "Tubalcain" ==> "Lamech", "Enos" ==> "Seth" ] children :: AssocDefault String (SetList String) children = father.foldl (\a (k,v) -> a.update v (insert k)) empty -- TODO -- @@PLEAC@@_APPENDIX ---------------------------------------------------------------------------------------------------- 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 ^