lib/lists: document all functions
This commit is contained in:
parent
523e328318
commit
c71e2d4235
268
lib/lists.nix
268
lib/lists.nix
@ -6,17 +6,26 @@ rec {
|
|||||||
|
|
||||||
inherit (builtins) head tail length isList elemAt concatLists filter elem genList;
|
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.
|
||||||
|
|
||||||
# Create a list consisting of a single element. `singleton x' is
|
Example:
|
||||||
# sometimes more convenient with respect to indentation than `[x]'
|
singleton "foo"
|
||||||
# when x spans multiple lines.
|
=> [ "foo" ]
|
||||||
|
*/
|
||||||
singleton = x: [x];
|
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" a binary function `op' between successive elements of
|
Example:
|
||||||
# `list' with `nul' as the starting value, i.e., `fold op nul [x_1
|
concat = fold (a: b: a + b) "z"
|
||||||
# x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. (This is
|
concat [ "a" "b" "c" ]
|
||||||
# Haskell's foldr).
|
=> "abcnul"
|
||||||
|
*/
|
||||||
fold = op: nul: list:
|
fold = op: nul: list:
|
||||||
let
|
let
|
||||||
len = length list;
|
len = length list;
|
||||||
@ -26,8 +35,14 @@ rec {
|
|||||||
else op (elemAt list n) (fold' (n + 1));
|
else op (elemAt list n) (fold' (n + 1));
|
||||||
in fold' 0;
|
in fold' 0;
|
||||||
|
|
||||||
# Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul
|
/* Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul
|
||||||
# x_1) x_2) ... x_n)'.
|
x_1) x_2) ... x_n)'.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
lconcat = foldl (a: b: a + b) "z"
|
||||||
|
lconcat [ "a" "b" "c" ]
|
||||||
|
=> "zabc"
|
||||||
|
*/
|
||||||
foldl = op: nul: list:
|
foldl = op: nul: list:
|
||||||
let
|
let
|
||||||
len = length list;
|
len = length list;
|
||||||
@ -37,13 +52,22 @@ rec {
|
|||||||
else op (foldl' (n - 1)) (elemAt list n);
|
else op (foldl' (n - 1)) (elemAt list n);
|
||||||
in foldl' (length list - 1);
|
in foldl' (length list - 1);
|
||||||
|
|
||||||
|
/* Strict version of foldl.
|
||||||
|
|
||||||
# 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.)
|
||||||
|
*/
|
||||||
foldl' = builtins.foldl' or foldl;
|
foldl' = builtins.foldl' or foldl;
|
||||||
|
|
||||||
|
/* Map with index
|
||||||
|
|
||||||
# Map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] ==
|
FIXME(zimbatm): why does this start to count at 1?
|
||||||
# ["a-1" "b-2"]'. FIXME: why does this start to count at 1?
|
|
||||||
|
Example:
|
||||||
|
imap (i: v: "${v}-${toString i}") ["a" "b"]
|
||||||
|
=> [ "a-1" "b-2" ]
|
||||||
|
*/
|
||||||
imap =
|
imap =
|
||||||
if builtins ? genList then
|
if builtins ? genList then
|
||||||
f: list: genList (n: f (n + 1) (elemAt list n)) (length list)
|
f: list: genList (n: f (n + 1) (elemAt list n)) (length list)
|
||||||
@ -57,73 +81,141 @@ rec {
|
|||||||
else [ (f (n + 1) (elemAt list n)) ] ++ imap' (n + 1);
|
else [ (f (n + 1) (elemAt list n)) ] ++ imap' (n + 1);
|
||||||
in imap' 0;
|
in imap' 0;
|
||||||
|
|
||||||
|
/* Map and concatenate the result.
|
||||||
|
|
||||||
# Map and concatenate the result.
|
Example:
|
||||||
|
concatMap (x: [x] ++ ["z"]) ["a" "b"]
|
||||||
|
=> [ "a" "z" "b" "z" ]
|
||||||
|
*/
|
||||||
concatMap = f: list: concatLists (map f list);
|
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.
|
||||||
|
|
||||||
# Flatten the argument into a single list; that is, nested lists are
|
Example:
|
||||||
# spliced into the top-level lists. E.g., `flatten [1 [2 [3] 4] 5]
|
flatten [1 [2 [3] 4] 5]
|
||||||
# == [1 2 3 4 5]' and `flatten 1 == [1]'.
|
=> [1 2 3 4 5]
|
||||||
|
flatten 1
|
||||||
|
=> [1]
|
||||||
|
*/
|
||||||
flatten = x:
|
flatten = x:
|
||||||
if isList x
|
if isList x
|
||||||
then foldl' (x: y: x ++ (flatten y)) [] x
|
then foldl' (x: y: x ++ (flatten y)) [] x
|
||||||
else [x];
|
else [x];
|
||||||
|
|
||||||
|
/* Remove elements equal to 'e' from a list. Useful for buildInputs.
|
||||||
|
|
||||||
# Remove elements equal to 'e' from a list. Useful for buildInputs.
|
Example:
|
||||||
|
remove 3 [ 1 3 4 3 ]
|
||||||
|
=> [ 1 4 ]
|
||||||
|
*/
|
||||||
remove = e: filter (x: x != e);
|
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.
|
||||||
|
|
||||||
# Find the sole element in the list matching the specified
|
Example:
|
||||||
# predicate, returns `default' if no such element exists, or
|
findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ]
|
||||||
# `multiple' if there are multiple matching elements.
|
=> "multiple"
|
||||||
|
findSingle (x: x == 3) "none" "multiple" [ 1 3 ]
|
||||||
|
=> 3
|
||||||
|
findSingle (x: x == 3) "none" "multiple" [ 1 9 ]
|
||||||
|
=> "none"
|
||||||
|
*/
|
||||||
findSingle = pred: default: multiple: list:
|
findSingle = pred: default: multiple: list:
|
||||||
let found = filter pred list; len = length found;
|
let found = filter pred list; len = length found;
|
||||||
in if len == 0 then default
|
in if len == 0 then default
|
||||||
else if len != 1 then multiple
|
else if len != 1 then multiple
|
||||||
else head found;
|
else head found;
|
||||||
|
|
||||||
|
/* Find the first element in the list matching the specified
|
||||||
|
predicate or returns `default' if no such element exists.
|
||||||
|
|
||||||
# Find the first element in the list matching the specified
|
Example:
|
||||||
# predicate or returns `default' if no such element exists.
|
findFirst (x: x > 3) 7 [ 1 6 4 ]
|
||||||
|
=> 6
|
||||||
|
findFirst (x: x > 9) 7 [ 1 6 4 ]
|
||||||
|
=> 7
|
||||||
|
*/
|
||||||
findFirst = pred: default: list:
|
findFirst = pred: default: list:
|
||||||
let found = filter pred list;
|
let found = filter pred list;
|
||||||
in if found == [] then default else head found;
|
in if found == [] then default else head found;
|
||||||
|
|
||||||
|
/* Return true iff function `pred' returns true for at least element
|
||||||
|
of `list'.
|
||||||
|
|
||||||
# Return true iff function `pred' returns true for at least element
|
Example:
|
||||||
# of `list'.
|
any isString [ 1 "a" { } ]
|
||||||
|
=> true
|
||||||
|
any isString [ 1 { } ]
|
||||||
|
=> false
|
||||||
|
*/
|
||||||
any = builtins.any or (pred: fold (x: y: if pred x then true else y) false);
|
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'.
|
||||||
|
|
||||||
# Return true iff function `pred' returns true for all elements of
|
Example:
|
||||||
# `list'.
|
all (x: x < 3) [ 1 2 ]
|
||||||
|
=> true
|
||||||
|
all (x: x < 3) [ 1 2 3 ]
|
||||||
|
=> false
|
||||||
|
*/
|
||||||
all = builtins.all or (pred: fold (x: y: if pred x then y else false) true);
|
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 how many times function `pred' returns true for the elements
|
Example:
|
||||||
# of `list'.
|
count (x: x == 3) [ 3 2 3 4 6 ]
|
||||||
|
=> 2
|
||||||
|
*/
|
||||||
count = pred: foldl' (c: x: if pred x then c + 1 else c) 0;
|
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').
|
||||||
|
|
||||||
# Return a singleton list or an empty list, depending on a boolean
|
Example:
|
||||||
# value. Useful when building lists with optional elements
|
optional true "foo"
|
||||||
# (e.g. `++ optional (system == "i686-linux") flashplayer').
|
=> [ "foo" ]
|
||||||
|
optional false "foo"
|
||||||
|
=> [ ]
|
||||||
|
*/
|
||||||
optional = cond: elem: if cond then [elem] else [];
|
optional = cond: elem: if cond then [elem] else [];
|
||||||
|
|
||||||
|
/* Return a list or an empty list, dependening on a boolean value.
|
||||||
|
|
||||||
# Return a list or an empty list, dependening on a boolean value.
|
Example:
|
||||||
|
optionals true [ 2 3 ]
|
||||||
|
=> [ 2 3 ]
|
||||||
|
optionals false [ 2 3 ]
|
||||||
|
=> [ ]
|
||||||
|
*/
|
||||||
optionals = cond: elems: if cond then elems else [];
|
optionals = cond: elems: if cond then elems else [];
|
||||||
|
|
||||||
|
|
||||||
# If argument is a list, return it; else, wrap it in a singleton
|
/* If argument is a list, return it; else, wrap it in a singleton
|
||||||
# list. If you're using this, you should almost certainly
|
list. If you're using this, you should almost certainly
|
||||||
# reconsider if there isn't a more "well-typed" approach.
|
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];
|
toList = x: if isList x then x else [x];
|
||||||
|
|
||||||
|
/* Return a list of integers from `first' up to and including `last'.
|
||||||
|
|
||||||
# Return a list of integers from `first' up to and including `last'.
|
Example:
|
||||||
|
range 2 4
|
||||||
|
=> [ 2 3 4 ]
|
||||||
|
range 3 2
|
||||||
|
=> [ ]
|
||||||
|
*/
|
||||||
range =
|
range =
|
||||||
if builtins ? genList then
|
if builtins ? genList then
|
||||||
first: last:
|
first: last:
|
||||||
@ -136,9 +228,13 @@ rec {
|
|||||||
then []
|
then []
|
||||||
else [first] ++ range (first + 1) last;
|
else [first] ++ range (first + 1) last;
|
||||||
|
|
||||||
|
/* Splits the elements of a list in two lists, `right' and
|
||||||
|
`wrong', depending on the evaluation of a predicate.
|
||||||
|
|
||||||
# Partition the elements of a list in two lists, `right' and
|
Example:
|
||||||
# `wrong', depending on the evaluation of a predicate.
|
partition (x: x > 2) [ 5 1 2 3 4 ]
|
||||||
|
=> { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
|
||||||
|
*/
|
||||||
partition = pred:
|
partition = pred:
|
||||||
fold (h: t:
|
fold (h: t:
|
||||||
if pred h
|
if pred h
|
||||||
@ -146,7 +242,14 @@ rec {
|
|||||||
else { right = t.right; wrong = [h] ++ t.wrong; }
|
else { right = t.right; wrong = [h] ++ t.wrong; }
|
||||||
) { right = []; wrong = []; };
|
) { right = []; wrong = []; };
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"]
|
||||||
|
=> ["he" "lo"]
|
||||||
|
*/
|
||||||
zipListsWith =
|
zipListsWith =
|
||||||
if builtins ? genList then
|
if builtins ? genList then
|
||||||
f: fst: snd: genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd))
|
f: fst: snd: genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd))
|
||||||
@ -161,21 +264,37 @@ rec {
|
|||||||
else [];
|
else [];
|
||||||
in zipListsWith' 0;
|
in zipListsWith' 0;
|
||||||
|
|
||||||
|
/* Merges two lists of the same size together. If the sizes aren't the same
|
||||||
|
the merging stops at the shortest.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
zipLists [ 1 2 ] [ "a" "b" ]
|
||||||
|
=> [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ]
|
||||||
|
*/
|
||||||
zipLists = zipListsWith (fst: snd: { inherit fst snd; });
|
zipLists = zipListsWith (fst: snd: { inherit fst snd; });
|
||||||
|
|
||||||
|
/* Reverse the order of the elements of a list.
|
||||||
|
|
||||||
# Reverse the order of the elements of a list.
|
Example:
|
||||||
|
|
||||||
|
reverseList [ "b" "o" "j" ]
|
||||||
|
=> [ "j" "o" "b" ]
|
||||||
|
*/
|
||||||
reverseList =
|
reverseList =
|
||||||
if builtins ? genList then
|
if builtins ? genList then
|
||||||
xs: let l = length xs; in genList (n: elemAt xs (l - n - 1)) l
|
xs: let l = length xs; in genList (n: elemAt xs (l - n - 1)) l
|
||||||
else
|
else
|
||||||
fold (e: acc: acc ++ [ e ]) [];
|
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 a list based on a comparator function which compares two
|
Example:
|
||||||
# elements and returns true if the first argument is strictly below
|
sort (a: b: a < b) [ 5 3 7 ]
|
||||||
# the second argument. The returned list is sorted in an increasing
|
=> [ 3 5 7 ]
|
||||||
# order. The implementation does a quick-sort.
|
*/
|
||||||
sort = builtins.sort or (
|
sort = builtins.sort or (
|
||||||
strictLess: list:
|
strictLess: list:
|
||||||
let
|
let
|
||||||
@ -193,8 +312,14 @@ rec {
|
|||||||
if len < 2 then list
|
if len < 2 then list
|
||||||
else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
|
else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
|
||||||
|
|
||||||
|
/* Return the first (at most) N elements of a list.
|
||||||
|
|
||||||
# Return the first (at most) N elements of a list.
|
Example:
|
||||||
|
take 2 [ "a" "b" "c" "d" ]
|
||||||
|
=> [ "a" "b" ]
|
||||||
|
take 2 [ ]
|
||||||
|
=> [ ]
|
||||||
|
*/
|
||||||
take =
|
take =
|
||||||
if builtins ? genList then
|
if builtins ? genList then
|
||||||
count: sublist 0 count
|
count: sublist 0 count
|
||||||
@ -209,8 +334,14 @@ rec {
|
|||||||
[ (elemAt list n) ] ++ take' (n + 1);
|
[ (elemAt list n) ] ++ take' (n + 1);
|
||||||
in take' 0;
|
in take' 0;
|
||||||
|
|
||||||
|
/* Remove the first (at most) N elements of a list.
|
||||||
|
|
||||||
# Remove the first (at most) N elements of a list.
|
Example:
|
||||||
|
drop 2 [ "a" "b" "c" "d" ]
|
||||||
|
=> [ "c" "d" ]
|
||||||
|
drop 2 [ ]
|
||||||
|
=> [ ]
|
||||||
|
*/
|
||||||
drop =
|
drop =
|
||||||
if builtins ? genList then
|
if builtins ? genList then
|
||||||
count: list: sublist count (length list) list
|
count: list: sublist count (length list) list
|
||||||
@ -225,9 +356,15 @@ rec {
|
|||||||
drop' (n - 1) ++ [ (elemAt list n) ];
|
drop' (n - 1) ++ [ (elemAt list n) ];
|
||||||
in drop' (len - 1);
|
in drop' (len - 1);
|
||||||
|
|
||||||
|
/* Return a list consisting of at most ‘count’ elements of ‘list’,
|
||||||
|
starting at index ‘start’.
|
||||||
|
|
||||||
# Return a list consisting of at most ‘count’ elements of ‘list’,
|
Example:
|
||||||
# starting at index ‘start’.
|
sublist 1 3 [ "a" "b" "c" "d" "e" ]
|
||||||
|
=> [ "b" "c" "d" ]
|
||||||
|
sublist 1 3 [ ]
|
||||||
|
=> [ ]
|
||||||
|
*/
|
||||||
sublist = start: count: list:
|
sublist = start: count: list:
|
||||||
let len = length list; in
|
let len = length list; in
|
||||||
genList
|
genList
|
||||||
@ -236,20 +373,36 @@ rec {
|
|||||||
else if start + count > len then len - start
|
else if start + count > len then len - start
|
||||||
else count);
|
else count);
|
||||||
|
|
||||||
|
/* Return the last element of a list.
|
||||||
|
|
||||||
# Return the last element of a list.
|
Example:
|
||||||
|
last [ 1 2 3 ]
|
||||||
|
=> 3
|
||||||
|
*/
|
||||||
last = list:
|
last = list:
|
||||||
assert list != []; elemAt list (length list - 1);
|
assert list != []; elemAt list (length list - 1);
|
||||||
|
|
||||||
|
/* Return all elements but the last
|
||||||
|
|
||||||
# Return all elements but the last
|
Example:
|
||||||
|
init [ 1 2 3 ]
|
||||||
|
=> [ 1 2 ]
|
||||||
|
*/
|
||||||
init = list: assert list != []; take (length list - 1) list;
|
init = list: assert list != []; take (length list - 1) list;
|
||||||
|
|
||||||
|
|
||||||
|
/* FIXME(zimbatm) Not used anywhere
|
||||||
|
*/
|
||||||
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
|
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
|
||||||
|
|
||||||
|
|
||||||
# Remove duplicate elements from the list. O(n^2) complexity.
|
/* Remove duplicate elements from the list. O(n^2) complexity.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
unique [ 3 2 3 4 ]
|
||||||
|
=> [ 3 2 4 ]
|
||||||
|
*/
|
||||||
unique = list:
|
unique = list:
|
||||||
if list == [] then
|
if list == [] then
|
||||||
[]
|
[]
|
||||||
@ -259,15 +412,24 @@ rec {
|
|||||||
xs = unique (drop 1 list);
|
xs = unique (drop 1 list);
|
||||||
in [x] ++ remove x xs;
|
in [x] ++ remove x xs;
|
||||||
|
|
||||||
|
/* Intersects list 'e' and another list. O(nm) complexity.
|
||||||
|
|
||||||
# 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);
|
intersectLists = e: filter (x: elem x e);
|
||||||
|
|
||||||
|
/* Subtracts list 'e' from another list. O(nm) complexity.
|
||||||
|
|
||||||
# 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));
|
subtractLists = e: filter (x: !(elem x e));
|
||||||
|
|
||||||
|
/*** deprecated stuff ***/
|
||||||
|
|
||||||
deepSeqList = throw "removed 2016-02-29 because unused and broken";
|
deepSeqList = throw "removed 2016-02-29 because unused and broken";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user