237 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| # General list operations.
 | |
| 
 | |
| with import ./trivial.nix;
 | |
| 
 | |
| rec {
 | |
| 
 | |
|   inherit (builtins) head tail length isList elemAt concatLists filter elem;
 | |
| 
 | |
| 
 | |
|   # Create a list consisting of a single element.  `singleton x' is
 | |
|   # sometimes more convenient with respect to indentation than `[x]'
 | |
|   # when x spans multiple lines.
 | |
|   singleton = x: [x];
 | |
| 
 | |
| 
 | |
|   # "Fold" a binary function `op' between successive elements of
 | |
|   # `list' with `nul' as the starting value, i.e., `fold op nul [x_1
 | |
|   # x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'.  (This is
 | |
|   # Haskell's foldr).
 | |
|   fold = op: nul: list:
 | |
|     let
 | |
|       len = length list;
 | |
|       fold' = n:
 | |
|         if n == len
 | |
|         then nul
 | |
|         else op (elemAt list n) (fold' (n + 1));
 | |
|     in fold' 0;
 | |
| 
 | |
|   # Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul
 | |
|   # x_1) x_2) ... x_n)'.
 | |
|   foldl = op: nul: list:
 | |
|     let
 | |
|       len = length list;
 | |
|       foldl' = n:
 | |
|         if n == -1
 | |
|         then nul
 | |
|         else op (foldl' (n - 1)) (elemAt list n);
 | |
|     in foldl' (length list - 1);
 | |
| 
 | |
| 
 | |
|   # map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] ==
 | |
|   # ["a-1" "b-2"]'
 | |
|   imap = f: list:
 | |
|     let
 | |
|       len = length list;
 | |
|       imap' = n:
 | |
|         if n == len
 | |
|           then []
 | |
|           else [ (f (n + 1) (elemAt list n)) ] ++ imap' (n + 1);
 | |
|     in imap' 0;
 | |
| 
 | |
| 
 | |
|   # Map and concatenate the result.
 | |
|   concatMap = f: list: concatLists (map f list);
 | |
| 
 | |
| 
 | |
|   # Flatten the argument into a single list; that is, nested lists are
 | |
|   # spliced into the top-level lists.  E.g., `flatten [1 [2 [3] 4] 5]
 | |
|   # == [1 2 3 4 5]' and `flatten 1 == [1]'.
 | |
|   flatten = x:
 | |
|     if isList x
 | |
|     then fold (x: y: (flatten x) ++ y) [] x
 | |
|     else [x];
 | |
| 
 | |
| 
 | |
|   # Remove elements equal to 'e' from a list.  Useful for buildInputs.
 | |
|   remove = e: filter (x: x != e);
 | |
| 
 | |
| 
 | |
|   # Find the sole element in the list matching the specified
 | |
|   # predicate, returns `default' if no such element exists, or
 | |
|   # `multiple' if there are multiple matching elements.
 | |
|   findSingle = pred: default: multiple: list:
 | |
|     let found = filter pred list; len = length found;
 | |
|     in if len == 0 then default
 | |
|       else if len != 1 then multiple
 | |
|       else head found;
 | |
| 
 | |
| 
 | |
|   # Find the first element in the list matching the specified
 | |
|   # predicate or returns `default' if no such element exists.
 | |
|   findFirst = pred: default: list:
 | |
|     let found = filter pred list;
 | |
|     in if found == [] then default else head found;
 | |
| 
 | |
| 
 | |
|   # Return true iff function `pred' returns true for at least element
 | |
|   # of `list'.
 | |
|   any = pred: fold (x: y: if pred x then true else y) false;
 | |
| 
 | |
| 
 | |
|   # Return true iff function `pred' returns true for all elements of
 | |
|   # `list'.
 | |
|   all = pred: fold (x: y: if pred x then y else false) true;
 | |
| 
 | |
| 
 | |
|   # Count how many times function `pred' returns true for the elements
 | |
|   # of `list'.
 | |
|   count = pred: fold (x: c: if pred x then c + 1 else c) 0;
 | |
| 
 | |
| 
 | |
|   # Return a singleton list or an empty list, depending on a boolean
 | |
|   # value.  Useful when building lists with optional elements
 | |
|   # (e.g. `++ optional (system == "i686-linux") flashplayer').
 | |
|   optional = cond: elem: if cond then [elem] else [];
 | |
| 
 | |
| 
 | |
|   # Return a list or an empty list, dependening on a boolean value.
 | |
|   optionals = cond: elems: if cond then elems else [];
 | |
| 
 | |
| 
 | |
|   # If argument is a list, return it; else, wrap it in a singleton
 | |
|   # list.  If you're using this, you should almost certainly
 | |
|   # reconsider if there isn't a more "well-typed" approach.
 | |
|   toList = x: if isList x then x else [x];
 | |
| 
 | |
| 
 | |
|   # Return a list of integers from `first' up to and including `last'.
 | |
|   range = first: last:
 | |
|     if last < first
 | |
|     then []
 | |
|     else [first] ++ range (first + 1) last;
 | |
| 
 | |
| 
 | |
|   # Partition the elements of a list in two lists, `right' and
 | |
|   # `wrong', depending on the evaluation of a predicate.
 | |
|   partition = pred:
 | |
|     fold (h: t:
 | |
|       if pred h
 | |
|       then { right = [h] ++ t.right; wrong = t.wrong; }
 | |
|       else { right = t.right; wrong = [h] ++ t.wrong; }
 | |
|     ) { right = []; wrong = []; };
 | |
| 
 | |
| 
 | |
|   zipListsWith = f: fst: snd:
 | |
|     let
 | |
|       len1 = length fst;
 | |
|       len2 = length snd;
 | |
|       len = if len1 < len2 then len1 else len2;
 | |
|       zipListsWith' = n:
 | |
|         if n != len then
 | |
|           [ (f (elemAt fst n) (elemAt snd n)) ]
 | |
|           ++ zipListsWith' (n + 1)
 | |
|         else [];
 | |
|     in zipListsWith' 0;
 | |
| 
 | |
|   zipLists = zipListsWith (fst: snd: { inherit fst snd; });
 | |
| 
 | |
| 
 | |
|   # Reverse the order of the elements of a list.  FIXME: O(n^2)!
 | |
|   reverseList = fold (e: acc: acc ++ [ e ]) [];
 | |
| 
 | |
| 
 | |
|   # Sort a list based on a comparator function which compares two
 | |
|   # elements and returns true if the first argument is strictly below
 | |
|   # the second argument.  The returned list is sorted in an increasing
 | |
|   # order.  The implementation does a quick-sort.
 | |
|   sort = strictLess: list:
 | |
|     let
 | |
|       len = length list;
 | |
|       first = head list;
 | |
|       pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (n + 1); in
 | |
|         if n == len
 | |
|           then acc
 | |
|         else if strictLess first el
 | |
|           then next { inherit left; right = [ el ] ++ right; }
 | |
|         else
 | |
|           next { left = [ el ] ++ left; inherit right; };
 | |
|       pivot = pivot' 1 { left = []; right = []; };
 | |
|     in
 | |
|       if len < 2 then list
 | |
|       else (sort strictLess pivot.left) ++  [ first ] ++  (sort strictLess pivot.right);
 | |
| 
 | |
| 
 | |
|   # Return the first (at most) N elements of a list.
 | |
|   take = count: list:
 | |
|     let
 | |
|       len = length list;
 | |
|       take' = n:
 | |
|         if n == len || n == count
 | |
|           then []
 | |
|         else
 | |
|           [ (elemAt list n) ] ++ take' (n + 1);
 | |
|     in take' 0;
 | |
| 
 | |
| 
 | |
|   # Remove the first (at most) N elements of a list.
 | |
|   drop = count: list:
 | |
|     let
 | |
|       len = length list;
 | |
|       drop' = n:
 | |
|         if n == -1 || n < count
 | |
|           then []
 | |
|         else
 | |
|           drop' (n - 1) ++ [ (elemAt list n) ];
 | |
|     in drop' (len - 1);
 | |
| 
 | |
| 
 | |
|   # Return the last element of a list.
 | |
|   last = list:
 | |
|     assert list != []; elemAt list (length list - 1);
 | |
| 
 | |
| 
 | |
|   # Return all elements but the last
 | |
|   init = list: assert list != []; take (length list - 1) list;
 | |
| 
 | |
| 
 | |
|   # Zip two lists together.
 | |
|   zipTwoLists = xs: ys:
 | |
|     let
 | |
|       len1 = length xs;
 | |
|       len2 = length ys;
 | |
|       len = if len1 < len2 then len1 else len2;
 | |
|       zipTwoLists' = n:
 | |
|         if n != len then
 | |
|           [ { first = elemAt xs n; second = elemAt ys n; } ]
 | |
|           ++ zipTwoLists' (n + 1)
 | |
|         else [];
 | |
|     in zipTwoLists' 0;
 | |
| 
 | |
| 
 | |
|   deepSeqList = xs: y: if any (x: deepSeq x false) xs then y else y;
 | |
| 
 | |
|   crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
 | |
| 
 | |
|   # Remove duplicate elements from the list
 | |
|   unique = list:
 | |
|     if list == [] then
 | |
|       []
 | |
|     else
 | |
|       let
 | |
|         x = head list;
 | |
|         xs = unique (drop 1 list);
 | |
|       in [x] ++ remove x xs;
 | |
| 
 | |
| }
 | 
