lib/lists.nix: Remove uses of the tail function
nix lists are not like haskell lists, and tail is an O(n) operation. This makes recursion using tail less efficient than recursion using length + elemAt. Signed-off-by: Shea Levy <shea@shealevy.com>
This commit is contained in:
parent
18e9efe0f7
commit
a4c333474c
@ -1,9 +1,13 @@
|
|||||||
# General list operations.
|
# General list operations.
|
||||||
with {
|
let
|
||||||
inherit (import ./trivial.nix) deepSeq;
|
inherit (import ./trivial.nix) deepSeq;
|
||||||
};
|
|
||||||
|
|
||||||
rec {
|
inc = builtins.add 1;
|
||||||
|
|
||||||
|
dec = n: builtins.sub n 1;
|
||||||
|
|
||||||
|
inherit (builtins) elemAt;
|
||||||
|
in rec {
|
||||||
inherit (builtins) head tail length isList add sub lessThan;
|
inherit (builtins) head tail length isList add sub lessThan;
|
||||||
|
|
||||||
|
|
||||||
@ -17,50 +21,39 @@ rec {
|
|||||||
# `list' with `nul' as the starting value, i.e., `fold op nul [x_1
|
# `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
|
# x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. (This is
|
||||||
# Haskell's foldr).
|
# Haskell's foldr).
|
||||||
fold =
|
fold = op: nul: list:
|
||||||
if builtins ? elemAt
|
let
|
||||||
then op: nul: list:
|
len = length list;
|
||||||
let
|
fold' = n:
|
||||||
len = length list;
|
if n == len
|
||||||
fold' = n:
|
|
||||||
if n == len
|
|
||||||
then nul
|
|
||||||
else op (builtins.elemAt list n) (fold' (add n 1));
|
|
||||||
in fold' 0
|
|
||||||
else op: nul:
|
|
||||||
let fold' = list:
|
|
||||||
if list == []
|
|
||||||
then nul
|
then nul
|
||||||
else op (head list) (fold' (tail list));
|
else op (elemAt list n) (fold' (inc n));
|
||||||
in fold';
|
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)'.
|
||||||
foldl =
|
foldl = op: nul: list:
|
||||||
if builtins ? elemAt
|
let
|
||||||
then op: nul: list:
|
len = length list;
|
||||||
let
|
foldl' = n:
|
||||||
len = length list;
|
if n == minus1
|
||||||
foldl' = n:
|
|
||||||
if n == minus1
|
|
||||||
then nul
|
|
||||||
else op (foldl' (sub n 1)) (builtins.elemAt list n);
|
|
||||||
in foldl' (sub (length list) 1)
|
|
||||||
else op:
|
|
||||||
let foldl' = nul: list:
|
|
||||||
if list == []
|
|
||||||
then nul
|
then nul
|
||||||
else foldl' (op nul (head list)) (tail list);
|
else op (foldl' (dec n)) (elemAt list n);
|
||||||
in foldl';
|
in foldl' (dec (length list));
|
||||||
|
|
||||||
minus1 = sub 0 1;
|
minus1 = dec 0;
|
||||||
|
|
||||||
|
|
||||||
# map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] ==
|
# map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] ==
|
||||||
# ["a-1" "b-2"]'
|
# ["a-1" "b-2"]'
|
||||||
imap = f: list:
|
imap = f: list:
|
||||||
zipListsWith f (range 1 (length list)) list;
|
let
|
||||||
|
len = length list;
|
||||||
|
imap' = n:
|
||||||
|
if n == len
|
||||||
|
then []
|
||||||
|
else [ (f n (elemAt list n)) ] ++ imap' (inc n);
|
||||||
|
in imap' 0;
|
||||||
|
|
||||||
|
|
||||||
# Concatenate a list of lists.
|
# Concatenate a list of lists.
|
||||||
@ -102,10 +95,10 @@ rec {
|
|||||||
# predicate, returns `default' if no such element exists, or
|
# predicate, returns `default' if no such element exists, or
|
||||||
# `multiple' if there are multiple matching elements.
|
# `multiple' if there are multiple matching elements.
|
||||||
findSingle = pred: default: multiple: list:
|
findSingle = pred: default: multiple: list:
|
||||||
let found = filter pred list;
|
let found = filter pred list; len = length found;
|
||||||
in if found == [] then default
|
in if len == 0 then default
|
||||||
else if tail found != [] then multiple
|
else if len != 1 then multiple
|
||||||
else head found;
|
else head found;
|
||||||
|
|
||||||
|
|
||||||
# Find the first element in the list matching the specified
|
# Find the first element in the list matching the specified
|
||||||
@ -159,65 +152,84 @@ rec {
|
|||||||
|
|
||||||
|
|
||||||
zipListsWith = f: fst: snd:
|
zipListsWith = f: fst: snd:
|
||||||
if fst != [] && snd != [] then
|
let
|
||||||
[ (f (head fst) (head snd)) ]
|
len1 = length fst;
|
||||||
++ zipListsWith f (tail fst) (tail snd)
|
len2 = length snd;
|
||||||
else [];
|
len = if builtins.lessThan len1 len2 then len1 else len2;
|
||||||
|
zipListsWith' = n:
|
||||||
|
if n != len then
|
||||||
|
[ (f (elemAt fst n) (elemAt snd n)) ]
|
||||||
|
++ zipListsWith' (inc n)
|
||||||
|
else [];
|
||||||
|
in zipListsWith' 0;
|
||||||
|
|
||||||
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.
|
||||||
reverseList = l:
|
reverseList = fold (e: acc: acc ++ [ e ]) [];
|
||||||
let reverse_ = accu: l:
|
|
||||||
if l == [] then accu
|
|
||||||
else reverse_ ([(head l)] ++ accu) (tail l);
|
|
||||||
in reverse_ [] l;
|
|
||||||
|
|
||||||
|
|
||||||
# Sort a list based on a comparator function which compares two
|
# Sort a list based on a comparator function which compares two
|
||||||
# elements and returns true if the first argument is strictly below
|
# elements and returns true if the first argument is strictly below
|
||||||
# the second argument. The returned list is sorted in an increasing
|
# the second argument. The returned list is sorted in an increasing
|
||||||
# order. The implementation does a quick-sort.
|
# order. The implementation does a quick-sort.
|
||||||
sort = strictLess: list:
|
sort = strictLess: list:
|
||||||
let
|
let
|
||||||
# This implementation only has one element list on the left hand
|
len = length list;
|
||||||
# side of the concatenation operator.
|
first = head list;
|
||||||
qs = l: concat:
|
pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (inc n); in
|
||||||
if l == [] then concat
|
if n == len
|
||||||
else if length l == 1 then l ++ concat
|
then acc
|
||||||
else let
|
else if strictLess first el
|
||||||
part = partition (strictLess (head l)) (tail l);
|
then next { inherit left; right = [ el ] ++ right; }
|
||||||
in
|
else
|
||||||
qs part.wrong ([(head l)] ++ qs part.right concat);
|
next { left = [ el ] ++ left; inherit right; };
|
||||||
|
pivot = pivot' 1 { left = []; right = []; };
|
||||||
in
|
in
|
||||||
qs list [];
|
if lessThan len 2 then list
|
||||||
|
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.
|
||||||
take = count: list:
|
take = count: list:
|
||||||
if list == [] || count == 0 then []
|
let
|
||||||
else [ (head list) ] ++ take (builtins.sub count 1) (tail list);
|
len = length list;
|
||||||
|
take' = n:
|
||||||
|
if n == len || n == count
|
||||||
|
then []
|
||||||
|
else
|
||||||
|
[ (elemAt list n) ] ++ take' (inc n);
|
||||||
|
in take' 0;
|
||||||
|
|
||||||
|
|
||||||
# Remove the first (at most) N elements of a list.
|
# Remove the first (at most) N elements of a list.
|
||||||
drop = count: list:
|
drop = count: list:
|
||||||
if count == 0 then list
|
let
|
||||||
else drop (builtins.sub count 1) (tail list);
|
len = length list;
|
||||||
|
drop' = n:
|
||||||
|
if n == minus1 || lessThan n count
|
||||||
|
then []
|
||||||
|
else
|
||||||
|
drop' (dec n) ++ [ (elemAt list n) ];
|
||||||
|
in drop' (dec len);
|
||||||
|
|
||||||
|
|
||||||
last = list:
|
last = list:
|
||||||
assert list != [];
|
assert list != []; elemAt list (dec (length list));
|
||||||
let loop = l: if tail l == [] then head l else loop (tail l); in
|
|
||||||
loop list;
|
|
||||||
|
|
||||||
|
|
||||||
# Zip two lists together.
|
# Zip two lists together.
|
||||||
zipTwoLists = xs: ys:
|
zipTwoLists = xs: ys:
|
||||||
if xs != [] && ys != [] then
|
let
|
||||||
[ {first = head xs; second = head ys;} ]
|
len1 = length xs;
|
||||||
++ zipTwoLists (tail xs) (tail ys)
|
len2 = length ys;
|
||||||
else [];
|
len = if lessThan len1 len2 then len1 else len2;
|
||||||
|
zipTwoLists' = n:
|
||||||
|
if n != len then
|
||||||
|
[ { first = elemAt xs n; second = elemAt ys n; } ]
|
||||||
|
++ zipTwoLists' (inc n)
|
||||||
|
else [];
|
||||||
|
in zipTwoLists' 0;
|
||||||
|
|
||||||
deepSeqList = xs: y: if any (x: deepSeq x false) xs then y else y;
|
deepSeqList = xs: y: if any (x: deepSeq x false) xs then y else y;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user