\subsection{Utility functions} \begin{haskelllisting} > module Haskore.General.Utility where > > import System.Random (randomRs, mkStdGen, ) > import Data.List.HT (segmentBefore, ) > import Data.List (foldl', ) > import Data.Ratio ((%), denominator, numerator, Ratio, ) > import qualified Haskore.General.Map as Map \end{haskelllisting} \function{splitBy} takes a boolean test and a list; it divides up the list and turns it into a {\em list of sub-lists}; each sub-list consists of \begin{enumerate} \item one element for which the test is true (or the first element in the list), and \item all elements after that element for which the test is false. \end{enumerate} For example, \code{splitBy (>10) [27, 0, 2, 1, 15, 3, 42, 4]} yields \code{[ [27,0,2,1], [15,3], [42,4] ]}. \begin{haskelllisting} > splitBy :: (a -> Bool) -> [a] -> [[a]] > splitBy p = dropWhile null . segmentBefore p \end{haskelllisting} \function{segmentBefore} will have at most one empty list at the beginning, which is dropped by \function{dropWhile}. It should have signature segmentBefore :: (a -> Bool) -> [a] -> ([a], [(a, [a])]) or even better segmentBefore :: (a -> Bool) -> [a] -> AlternatingListUniform.T a [a] and could be implemented using Uniform.fromEitherList A variant of \function{foldr} and \function{foldr1} which works only for non-empty lists and initializes the accumulator depending on the last element of the list. \begin{haskelllisting} > foldrf :: (a -> b -> b) -> (a -> b) -> [a] -> b > foldrf f g = > let aux [] = error "foldrf: list must be non-empty" > aux (x:[]) = g x > aux (x:xs) = f x (aux xs) > in aux \end{haskelllisting} \function{flattenTuples2} flattens a list of pairs into a list. Similarly, \function{flattenTuples3} flattens a list of 3-tuples into a list, and so on. \begin{haskelllisting} > flattenTuples2 :: [(a,a)] -> [a] > flattenTuples3 :: [(a,a,a)] -> [a] > flattenTuples4 :: [(a,a,a,a)] -> [a] > > flattenTuples2 = concatMap (\(x,y) -> [x,y]) > flattenTuples3 = concatMap (\(x,y,z) -> [x,y,z]) > flattenTuples4 = concatMap (\(x,y,z,w) -> [x,y,z,w]) \end{haskelllisting} Variants of \function{zip} and \function{zip3} which check that all argument lists have the same length. \begin{haskelllisting} > zipWithMatch :: (a -> b -> c) -> [a] -> [b] -> [c] > zipWithMatch f (x:xs) (y:ys) = f x y : zipWithMatch f xs ys > zipWithMatch _ [] [] = [] > zipWithMatch _ _ _ = error "zipWithMatch: lengths of lists differ" > zipWithMatch3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d] > zipWithMatch3 f (x:xs) (y:ys) (z:zs) = f x y z : zipWithMatch3 f xs ys zs > zipWithMatch3 _ [] [] [] = [] > zipWithMatch3 _ _ _ _ = error "zipWithMatch3: lengths of lists differ" \end{haskelllisting} This is a variant of \function{maximum} which returns at least zero, i.e. always a non-negative number. This is necessary for determining the length of a parallel music composition where the empty list has zero duration. \begin{haskelllisting} > maximum0 :: (Ord a, Num a) => [a] -> a > maximum0 = foldl' max 0 \end{haskelllisting} Convert a mapping (i.e. list of pairs) to a function, and use this for a translation function, which translates every character in a by replacing it by looking it up in l2 and replacing it with the according character in l2. \begin{haskelllisting} > translate :: (Ord a) => [ a ] -> [ a ] -> [ a ] -> [ a ] > translate l1 l2 a = > if length l1 == length l2 > then let table = Map.fromList (zip l1 l2) > in map (\x -> Map.findWithDefault table x x) a > else error "translate: lists must have equal lengths" \end{haskelllisting} A random list of integers between 0 and n. \begin{haskelllisting} > randList :: Int -> [ Int ] > randList n = randomRs (0, n) (mkStdGen 0) \end{haskelllisting} Is one rational divisible by another one (i.e., is it a integer multiple of it)? \begin{haskelllisting} > divisible :: Integral a => Ratio a -> Ratio a -> Bool > divisible r1 r2 = > 0 == mod (numerator r1 * denominator r2) > (numerator r2 * denominator r1) \end{haskelllisting} Do the division. \begin{haskelllisting} > divide :: Integral a => Ratio a -> Ratio a -> a > divide r1 r2 = > let (q, r) = divideModulus r1 r2 > in if r == 0 > then q > else error "Utility.divide: rationals are indivisible" > modulus :: Integral a => Ratio a -> Ratio a -> Ratio a > modulus r1 r2 = snd (divideModulus r1 r2) > divideModulus :: Integral a => Ratio a -> Ratio a -> (a, Ratio a) > divideModulus r1 r2 = > let (q, r) = divMod (numerator r1 * denominator r2) > (numerator r2 * denominator r1) > in (q, r % (denominator r1 * denominator r2)) \end{haskelllisting} Also the GCD can be generalized to ratios: \begin{haskelllisting} > gcdDur :: Integral a => Ratio a -> Ratio a -> Ratio a > gcdDur x1 x2 = > let a = numerator x1 > b = denominator x1 > c = numerator x2 > d = denominator x2 > in gcd a c % lcm b d \end{haskelllisting}