4. Arrays

Introduction

single_level = [ "this", "that", "the", "other" ]
-- can make nested list, but must all have the same depth

Specifying a List In Your Program

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 " "

Printing a List with Commas

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

Changing Array Size

-- 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

Doing Something with Every Element in a List

-- 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))

Iterating Over an Array by Reference

-- not relevant in haskell since we have always kind of references

Extracting Unique Elements from a List

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))

Finding Elements in One Array but Not Another

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]

Computing Union, Intersection, or Difference of Unique Lists

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)

Appending One Array to Another

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")]

Reversing an Array


reversed = [1..5].reverse
array5 = [3,2,5,1]
descending  = array5.sort.reverse
descending' = array5.sortBy (\a b -> b <=> a)
descending2 = array5.sortBy (flip (<=>))

Processing Multiple Elements of an Array

-- 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

Finding the First List Element That Passes a Test

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

Finding All Elements in an Array Matching Certain Criteria

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)

Sorting an Array Numerically

-- 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)

Sorting a List by Computable Field

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)

Implementing a Circular List

-- 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?

Randomizing an Array

-- 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)!)

Program: words

Program: permute

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)

----------------------------------