 65f50a9bb0
			
		
	
	
		65f50a9bb0
		
	
	
	
	
		
			
			Updates documentation comments with extra information for nixdoc[1] compatibility. [1]: https://github.com/tazjin/nixdoc
		
			
				
	
	
		
			664 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			664 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| # General list operations.
 | |
| 
 | |
| { lib }:
 | |
| with lib.trivial;
 | |
| let
 | |
|   inherit (lib.strings) toInt;
 | |
| in
 | |
| rec {
 | |
| 
 | |
|   inherit (builtins) head tail length isList elemAt concatLists filter elem genList;
 | |
| 
 | |
|   /*  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.
 | |
| 
 | |
|       Type: singleton :: a -> [a]
 | |
| 
 | |
|       Example:
 | |
|         singleton "foo"
 | |
|         => [ "foo" ]
 | |
|   */
 | |
|   singleton = x: [x];
 | |
| 
 | |
|   /* “right fold” a binary function `op` between successive elements of
 | |
|      `list` with `nul' as the starting value, i.e.,
 | |
|      `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`.
 | |
| 
 | |
|      Type: foldr :: (a -> b -> b) -> b -> [a] -> b
 | |
| 
 | |
|      Example:
 | |
|        concat = foldr (a: b: a + b) "z"
 | |
|        concat [ "a" "b" "c" ]
 | |
|        => "abcz"
 | |
|        # different types
 | |
|        strange = foldr (int: str: toString (int + 1) + str) "a"
 | |
|        strange [ 1 2 3 4 ]
 | |
|        => "2345a"
 | |
|   */
 | |
|   foldr = 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;
 | |
| 
 | |
|   /* `fold` is an alias of `foldr` for historic reasons */
 | |
|   # FIXME(Profpatsch): deprecate?
 | |
|   fold = foldr;
 | |
| 
 | |
| 
 | |
|   /* “left fold”, like `foldr`, but from the left:
 | |
|      `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`.
 | |
| 
 | |
|      Type: foldl :: (b -> a -> b) -> b -> [a] -> b
 | |
| 
 | |
|      Example:
 | |
|        lconcat = foldl (a: b: a + b) "z"
 | |
|        lconcat [ "a" "b" "c" ]
 | |
|        => "zabc"
 | |
|        # different types
 | |
|        lstrange = foldl (str: int: str + toString (int + 1)) ""
 | |
|        strange [ 1 2 3 4 ]
 | |
|        => "a2345"
 | |
|   */
 | |
|   foldl = op: nul: list:
 | |
|     let
 | |
|       foldl' = n:
 | |
|         if n == -1
 | |
|         then nul
 | |
|         else op (foldl' (n - 1)) (elemAt list n);
 | |
|     in foldl' (length list - 1);
 | |
| 
 | |
|   /* Strict version of `foldl`.
 | |
| 
 | |
|      The difference is that evaluation is forced upon access. Usually used
 | |
|      with small whole results (in contract with lazily-generated list or large
 | |
|      lists where only a part is consumed.)
 | |
| 
 | |
|      Type: foldl' :: (b -> a -> b) -> b -> [a] -> b
 | |
|   */
 | |
|   foldl' = builtins.foldl' or foldl;
 | |
| 
 | |
|   /* Map with index starting from 0
 | |
| 
 | |
|      Type: imap0 :: (int -> a -> b) -> [a] -> [b]
 | |
| 
 | |
|      Example:
 | |
|        imap0 (i: v: "${v}-${toString i}") ["a" "b"]
 | |
|        => [ "a-0" "b-1" ]
 | |
|   */
 | |
|   imap0 = f: list: genList (n: f n (elemAt list n)) (length list);
 | |
| 
 | |
|   /* Map with index starting from 1
 | |
| 
 | |
|      Type: imap1 :: (int -> a -> b) -> [a] -> [b]
 | |
| 
 | |
|      Example:
 | |
|        imap1 (i: v: "${v}-${toString i}") ["a" "b"]
 | |
|        => [ "a-1" "b-2" ]
 | |
|   */
 | |
|   imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
 | |
| 
 | |
|   /* Map and concatenate the result.
 | |
| 
 | |
|      Type: concatMap :: (a -> [b]) -> [a] -> [b]
 | |
| 
 | |
|      Example:
 | |
|        concatMap (x: [x] ++ ["z"]) ["a" "b"]
 | |
|        => [ "a" "z" "b" "z" ]
 | |
|   */
 | |
|   concatMap = builtins.concatMap or (f: list: concatLists (map f list));
 | |
| 
 | |
|   /* Flatten the argument into a single list; that is, nested lists are
 | |
|      spliced into the top-level lists.
 | |
| 
 | |
|      Example:
 | |
|        flatten [1 [2 [3] 4] 5]
 | |
|        => [1 2 3 4 5]
 | |
|        flatten 1
 | |
|        => [1]
 | |
|   */
 | |
|   flatten = x:
 | |
|     if isList x
 | |
|     then concatMap (y: flatten y) x
 | |
|     else [x];
 | |
| 
 | |
|   /* Remove elements equal to 'e' from a list.  Useful for buildInputs.
 | |
| 
 | |
|      Type: remove :: a -> [a] -> [a]
 | |
| 
 | |
|      Example:
 | |
|        remove 3 [ 1 3 4 3 ]
 | |
|        => [ 1 4 ]
 | |
|   */
 | |
|   remove =
 | |
|     # Element to remove from the list
 | |
|     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.
 | |
| 
 | |
|      Type: findSingle :: (a -> bool) -> a -> a -> [a] -> a
 | |
| 
 | |
|      Example:
 | |
|        findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ]
 | |
|        => "multiple"
 | |
|        findSingle (x: x == 3) "none" "multiple" [ 1 3 ]
 | |
|        => 3
 | |
|        findSingle (x: x == 3) "none" "multiple" [ 1 9 ]
 | |
|        => "none"
 | |
|   */
 | |
|   findSingle =
 | |
|     # Predicate
 | |
|     pred:
 | |
|     # Default value to return if element was not found.
 | |
|     default:
 | |
|     # Default value to return if more than one element was found
 | |
|     multiple:
 | |
|     # Input list
 | |
|     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 return `default` if no such element exists.
 | |
| 
 | |
|      Type: findFirst :: (a -> bool) -> a -> [a] -> a
 | |
| 
 | |
|      Example:
 | |
|        findFirst (x: x > 3) 7 [ 1 6 4 ]
 | |
|        => 6
 | |
|        findFirst (x: x > 9) 7 [ 1 6 4 ]
 | |
|        => 7
 | |
|   */
 | |
|   findFirst =
 | |
|     # Predicate
 | |
|     pred:
 | |
|     # Default value to return
 | |
|     default:
 | |
|     # Input list
 | |
|     list:
 | |
|     let found = filter pred list;
 | |
|     in if found == [] then default else head found;
 | |
| 
 | |
|   /* Return true if function `pred` returns true for at least one
 | |
|      element of `list`.
 | |
| 
 | |
|      Type: any :: (a -> bool) -> [a] -> bool
 | |
| 
 | |
|      Example:
 | |
|        any isString [ 1 "a" { } ]
 | |
|        => true
 | |
|        any isString [ 1 { } ]
 | |
|        => false
 | |
|   */
 | |
|   any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false);
 | |
| 
 | |
|   /* Return true if function `pred` returns true for all elements of
 | |
|      `list`.
 | |
| 
 | |
|      Type: all :: (a -> bool) -> [a] -> bool
 | |
| 
 | |
|      Example:
 | |
|        all (x: x < 3) [ 1 2 ]
 | |
|        => true
 | |
|        all (x: x < 3) [ 1 2 3 ]
 | |
|        => false
 | |
|   */
 | |
|   all = builtins.all or (pred: foldr (x: y: if pred x then y else false) true);
 | |
| 
 | |
|   /* Count how many elements of `list` match the supplied predicate
 | |
|      function.
 | |
| 
 | |
|      Type: count :: (a -> bool) -> [a] -> int
 | |
| 
 | |
|      Example:
 | |
|        count (x: x == 3) [ 3 2 3 4 6 ]
 | |
|        => 2
 | |
|   */
 | |
|   count =
 | |
|     # Predicate
 | |
|     pred: foldl' (c: x: 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').
 | |
| 
 | |
|      Type: optional :: bool -> a -> [a]
 | |
| 
 | |
|      Example:
 | |
|        optional true "foo"
 | |
|        => [ "foo" ]
 | |
|        optional false "foo"
 | |
|        => [ ]
 | |
|   */
 | |
|   optional = cond: elem: if cond then [elem] else [];
 | |
| 
 | |
|   /* Return a list or an empty list, depending on a boolean value.
 | |
| 
 | |
|      Type: optionals :: bool -> [a] -> [a]
 | |
| 
 | |
|      Example:
 | |
|        optionals true [ 2 3 ]
 | |
|        => [ 2 3 ]
 | |
|        optionals false [ 2 3 ]
 | |
|        => [ ]
 | |
|   */
 | |
|   optionals =
 | |
|     # Condition
 | |
|     cond:
 | |
|     # List to return if condition is true
 | |
|     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.
 | |
| 
 | |
|      Example:
 | |
|        toList [ 1 2 ]
 | |
|        => [ 1 2 ]
 | |
|        toList "hi"
 | |
|        => [ "hi "]
 | |
|   */
 | |
|   toList = x: if isList x then x else [x];
 | |
| 
 | |
|   /* Return a list of integers from `first' up to and including `last'.
 | |
| 
 | |
|      Type: range :: int -> int -> [int]
 | |
| 
 | |
|      Example:
 | |
|        range 2 4
 | |
|        => [ 2 3 4 ]
 | |
|        range 3 2
 | |
|        => [ ]
 | |
|   */
 | |
|   range =
 | |
|     # First integer in the range
 | |
|     first:
 | |
|     # Last integer in the range
 | |
|     last:
 | |
|     if first > last then
 | |
|       []
 | |
|     else
 | |
|       genList (n: first + n) (last - first + 1);
 | |
| 
 | |
|   /* Splits the elements of a list in two lists, `right` and
 | |
|      `wrong`, depending on the evaluation of a predicate.
 | |
| 
 | |
|      Type: (a -> bool) -> [a] -> { right :: [a], wrong :: [a] }
 | |
| 
 | |
|      Example:
 | |
|        partition (x: x > 2) [ 5 1 2 3 4 ]
 | |
|        => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
 | |
|   */
 | |
|   partition = builtins.partition or (pred:
 | |
|     foldr (h: t:
 | |
|       if pred h
 | |
|       then { right = [h] ++ t.right; wrong = t.wrong; }
 | |
|       else { right = t.right; wrong = [h] ++ t.wrong; }
 | |
|     ) { right = []; wrong = []; });
 | |
| 
 | |
|   /* Splits the elements of a list into many lists, using the return value of a predicate.
 | |
|      Predicate should return a string which becomes keys of attrset `groupBy' returns.
 | |
| 
 | |
|      `groupBy'` allows to customise the combining function and initial value
 | |
| 
 | |
|      Example:
 | |
|        groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ]
 | |
|        => { true = [ 5 3 4 ]; false = [ 1 2 ]; }
 | |
|        groupBy (x: x.name) [ {name = "icewm"; script = "icewm &";}
 | |
|                              {name = "xfce";  script = "xfce4-session &";}
 | |
|                              {name = "icewm"; script = "icewmbg &";}
 | |
|                              {name = "mate";  script = "gnome-session &";}
 | |
|                            ]
 | |
|        => { icewm = [ { name = "icewm"; script = "icewm &"; }
 | |
|                       { name = "icewm"; script = "icewmbg &"; } ];
 | |
|             mate  = [ { name = "mate";  script = "gnome-session &"; } ];
 | |
|             xfce  = [ { name = "xfce";  script = "xfce4-session &"; } ];
 | |
|           }
 | |
| 
 | |
|        groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ]
 | |
|        => { true = 12; false = 3; }
 | |
|   */
 | |
|   groupBy' = op: nul: pred: lst:
 | |
|     foldl' (r: e:
 | |
|               let
 | |
|                 key = pred e;
 | |
|               in
 | |
|                 r // { ${key} = op (r.${key} or nul) e; }
 | |
|            ) {} lst;
 | |
| 
 | |
|   groupBy = groupBy' (sum: e: sum ++ [e]) [];
 | |
| 
 | |
|   /* Merges two lists of the same size together. If the sizes aren't the same
 | |
|      the merging stops at the shortest. How both lists are merged is defined
 | |
|      by the first argument.
 | |
| 
 | |
|      Type: zipListsWith :: (a -> b -> c) -> [a] -> [b] -> [c]
 | |
| 
 | |
|      Example:
 | |
|        zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"]
 | |
|        => ["he" "lo"]
 | |
|   */
 | |
|   zipListsWith =
 | |
|     # Function to zip elements of both lists
 | |
|     f:
 | |
|     # First list
 | |
|     fst:
 | |
|     # Second list
 | |
|     snd:
 | |
|     genList
 | |
|       (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
 | |
| 
 | |
|   /* Merges two lists of the same size together. If the sizes aren't the same
 | |
|      the merging stops at the shortest.
 | |
| 
 | |
|      Type: zipLists :: [a] -> [b] -> [{ fst :: a, snd :: b}]
 | |
| 
 | |
|      Example:
 | |
|        zipLists [ 1 2 ] [ "a" "b" ]
 | |
|        => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ]
 | |
|   */
 | |
|   zipLists = zipListsWith (fst: snd: { inherit fst snd; });
 | |
| 
 | |
|   /* Reverse the order of the elements of a list.
 | |
| 
 | |
|      Type: reverseList :: [a] -> [a]
 | |
| 
 | |
|      Example:
 | |
| 
 | |
|        reverseList [ "b" "o" "j" ]
 | |
|        => [ "j" "o" "b" ]
 | |
|   */
 | |
|   reverseList = xs:
 | |
|     let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
 | |
| 
 | |
|   /* Depth-First Search (DFS) for lists `list != []`.
 | |
| 
 | |
|      `before a b == true` means that `b` depends on `a` (there's an
 | |
|      edge from `b` to `a`).
 | |
| 
 | |
|      Example:
 | |
|          listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ]
 | |
|            == { minimal = "/";                  # minimal element
 | |
|                 visited = [ "/home/user" ];     # seen elements (in reverse order)
 | |
|                 rest    = [ "/home" "other" ];  # everything else
 | |
|               }
 | |
| 
 | |
|          listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
 | |
|            == { cycle   = "/";                  # cycle encountered at this element
 | |
|                 loops   = [ "/" ];              # and continues to these elements
 | |
|                 visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order)
 | |
|                 rest    = [ "/home" "other" ];  # everything else
 | |
| 
 | |
|    */
 | |
|   listDfs = stopOnCycles: before: list:
 | |
|     let
 | |
|       dfs' = us: visited: rest:
 | |
|         let
 | |
|           c = filter (x: before x us) visited;
 | |
|           b = partition (x: before x us) rest;
 | |
|         in if stopOnCycles && (length c > 0)
 | |
|            then { cycle = us; loops = c; inherit visited rest; }
 | |
|            else if length b.right == 0
 | |
|                 then # nothing is before us
 | |
|                      { minimal = us; inherit visited rest; }
 | |
|                 else # grab the first one before us and continue
 | |
|                      dfs' (head b.right)
 | |
|                           ([ us ] ++ visited)
 | |
|                           (tail b.right ++ b.wrong);
 | |
|     in dfs' (head list) [] (tail list);
 | |
| 
 | |
|   /* Sort a list based on a partial ordering using DFS. This
 | |
|      implementation is O(N^2), if your ordering is linear, use `sort`
 | |
|      instead.
 | |
| 
 | |
|      `before a b == true` means that `b` should be after `a`
 | |
|      in the result.
 | |
| 
 | |
|      Example:
 | |
| 
 | |
|          toposort hasPrefix [ "/home/user" "other" "/" "/home" ]
 | |
|            == { result = [ "/" "/home" "/home/user" "other" ]; }
 | |
| 
 | |
|          toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
 | |
|            == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle
 | |
|                 loops = [ "/" ]; }                # loops back to these elements
 | |
| 
 | |
|          toposort hasPrefix [ "other" "/home/user" "/home" "/" ]
 | |
|            == { result = [ "other" "/" "/home" "/home/user" ]; }
 | |
| 
 | |
|          toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; }
 | |
| 
 | |
|    */
 | |
|   toposort = before: list:
 | |
|     let
 | |
|       dfsthis = listDfs true before list;
 | |
|       toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
 | |
|     in
 | |
|       if length list < 2
 | |
|       then # finish
 | |
|            { result =  list; }
 | |
|       else if dfsthis ? "cycle"
 | |
|            then # there's a cycle, starting from the current vertex, return it
 | |
|                 { cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
 | |
|                   inherit (dfsthis) loops; }
 | |
|            else if toporest ? "cycle"
 | |
|                 then # there's a cycle somewhere else in the graph, return it
 | |
|                      toporest
 | |
|                 # Slow, but short. Can be made a bit faster with an explicit stack.
 | |
|                 else # there are no cycles
 | |
|                      { result = [ dfsthis.minimal ] ++ toporest.result; };
 | |
| 
 | |
|   /* 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.
 | |
| 
 | |
|      Example:
 | |
|        sort (a: b: a < b) [ 5 3 7 ]
 | |
|        => [ 3 5 7 ]
 | |
|   */
 | |
|   sort = builtins.sort or (
 | |
|     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));
 | |
| 
 | |
|   /* Compare two lists element-by-element.
 | |
| 
 | |
|      Example:
 | |
|        compareLists compare [] []
 | |
|        => 0
 | |
|        compareLists compare [] [ "a" ]
 | |
|        => -1
 | |
|        compareLists compare [ "a" ] []
 | |
|        => 1
 | |
|        compareLists compare [ "a" "b" ] [ "a" "c" ]
 | |
|        => 1
 | |
|   */
 | |
|   compareLists = cmp: a: b:
 | |
|     if a == []
 | |
|     then if b == []
 | |
|          then 0
 | |
|          else -1
 | |
|     else if b == []
 | |
|          then 1
 | |
|          else let rel = cmp (head a) (head b); in
 | |
|               if rel == 0
 | |
|               then compareLists cmp (tail a) (tail b)
 | |
|               else rel;
 | |
| 
 | |
|   /* Sort list using "Natural sorting".
 | |
|      Numeric portions of strings are sorted in numeric order.
 | |
| 
 | |
|      Example:
 | |
|        naturalSort ["disk11" "disk8" "disk100" "disk9"]
 | |
|        => ["disk8" "disk9" "disk11" "disk100"]
 | |
|        naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"]
 | |
|        => ["10.5.16.62" "10.46.133.149" "10.54.16.25"]
 | |
|        naturalSort ["v0.2" "v0.15" "v0.0.9"]
 | |
|        => [ "v0.0.9" "v0.2" "v0.15" ]
 | |
|   */
 | |
|   naturalSort = lst:
 | |
|     let
 | |
|       vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s);
 | |
|       prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits
 | |
|       less = a: b: (compareLists compare (head a) (head b)) < 0;
 | |
|     in
 | |
|       map (x: elemAt x 1) (sort less prepared);
 | |
| 
 | |
|   /* Return the first (at most) N elements of a list.
 | |
| 
 | |
|      Type: take :: int -> [a] -> [a]
 | |
| 
 | |
|      Example:
 | |
|        take 2 [ "a" "b" "c" "d" ]
 | |
|        => [ "a" "b" ]
 | |
|        take 2 [ ]
 | |
|        => [ ]
 | |
|   */
 | |
|   take =
 | |
|     # Number of elements to take
 | |
|     count: sublist 0 count;
 | |
| 
 | |
|   /* Remove the first (at most) N elements of a list.
 | |
| 
 | |
|      Type: drop :: int -> [a] -> [a]
 | |
| 
 | |
|      Example:
 | |
|        drop 2 [ "a" "b" "c" "d" ]
 | |
|        => [ "c" "d" ]
 | |
|        drop 2 [ ]
 | |
|        => [ ]
 | |
|   */
 | |
|   drop =
 | |
|     # Number of elements to drop
 | |
|     count:
 | |
|     # Input list
 | |
|     list: sublist count (length list) list;
 | |
| 
 | |
|   /* Return a list consisting of at most `count` elements of `list`,
 | |
|      starting at index `start`.
 | |
| 
 | |
|      Type: sublist :: int -> int -> [a] -> [a]
 | |
| 
 | |
|      Example:
 | |
|        sublist 1 3 [ "a" "b" "c" "d" "e" ]
 | |
|        => [ "b" "c" "d" ]
 | |
|        sublist 1 3 [ ]
 | |
|        => [ ]
 | |
|   */
 | |
|   sublist =
 | |
|     # Index at which to start the sublist
 | |
|     start:
 | |
|     # Number of elements to take
 | |
|     count:
 | |
|     # Input list
 | |
|     list:
 | |
|     let len = length list; in
 | |
|     genList
 | |
|       (n: elemAt list (n + start))
 | |
|       (if start >= len then 0
 | |
|        else if start + count > len then len - start
 | |
|        else count);
 | |
| 
 | |
|   /* Return the last element of a list.
 | |
| 
 | |
|      This function throws an error if the list is empty.
 | |
| 
 | |
|      Type: last :: [a] -> a
 | |
| 
 | |
|      Example:
 | |
|        last [ 1 2 3 ]
 | |
|        => 3
 | |
|   */
 | |
|   last = list:
 | |
|     assert lib.assertMsg (list != []) "lists.last: list must not be empty!";
 | |
|     elemAt list (length list - 1);
 | |
| 
 | |
|   /* Return all elements but the last.
 | |
| 
 | |
|      This function throws an error if the list is empty.
 | |
| 
 | |
|      Type: init :: [a] -> [a]
 | |
| 
 | |
|      Example:
 | |
|        init [ 1 2 3 ]
 | |
|        => [ 1 2 ]
 | |
|   */
 | |
|   init = list:
 | |
|     assert lib.assertMsg (list != []) "lists.init: list must not be empty!";
 | |
|     take (length list - 1) list;
 | |
| 
 | |
| 
 | |
|   /* Return the image of the cross product of some lists by a function.
 | |
| 
 | |
|     Example:
 | |
|       crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
 | |
|       => [ "13" "14" "23" "24" ]
 | |
|   */
 | |
|   crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
 | |
| 
 | |
| 
 | |
|   /* Remove duplicate elements from the list. O(n^2) complexity.
 | |
| 
 | |
|      Type: unique :: [a] -> [a]
 | |
| 
 | |
|      Example:
 | |
|        unique [ 3 2 3 4 ]
 | |
|        => [ 3 2 4 ]
 | |
|    */
 | |
|   unique = list:
 | |
|     if list == [] then
 | |
|       []
 | |
|     else
 | |
|       let
 | |
|         x = head list;
 | |
|         xs = unique (drop 1 list);
 | |
|       in [x] ++ remove x xs;
 | |
| 
 | |
|   /* Intersects list 'e' and another list. O(nm) complexity.
 | |
| 
 | |
|      Example:
 | |
|        intersectLists [ 1 2 3 ] [ 6 3 2 ]
 | |
|        => [ 3 2 ]
 | |
|   */
 | |
|   intersectLists = e: filter (x: elem x e);
 | |
| 
 | |
|   /* Subtracts list 'e' from another list. O(nm) complexity.
 | |
| 
 | |
|      Example:
 | |
|        subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ]
 | |
|        => [ 1 4 5 ]
 | |
|   */
 | |
|   subtractLists = e: filter (x: !(elem x e));
 | |
| 
 | |
|   /* Test if two lists have no common element.
 | |
|      It should be slightly more efficient than (intersectLists a b == [])
 | |
|   */
 | |
|   mutuallyExclusive = a: b:
 | |
|     (builtins.length a) == 0 ||
 | |
|     (!(builtins.elem (builtins.head a) b) &&
 | |
|      mutuallyExclusive (builtins.tail a) b);
 | |
| 
 | |
| }
 |