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:
Shea Levy 2013-07-12 12:36:36 -04:00
parent 18e9efe0f7
commit a4c333474c

View File

@ -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;
} }