|
|
|
@ -1,4 +1,5 @@ |
|
|
|
|
# General list operations. |
|
|
|
|
|
|
|
|
|
{ lib }: |
|
|
|
|
with lib.trivial; |
|
|
|
|
let |
|
|
|
@ -8,21 +9,23 @@ 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]' |
|
|
|
|
/* 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 |
|
|
|
|
/* “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" |
|
|
|
@ -42,16 +45,15 @@ rec { |
|
|
|
|
else op (elemAt list n) (fold' (n + 1)); |
|
|
|
|
in fold' 0; |
|
|
|
|
|
|
|
|
|
/* `fold' is an alias of `foldr' for historic reasons */ |
|
|
|
|
/* `fold` is an alias of `foldr` for historic reasons */ |
|
|
|
|
# FIXME(Profpatsch): deprecate? |
|
|
|
|
fold = foldr; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* “left fold”, like `foldr', but from the left: |
|
|
|
|
/* “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 |
|
|
|
|
Type: foldl :: (b -> a -> b) -> b -> [a] -> b |
|
|
|
|
|
|
|
|
|
Example: |
|
|
|
|
lconcat = foldl (a: b: a + b) "z" |
|
|
|
@ -70,16 +72,20 @@ rec { |
|
|
|
|
else op (foldl' (n - 1)) (elemAt list n); |
|
|
|
|
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.) |
|
|
|
|
|
|
|
|
|
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" ] |
|
|
|
@ -88,6 +94,8 @@ rec { |
|
|
|
|
|
|
|
|
|
/* 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" ] |
|
|
|
@ -96,6 +104,8 @@ rec { |
|
|
|
|
|
|
|
|
|
/* Map and concatenate the result. |
|
|
|
|
|
|
|
|
|
Type: concatMap :: (a -> [b]) -> [a] -> [b] |
|
|
|
|
|
|
|
|
|
Example: |
|
|
|
|
concatMap (x: [x] ++ ["z"]) ["a" "b"] |
|
|
|
|
=> [ "a" "z" "b" "z" ] |
|
|
|
@ -118,15 +128,21 @@ rec { |
|
|
|
|
|
|
|
|
|
/* 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 = e: filter (x: x != e); |
|
|
|
|
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. |
|
|
|
|
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 ] |
|
|
|
@ -136,14 +152,24 @@ rec { |
|
|
|
|
findSingle (x: x == 3) "none" "multiple" [ 1 9 ] |
|
|
|
|
=> "none" |
|
|
|
|
*/ |
|
|
|
|
findSingle = pred: default: multiple: list: |
|
|
|
|
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 returns `default' if no such element exists. |
|
|
|
|
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 ] |
|
|
|
@ -151,12 +177,20 @@ rec { |
|
|
|
|
findFirst (x: x > 9) 7 [ 1 6 4 ] |
|
|
|
|
=> 7 |
|
|
|
|
*/ |
|
|
|
|
findFirst = pred: default: list: |
|
|
|
|
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 iff function `pred' returns true for at least element |
|
|
|
|
of `list'. |
|
|
|
|
/* 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" { } ] |
|
|
|
@ -166,8 +200,10 @@ rec { |
|
|
|
|
*/ |
|
|
|
|
any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false); |
|
|
|
|
|
|
|
|
|
/* Return true iff function `pred' returns true for all elements of |
|
|
|
|
`list'. |
|
|
|
|
/* 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 ] |
|
|
|
@ -177,19 +213,25 @@ rec { |
|
|
|
|
*/ |
|
|
|
|
all = builtins.all or (pred: foldr (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 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 = pred: foldl' (c: x: if pred x then c + 1 else c) 0; |
|
|
|
|
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" ] |
|
|
|
@ -200,13 +242,19 @@ rec { |
|
|
|
|
|
|
|
|
|
/* 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 = cond: elems: if cond then elems else []; |
|
|
|
|
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 |
|
|
|
@ -223,20 +271,28 @@ rec { |
|
|
|
|
|
|
|
|
|
/* 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: last: |
|
|
|
|
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. |
|
|
|
|
/* 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 ] |
|
|
|
@ -252,7 +308,7 @@ rec { |
|
|
|
|
/* 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 |
|
|
|
|
`groupBy'` allows to customise the combining function and initial value |
|
|
|
|
|
|
|
|
|
Example: |
|
|
|
|
groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ] |
|
|
|
@ -268,10 +324,6 @@ rec { |
|
|
|
|
xfce = [ { name = "xfce"; script = "xfce4-session &"; } ]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
groupBy' allows to customise the combining function and initial value |
|
|
|
|
|
|
|
|
|
Example: |
|
|
|
|
groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ] |
|
|
|
|
=> { true = 12; false = 3; } |
|
|
|
|
*/ |
|
|
|
@ -289,17 +341,27 @@ rec { |
|
|
|
|
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 = f: fst: snd: |
|
|
|
|
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"; } ] |
|
|
|
@ -308,6 +370,8 @@ rec { |
|
|
|
|
|
|
|
|
|
/* Reverse the order of the elements of a list. |
|
|
|
|
|
|
|
|
|
Type: reverseList :: [a] -> [a] |
|
|
|
|
|
|
|
|
|
Example: |
|
|
|
|
|
|
|
|
|
reverseList [ "b" "o" "j" ] |
|
|
|
@ -321,8 +385,7 @@ rec { |
|
|
|
|
`before a b == true` means that `b` depends on `a` (there's an |
|
|
|
|
edge from `b` to `a`). |
|
|
|
|
|
|
|
|
|
Examples: |
|
|
|
|
|
|
|
|
|
Example: |
|
|
|
|
listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ] |
|
|
|
|
== { minimal = "/"; # minimal element |
|
|
|
|
visited = [ "/home/user" ]; # seen elements (in reverse order) |
|
|
|
@ -336,7 +399,6 @@ rec { |
|
|
|
|
rest = [ "/home" "other" ]; # everything else |
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
listDfs = stopOnCycles: before: list: |
|
|
|
|
let |
|
|
|
|
dfs' = us: visited: rest: |
|
|
|
@ -361,7 +423,7 @@ rec { |
|
|
|
|
`before a b == true` means that `b` should be after `a` |
|
|
|
|
in the result. |
|
|
|
|
|
|
|
|
|
Examples: |
|
|
|
|
Example: |
|
|
|
|
|
|
|
|
|
toposort hasPrefix [ "/home/user" "other" "/" "/home" ] |
|
|
|
|
== { result = [ "/" "/home" "/home/user" "other" ]; } |
|
|
|
@ -376,7 +438,6 @@ rec { |
|
|
|
|
toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; } |
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
toposort = before: list: |
|
|
|
|
let |
|
|
|
|
dfsthis = listDfs true before list; |
|
|
|
@ -467,26 +528,38 @@ rec { |
|
|
|
|
|
|
|
|
|
/* 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 = count: sublist 0 count; |
|
|
|
|
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 = count: list: sublist count (length list) list; |
|
|
|
|
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`. |
|
|
|
|
|
|
|
|
|
/* 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" ] |
|
|
|
@ -494,7 +567,13 @@ rec { |
|
|
|
|
sublist 1 3 [ ] |
|
|
|
|
=> [ ] |
|
|
|
|
*/ |
|
|
|
|
sublist = start: count: list: |
|
|
|
|
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)) |
|
|
|
@ -504,6 +583,10 @@ rec { |
|
|
|
|
|
|
|
|
|
/* 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 |
|
|
|
@ -512,7 +595,11 @@ rec { |
|
|
|
|
assert lib.assertMsg (list != []) "lists.last: list must not be empty!"; |
|
|
|
|
elemAt list (length list - 1); |
|
|
|
|
|
|
|
|
|
/* Return all elements but the last |
|
|
|
|
/* 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 ] |
|
|
|
@ -523,7 +610,7 @@ rec { |
|
|
|
|
take (length list - 1) list; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* return the image of the cross product of some lists by a function |
|
|
|
|
/* 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]] |
|
|
|
@ -534,8 +621,9 @@ rec { |
|
|
|
|
|
|
|
|
|
/* Remove duplicate elements from the list. O(n^2) complexity. |
|
|
|
|
|
|
|
|
|
Example: |
|
|
|
|
Type: unique :: [a] -> [a] |
|
|
|
|
|
|
|
|
|
Example: |
|
|
|
|
unique [ 3 2 3 4 ] |
|
|
|
|
=> [ 3 2 4 ] |
|
|
|
|
*/ |
|
|
|
|