2009-02-09 16:51:03 +00:00
|
|
|
|
# General list operations.
|
2013-10-24 01:02:04 +01:00
|
|
|
|
|
2013-11-12 12:48:19 +00:00
|
|
|
|
with import ./trivial.nix;
|
|
|
|
|
|
2014-10-04 16:02:29 +01:00
|
|
|
|
rec {
|
2013-11-12 12:48:19 +00:00
|
|
|
|
|
2015-07-28 16:31:43 +01:00
|
|
|
|
inherit (builtins) head tail length isList elemAt concatLists filter elem genList;
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* 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.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
singleton "foo"
|
|
|
|
|
=> [ "foo" ]
|
|
|
|
|
*/
|
2009-06-08 23:42:42 +01:00
|
|
|
|
singleton = x: [x];
|
2012-08-14 18:42:43 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* "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).
|
2009-06-08 23:42:42 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
concat = fold (a: b: a + b) "z"
|
|
|
|
|
concat [ "a" "b" "c" ]
|
2016-05-26 11:52:35 +01:00
|
|
|
|
=> "abcz"
|
2016-02-28 23:27:06 +00:00
|
|
|
|
*/
|
2013-07-12 17:36:36 +01:00
|
|
|
|
fold = op: nul: list:
|
|
|
|
|
let
|
|
|
|
|
len = length list;
|
|
|
|
|
fold' = n:
|
|
|
|
|
if n == len
|
2012-08-13 19:19:50 +01:00
|
|
|
|
then nul
|
2014-10-04 16:02:29 +01:00
|
|
|
|
else op (elemAt list n) (fold' (n + 1));
|
2013-07-12 17:36:36 +01:00
|
|
|
|
in fold' 0;
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul
|
|
|
|
|
x_1) x_2) ... x_n)'.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
lconcat = foldl (a: b: a + b) "z"
|
|
|
|
|
lconcat [ "a" "b" "c" ]
|
|
|
|
|
=> "zabc"
|
|
|
|
|
*/
|
2013-07-12 17:36:36 +01:00
|
|
|
|
foldl = op: nul: list:
|
|
|
|
|
let
|
|
|
|
|
len = length list;
|
|
|
|
|
foldl' = n:
|
2014-10-04 16:02:29 +01:00
|
|
|
|
if n == -1
|
2012-08-13 19:19:50 +01:00
|
|
|
|
then nul
|
2014-10-04 16:02:29 +01:00
|
|
|
|
else op (foldl' (n - 1)) (elemAt list n);
|
|
|
|
|
in foldl' (length list - 1);
|
2012-08-13 19:19:50 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Strict version of foldl.
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
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.)
|
|
|
|
|
*/
|
2015-07-23 16:19:21 +01:00
|
|
|
|
foldl' = builtins.foldl' or foldl;
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Map with index
|
|
|
|
|
|
|
|
|
|
FIXME(zimbatm): why does this start to count at 1?
|
2015-07-23 16:19:21 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
imap (i: v: "${v}-${toString i}") ["a" "b"]
|
|
|
|
|
=> [ "a-1" "b-2" ]
|
|
|
|
|
*/
|
2016-06-17 11:06:48 +01:00
|
|
|
|
imap = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Map and concatenate the result.
|
2013-11-12 12:48:19 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
concatMap (x: [x] ++ ["z"]) ["a" "b"]
|
|
|
|
|
=> [ "a" "z" "b" "z" ]
|
|
|
|
|
*/
|
2009-02-09 16:51:03 +00:00
|
|
|
|
concatMap = f: list: concatLists (map f list);
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Flatten the argument into a single list; that is, nested lists are
|
|
|
|
|
spliced into the top-level lists.
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
flatten [1 [2 [3] 4] 5]
|
|
|
|
|
=> [1 2 3 4 5]
|
|
|
|
|
flatten 1
|
|
|
|
|
=> [1]
|
|
|
|
|
*/
|
2009-02-09 16:51:03 +00:00
|
|
|
|
flatten = x:
|
|
|
|
|
if isList x
|
2015-07-23 16:19:21 +01:00
|
|
|
|
then foldl' (x: y: x ++ (flatten y)) [] x
|
2009-02-09 16:51:03 +00:00
|
|
|
|
else [x];
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Remove elements equal to 'e' from a list. Useful for buildInputs.
|
2013-11-12 12:48:19 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
remove 3 [ 1 3 4 3 ]
|
|
|
|
|
=> [ 1 4 ]
|
|
|
|
|
*/
|
2011-03-15 09:24:47 +00:00
|
|
|
|
remove = e: filter (x: x != e);
|
|
|
|
|
|
2016-02-28 23:27:06 +00: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.
|
|
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
*/
|
2009-02-09 16:51:03 +00:00
|
|
|
|
findSingle = pred: default: multiple: list:
|
2013-07-12 17:36:36 +01:00
|
|
|
|
let found = filter pred list; len = length found;
|
|
|
|
|
in if len == 0 then default
|
|
|
|
|
else if len != 1 then multiple
|
|
|
|
|
else head found;
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Find the first element in the list matching the specified
|
|
|
|
|
predicate or returns `default' if no such element exists.
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
findFirst (x: x > 3) 7 [ 1 6 4 ]
|
|
|
|
|
=> 6
|
|
|
|
|
findFirst (x: x > 9) 7 [ 1 6 4 ]
|
|
|
|
|
=> 7
|
|
|
|
|
*/
|
2009-07-22 15:43:39 +01:00
|
|
|
|
findFirst = pred: default: list:
|
|
|
|
|
let found = filter pred list;
|
|
|
|
|
in if found == [] then default else head found;
|
2013-11-12 12:48:19 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Return true iff function `pred' returns true for at least element
|
|
|
|
|
of `list'.
|
2009-07-22 15:43:39 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
any isString [ 1 "a" { } ]
|
|
|
|
|
=> true
|
|
|
|
|
any isString [ 1 { } ]
|
|
|
|
|
=> false
|
|
|
|
|
*/
|
2015-07-23 18:25:58 +01:00
|
|
|
|
any = builtins.any or (pred: fold (x: y: if pred x then true else y) false);
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Return true iff function `pred' returns true for all elements of
|
|
|
|
|
`list'.
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
all (x: x < 3) [ 1 2 ]
|
|
|
|
|
=> true
|
|
|
|
|
all (x: x < 3) [ 1 2 3 ]
|
|
|
|
|
=> false
|
|
|
|
|
*/
|
2015-07-23 18:25:58 +01:00
|
|
|
|
all = builtins.all or (pred: fold (x: y: if pred x then y else false) true);
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Count how many times function `pred' returns true for the elements
|
|
|
|
|
of `list'.
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
count (x: x == 3) [ 3 2 3 4 6 ]
|
|
|
|
|
=> 2
|
|
|
|
|
*/
|
2015-07-23 16:19:21 +01:00
|
|
|
|
count = pred: foldl' (c: x: if pred x then c + 1 else c) 0;
|
2013-10-28 03:46:36 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* 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').
|
2013-10-28 03:46:36 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
optional true "foo"
|
|
|
|
|
=> [ "foo" ]
|
|
|
|
|
optional false "foo"
|
|
|
|
|
=> [ ]
|
|
|
|
|
*/
|
2009-02-09 16:51:03 +00:00
|
|
|
|
optional = cond: elem: if cond then [elem] else [];
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Return a list or an empty list, dependening on a boolean value.
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
optionals true [ 2 3 ]
|
|
|
|
|
=> [ 2 3 ]
|
|
|
|
|
optionals false [ 2 3 ]
|
|
|
|
|
=> [ ]
|
|
|
|
|
*/
|
2009-02-09 16:51:03 +00:00
|
|
|
|
optionals = cond: elems: if cond then elems else [];
|
|
|
|
|
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* 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 "]
|
|
|
|
|
*/
|
2013-11-12 12:48:19 +00:00
|
|
|
|
toList = x: if isList x then x else [x];
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Return a list of integers from `first' up to and including `last'.
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
range 2 4
|
|
|
|
|
=> [ 2 3 4 ]
|
|
|
|
|
range 3 2
|
|
|
|
|
=> [ ]
|
|
|
|
|
*/
|
2016-06-17 11:06:48 +01:00
|
|
|
|
range = first: last:
|
|
|
|
|
if first > last then
|
|
|
|
|
[]
|
2015-07-28 16:31:43 +01:00
|
|
|
|
else
|
2016-06-17 11:06:48 +01:00
|
|
|
|
genList (n: first + n) (last - first + 1);
|
2013-11-12 12:48:19 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Splits the elements of a list in two lists, `right' and
|
|
|
|
|
`wrong', depending on the evaluation of a predicate.
|
2009-02-09 16:51:03 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
partition (x: x > 2) [ 5 1 2 3 4 ]
|
|
|
|
|
=> { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
|
|
|
|
|
*/
|
2009-02-09 16:51:03 +00:00
|
|
|
|
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 = []; };
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* 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.
|
2009-09-28 19:22:14 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"]
|
|
|
|
|
=> ["he" "lo"]
|
|
|
|
|
*/
|
2016-06-17 11:06:48 +01:00
|
|
|
|
zipListsWith = f: fst: snd:
|
|
|
|
|
genList
|
|
|
|
|
(n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
|
2009-09-28 19:22:14 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* 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"; } ]
|
|
|
|
|
*/
|
2009-09-28 19:22:14 +01:00
|
|
|
|
zipLists = zipListsWith (fst: snd: { inherit fst snd; });
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Reverse the order of the elements of a list.
|
2013-10-27 23:56:22 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
reverseList [ "b" "o" "j" ]
|
|
|
|
|
=> [ "j" "o" "b" ]
|
|
|
|
|
*/
|
2016-06-17 11:06:48 +01:00
|
|
|
|
reverseList = xs:
|
|
|
|
|
let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
|
2009-11-07 01:59:55 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* 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.
|
2013-10-27 23:56:22 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
sort (a: b: a < b) [ 5 3 7 ]
|
|
|
|
|
=> [ 3 5 7 ]
|
|
|
|
|
*/
|
2015-07-28 17:42:04 +01:00
|
|
|
|
sort = builtins.sort or (
|
|
|
|
|
strictLess: list:
|
2009-11-07 01:59:55 +00:00
|
|
|
|
let
|
2013-07-12 17:36:36 +01:00
|
|
|
|
len = length list;
|
|
|
|
|
first = head list;
|
2014-10-04 16:02:29 +01:00
|
|
|
|
pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (n + 1); in
|
2013-07-12 17:36:36 +01:00
|
|
|
|
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 = []; };
|
2009-11-07 01:59:55 +00:00
|
|
|
|
in
|
2014-10-04 16:02:29 +01:00
|
|
|
|
if len < 2 then list
|
2015-07-28 17:42:04 +01:00
|
|
|
|
else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
|
2009-11-07 01:59:55 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Return the first (at most) N elements of a list.
|
2009-12-08 21:47:14 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
take 2 [ "a" "b" "c" "d" ]
|
|
|
|
|
=> [ "a" "b" ]
|
|
|
|
|
take 2 [ ]
|
|
|
|
|
=> [ ]
|
|
|
|
|
*/
|
2016-06-17 11:06:48 +01:00
|
|
|
|
take = count: sublist 0 count;
|
2009-12-08 21:47:14 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Remove the first (at most) N elements of a list.
|
2013-11-12 12:48:19 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
drop 2 [ "a" "b" "c" "d" ]
|
|
|
|
|
=> [ "c" "d" ]
|
|
|
|
|
drop 2 [ ]
|
|
|
|
|
=> [ ]
|
|
|
|
|
*/
|
2016-06-17 11:06:48 +01:00
|
|
|
|
drop = count: list: sublist count (length list) list;
|
2015-07-28 16:31:43 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Return a list consisting of at most ‘count’ elements of ‘list’,
|
|
|
|
|
starting at index ‘start’.
|
2015-07-28 16:31:43 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
sublist 1 3 [ "a" "b" "c" "d" "e" ]
|
|
|
|
|
=> [ "b" "c" "d" ]
|
|
|
|
|
sublist 1 3 [ ]
|
|
|
|
|
=> [ ]
|
|
|
|
|
*/
|
2015-07-28 16:31:43 +01:00
|
|
|
|
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);
|
2010-04-17 19:26:40 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Return the last element of a list.
|
2013-11-12 12:48:19 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
last [ 1 2 3 ]
|
|
|
|
|
=> 3
|
|
|
|
|
*/
|
2010-01-03 11:05:42 +00:00
|
|
|
|
last = list:
|
2014-10-04 16:02:29 +01:00
|
|
|
|
assert list != []; elemAt list (length list - 1);
|
2010-01-03 11:05:42 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Return all elements but the last
|
2012-08-13 23:26:19 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
init [ 1 2 3 ]
|
|
|
|
|
=> [ 1 2 ]
|
|
|
|
|
*/
|
2014-09-16 17:03:46 +01:00
|
|
|
|
init = list: assert list != []; take (length list - 1) list;
|
|
|
|
|
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* FIXME(zimbatm) Not used anywhere
|
|
|
|
|
*/
|
2013-12-12 19:01:48 +00:00
|
|
|
|
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
|
2014-04-08 23:02:20 +01:00
|
|
|
|
|
2015-07-28 16:31:43 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Remove duplicate elements from the list. O(n^2) complexity.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
|
|
unique [ 3 2 3 4 ]
|
|
|
|
|
=> [ 3 2 4 ]
|
|
|
|
|
*/
|
2014-11-12 20:36:26 +00:00
|
|
|
|
unique = list:
|
|
|
|
|
if list == [] then
|
|
|
|
|
[]
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
x = head list;
|
|
|
|
|
xs = unique (drop 1 list);
|
|
|
|
|
in [x] ++ remove x xs;
|
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Intersects list 'e' and another list. O(nm) complexity.
|
2015-07-28 16:31:43 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
intersectLists [ 1 2 3 ] [ 6 3 2 ]
|
|
|
|
|
=> [ 3 2 ]
|
|
|
|
|
*/
|
2015-03-21 16:09:38 +00:00
|
|
|
|
intersectLists = e: filter (x: elem x e);
|
2015-02-28 03:02:15 +00:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
/* Subtracts list 'e' from another list. O(nm) complexity.
|
2015-07-28 16:31:43 +01:00
|
|
|
|
|
2016-02-28 23:27:06 +00:00
|
|
|
|
Example:
|
|
|
|
|
subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ]
|
|
|
|
|
=> [ 1 4 5 ]
|
|
|
|
|
*/
|
2015-03-21 16:09:38 +00:00
|
|
|
|
subtractLists = e: filter (x: !(elem x e));
|
2015-07-28 16:31:43 +01:00
|
|
|
|
|
2009-02-09 16:51:03 +00:00
|
|
|
|
}
|