85 lines
2.5 KiB
Nix
85 lines
2.5 KiB
Nix
{nib, ...}: let
|
|
inherit
|
|
(builtins)
|
|
concatMap
|
|
elemAt
|
|
foldl'
|
|
genList
|
|
length
|
|
;
|
|
inherit
|
|
(nib.std)
|
|
min
|
|
;
|
|
in rec {
|
|
foldl = op: nul: list: let
|
|
foldl' = n:
|
|
if n == -1
|
|
then nul
|
|
else op (foldl' (n - 1)) (elemAt list n);
|
|
in
|
|
foldl' (length list - 1);
|
|
|
|
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
|
|
|
|
findFirstIndex = pred: default: list: let
|
|
# A naive recursive implementation would be much simpler, but
|
|
# would also overflow the evaluator stack. We use `foldl'` as a workaround
|
|
# because it reuses the same stack space, evaluating the function for one
|
|
# element after another. We can't return early, so this means that we
|
|
# sacrifice early cutoff, but that appears to be an acceptable cost. A
|
|
# clever scheme with "exponential search" is possible, but appears over-
|
|
# engineered for now. See https://github.com/NixOS/nixpkgs/pull/235267
|
|
# Invariant:
|
|
# - if index < 0 then el == elemAt list (- index - 1) and all elements before el didn't satisfy pred
|
|
# - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred
|
|
#
|
|
# We start with index -1 and the 0'th element of the list, which satisfies the invariant
|
|
resultIndex =
|
|
foldl' (
|
|
index: el:
|
|
if index < 0
|
|
then
|
|
# No match yet before the current index, we need to check the element
|
|
if pred el
|
|
then
|
|
# We have a match! Turn it into the actual index to prevent future iterations from modifying it
|
|
-index - 1
|
|
else
|
|
# Still no match, update the index to the next element (we're counting down, so minus one)
|
|
index - 1
|
|
else
|
|
# There's already a match, propagate the index without evaluating anything
|
|
index
|
|
) (-1)
|
|
list;
|
|
in
|
|
if resultIndex < 0
|
|
then default
|
|
else resultIndex;
|
|
|
|
findFirst = pred: default: list: let
|
|
index = findFirstIndex pred null list;
|
|
in
|
|
if index == null
|
|
then default
|
|
else elemAt list index;
|
|
|
|
zipListsWith = f: fst: snd:
|
|
genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
|
|
|
|
# zipLists = zipListsWith (fst: snd: {inherit fst snd;});
|
|
|
|
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
|
|
);
|
|
|
|
take = sublist 0;
|
|
}
|