nixpkgs-suyu/lib/lists.nix

274 lines
7.6 KiB
Nix
Raw Normal View History

# General list operations.
2013-11-12 13:48:19 +01:00
with import ./trivial.nix;
rec {
2013-11-12 13:48:19 +01:00
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.
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);
# Strict version of foldl.
foldl' = builtins.foldl' or foldl;
# Map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] ==
# ["a-1" "b-2"]'. FIXME: why does this start to count at 1?
imap =
if builtins ? genList then
f: list: genList (n: f (n + 1) (elemAt list n)) (length list)
else
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;
2013-11-12 13:48:19 +01:00
# 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 foldl' (x: y: x ++ (flatten y)) [] x
else [x];
2013-11-12 13:48:19 +01:00
# Remove elements equal to 'e' from a list. Useful for buildInputs.
remove = e: filter (x: x != e);
2013-11-12 13:48:19 +01:00
# 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;
2013-11-12 13:48:19 +01:00
# Return true iff function `pred' returns true for at least element
# of `list'.
2015-07-23 19:25:58 +02:00
any = builtins.any or (pred: fold (x: y: if pred x then true else y) false);
# Return true iff function `pred' returns true for all elements of
# `list'.
2015-07-23 19:25:58 +02:00
all = builtins.all or (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: 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').
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.
2013-11-12 13:48:19 +01:00
toList = x: if isList x then x else [x];
# Return a list of integers from `first' up to and including `last'.
range =
if builtins ? genList then
first: last:
if first > last
then []
else genList (n: first + n) (last - first + 1)
else
first: last:
if last < first
then []
else [first] ++ range (first + 1) last;
2013-11-12 13:48:19 +01:00
# 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 =
if builtins ? genList then
f: fst: snd: genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd))
else
f: fst: snd:
let
len = min (length fst) (length snd);
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.
reverseList =
if builtins ? genList then
xs: let l = length xs; in genList (n: elemAt xs (l - n - 1)) l
else
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.
2015-07-28 18:42:04 +02:00
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
2015-07-28 18:42:04 +02:00
else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
# Return the first (at most) N elements of a list.
take =
if builtins ? genList then
count: sublist 0 count
else
count: list:
let
len = length list;
take' = n:
if n == len || n == count
then []
else
[ (elemAt list n) ] ++ take' (n + 1);
in take' 0;
2013-11-12 13:48:19 +01:00
# Remove the first (at most) N elements of a list.
drop =
if builtins ? genList then
count: list: sublist count (length list) list
else
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 a list consisting of at most count elements of list,
# starting at index start.
sublist = start: count: 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);
2013-11-12 13:48:19 +01:00
# Return the last element of a list.
last = list:
assert list != []; elemAt list (length list - 1);
2012-08-14 00:26:19 +02:00
2014-09-16 18:03:46 +02:00
# Return all elements but the last
init = list: assert list != []; take (length list - 1) list;
2013-02-01 06:39:26 +01:00
deepSeqList = xs: y: if any (x: deepSeq x false) xs then y else y;
2013-11-12 13:48:19 +01:00
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
# Remove duplicate elements from the list. O(n^2) complexity.
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.
intersectLists = e: filter (x: elem x e);
# Subtracts list 'e' from another list. O(nm) complexity.
subtractLists = e: filter (x: !(elem x e));
}