at 24.05-pre 25 kB view raw
1/* General list operations. */ 2{ lib }: 3let 4 inherit (lib.strings) toInt; 5 inherit (lib.trivial) compare min id; 6 inherit (lib.attrsets) mapAttrs; 7in 8rec { 9 10 inherit (builtins) head tail length isList elemAt concatLists filter elem genList map; 11 12 /* Create a list consisting of a single element. `singleton x` is 13 sometimes more convenient with respect to indentation than `[x]` 14 when x spans multiple lines. 15 16 Type: singleton :: a -> [a] 17 18 Example: 19 singleton "foo" 20 => [ "foo" ] 21 */ 22 singleton = x: [x]; 23 24 /* Apply the function to each element in the list. Same as `map`, but arguments 25 flipped. 26 27 Type: forEach :: [a] -> (a -> b) -> [b] 28 29 Example: 30 forEach [ 1 2 ] (x: 31 toString x 32 ) 33 => [ "1" "2" ] 34 */ 35 forEach = xs: f: map f xs; 36 37 /* right fold a binary function `op` between successive elements of 38 `list` with `nul` as the starting value, i.e., 39 `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`. 40 41 Type: foldr :: (a -> b -> b) -> b -> [a] -> b 42 43 Example: 44 concat = foldr (a: b: a + b) "z" 45 concat [ "a" "b" "c" ] 46 => "abcz" 47 # different types 48 strange = foldr (int: str: toString (int + 1) + str) "a" 49 strange [ 1 2 3 4 ] 50 => "2345a" 51 */ 52 foldr = op: nul: list: 53 let 54 len = length list; 55 fold' = n: 56 if n == len 57 then nul 58 else op (elemAt list n) (fold' (n + 1)); 59 in fold' 0; 60 61 /* `fold` is an alias of `foldr` for historic reasons */ 62 # FIXME(Profpatsch): deprecate? 63 fold = foldr; 64 65 66 /* left fold, like `foldr`, but from the left: 67 `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`. 68 69 Type: foldl :: (b -> a -> b) -> b -> [a] -> b 70 71 Example: 72 lconcat = foldl (a: b: a + b) "z" 73 lconcat [ "a" "b" "c" ] 74 => "zabc" 75 # different types 76 lstrange = foldl (str: int: str + toString (int + 1)) "a" 77 lstrange [ 1 2 3 4 ] 78 => "a2345" 79 */ 80 foldl = op: nul: list: 81 let 82 foldl' = n: 83 if n == -1 84 then nul 85 else op (foldl' (n - 1)) (elemAt list n); 86 in foldl' (length list - 1); 87 88 /* 89 Reduce a list by applying a binary operator from left to right, 90 starting with an initial accumulator. 91 92 Before each application of the operator, the accumulator value is evaluated. 93 This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl). 94 95 Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'), 96 the initial accumulator argument is evaluated before the first iteration. 97 98 A call like 99 100 ```nix 101 foldl' op acc [ x x x ... x x ] 102 ``` 103 104 is (denotationally) equivalent to the following, 105 but with the added benefit that `foldl'` itself will never overflow the stack. 106 107 ```nix 108 let 109 acc = builtins.seq acc (op acc x ); 110 acc = builtins.seq acc (op acc x ); 111 acc = builtins.seq acc (op acc x ); 112 ... 113 acc = builtins.seq acc (op acc x); 114 acc = builtins.seq acc (op acc x ); 115 in 116 acc 117 118 # Or ignoring builtins.seq 119 op (op (... (op (op (op acc x) x) x) ...) x) x 120 ``` 121 122 Type: foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc 123 124 Example: 125 foldl' (acc: x: acc + x) 0 [1 2 3] 126 => 6 127 */ 128 foldl' = 129 /* The binary operation to run, where the two arguments are: 130 131 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration 132 2. `x`: The corresponding list element for this iteration 133 */ 134 op: 135 # The initial accumulator value 136 acc: 137 # The list to fold 138 list: 139 140 # The builtin `foldl'` is a bit lazier than one might expect. 141 # See https://github.com/NixOS/nix/pull/7158. 142 # In particular, the initial accumulator value is not forced before the first iteration starts. 143 builtins.seq acc 144 (builtins.foldl' op acc list); 145 146 /* Map with index starting from 0 147 148 Type: imap0 :: (int -> a -> b) -> [a] -> [b] 149 150 Example: 151 imap0 (i: v: "${v}-${toString i}") ["a" "b"] 152 => [ "a-0" "b-1" ] 153 */ 154 imap0 = f: list: genList (n: f n (elemAt list n)) (length list); 155 156 /* Map with index starting from 1 157 158 Type: imap1 :: (int -> a -> b) -> [a] -> [b] 159 160 Example: 161 imap1 (i: v: "${v}-${toString i}") ["a" "b"] 162 => [ "a-1" "b-2" ] 163 */ 164 imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list); 165 166 /* Map and concatenate the result. 167 168 Type: concatMap :: (a -> [b]) -> [a] -> [b] 169 170 Example: 171 concatMap (x: [x] ++ ["z"]) ["a" "b"] 172 => [ "a" "z" "b" "z" ] 173 */ 174 concatMap = builtins.concatMap or (f: list: concatLists (map f list)); 175 176 /* Flatten the argument into a single list; that is, nested lists are 177 spliced into the top-level lists. 178 179 Example: 180 flatten [1 [2 [3] 4] 5] 181 => [1 2 3 4 5] 182 flatten 1 183 => [1] 184 */ 185 flatten = x: 186 if isList x 187 then concatMap (y: flatten y) x 188 else [x]; 189 190 /* Remove elements equal to 'e' from a list. Useful for buildInputs. 191 192 Type: remove :: a -> [a] -> [a] 193 194 Example: 195 remove 3 [ 1 3 4 3 ] 196 => [ 1 4 ] 197 */ 198 remove = 199 # Element to remove from the list 200 e: filter (x: x != e); 201 202 /* Find the sole element in the list matching the specified 203 predicate, returns `default` if no such element exists, or 204 `multiple` if there are multiple matching elements. 205 206 Type: findSingle :: (a -> bool) -> a -> a -> [a] -> a 207 208 Example: 209 findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ] 210 => "multiple" 211 findSingle (x: x == 3) "none" "multiple" [ 1 3 ] 212 => 3 213 findSingle (x: x == 3) "none" "multiple" [ 1 9 ] 214 => "none" 215 */ 216 findSingle = 217 # Predicate 218 pred: 219 # Default value to return if element was not found. 220 default: 221 # Default value to return if more than one element was found 222 multiple: 223 # Input list 224 list: 225 let found = filter pred list; len = length found; 226 in if len == 0 then default 227 else if len != 1 then multiple 228 else head found; 229 230 /* Find the first index in the list matching the specified 231 predicate or return `default` if no such element exists. 232 233 Type: findFirstIndex :: (a -> Bool) -> b -> [a] -> (Int | b) 234 235 Example: 236 findFirstIndex (x: x > 3) null [ 0 6 4 ] 237 => 1 238 findFirstIndex (x: x > 9) null [ 0 6 4 ] 239 => null 240 */ 241 findFirstIndex = 242 # Predicate 243 pred: 244 # Default value to return 245 default: 246 # Input list 247 list: 248 let 249 # A naive recursive implementation would be much simpler, but 250 # would also overflow the evaluator stack. We use `foldl'` as a workaround 251 # because it reuses the same stack space, evaluating the function for one 252 # element after another. We can't return early, so this means that we 253 # sacrifice early cutoff, but that appears to be an acceptable cost. A 254 # clever scheme with "exponential search" is possible, but appears over- 255 # engineered for now. See https://github.com/NixOS/nixpkgs/pull/235267 256 257 # Invariant: 258 # - if index < 0 then el == elemAt list (- index - 1) and all elements before el didn't satisfy pred 259 # - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred 260 # 261 # We start with index -1 and the 0'th element of the list, which satisfies the invariant 262 resultIndex = foldl' (index: el: 263 if index < 0 then 264 # No match yet before the current index, we need to check the element 265 if pred el then 266 # We have a match! Turn it into the actual index to prevent future iterations from modifying it 267 - index - 1 268 else 269 # Still no match, update the index to the next element (we're counting down, so minus one) 270 index - 1 271 else 272 # There's already a match, propagate the index without evaluating anything 273 index 274 ) (-1) list; 275 in 276 if resultIndex < 0 then 277 default 278 else 279 resultIndex; 280 281 /* Find the first element in the list matching the specified 282 predicate or return `default` if no such element exists. 283 284 Type: findFirst :: (a -> bool) -> a -> [a] -> a 285 286 Example: 287 findFirst (x: x > 3) 7 [ 1 6 4 ] 288 => 6 289 findFirst (x: x > 9) 7 [ 1 6 4 ] 290 => 7 291 */ 292 findFirst = 293 # Predicate 294 pred: 295 # Default value to return 296 default: 297 # Input list 298 list: 299 let 300 index = findFirstIndex pred null list; 301 in 302 if index == null then 303 default 304 else 305 elemAt list index; 306 307 /* Return true if function `pred` returns true for at least one 308 element of `list`. 309 310 Type: any :: (a -> bool) -> [a] -> bool 311 312 Example: 313 any isString [ 1 "a" { } ] 314 => true 315 any isString [ 1 { } ] 316 => false 317 */ 318 any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false); 319 320 /* Return true if function `pred` returns true for all elements of 321 `list`. 322 323 Type: all :: (a -> bool) -> [a] -> bool 324 325 Example: 326 all (x: x < 3) [ 1 2 ] 327 => true 328 all (x: x < 3) [ 1 2 3 ] 329 => false 330 */ 331 all = builtins.all or (pred: foldr (x: y: if pred x then y else false) true); 332 333 /* Count how many elements of `list` match the supplied predicate 334 function. 335 336 Type: count :: (a -> bool) -> [a] -> int 337 338 Example: 339 count (x: x == 3) [ 3 2 3 4 6 ] 340 => 2 341 */ 342 count = 343 # Predicate 344 pred: foldl' (c: x: if pred x then c + 1 else c) 0; 345 346 /* Return a singleton list or an empty list, depending on a boolean 347 value. Useful when building lists with optional elements 348 (e.g. `++ optional (system == "i686-linux") firefox`). 349 350 Type: optional :: bool -> a -> [a] 351 352 Example: 353 optional true "foo" 354 => [ "foo" ] 355 optional false "foo" 356 => [ ] 357 */ 358 optional = cond: elem: if cond then [elem] else []; 359 360 /* Return a list or an empty list, depending on a boolean value. 361 362 Type: optionals :: bool -> [a] -> [a] 363 364 Example: 365 optionals true [ 2 3 ] 366 => [ 2 3 ] 367 optionals false [ 2 3 ] 368 => [ ] 369 */ 370 optionals = 371 # Condition 372 cond: 373 # List to return if condition is true 374 elems: if cond then elems else []; 375 376 377 /* If argument is a list, return it; else, wrap it in a singleton 378 list. If you're using this, you should almost certainly 379 reconsider if there isn't a more "well-typed" approach. 380 381 Example: 382 toList [ 1 2 ] 383 => [ 1 2 ] 384 toList "hi" 385 => [ "hi "] 386 */ 387 toList = x: if isList x then x else [x]; 388 389 /* Return a list of integers from `first` up to and including `last`. 390 391 Type: range :: int -> int -> [int] 392 393 Example: 394 range 2 4 395 => [ 2 3 4 ] 396 range 3 2 397 => [ ] 398 */ 399 range = 400 # First integer in the range 401 first: 402 # Last integer in the range 403 last: 404 if first > last then 405 [] 406 else 407 genList (n: first + n) (last - first + 1); 408 409 /* Return a list with `n` copies of an element. 410 411 Type: replicate :: int -> a -> [a] 412 413 Example: 414 replicate 3 "a" 415 => [ "a" "a" "a" ] 416 replicate 2 true 417 => [ true true ] 418 */ 419 replicate = n: elem: genList (_: elem) n; 420 421 /* Splits the elements of a list in two lists, `right` and 422 `wrong`, depending on the evaluation of a predicate. 423 424 Type: (a -> bool) -> [a] -> { right :: [a]; wrong :: [a]; } 425 426 Example: 427 partition (x: x > 2) [ 5 1 2 3 4 ] 428 => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; } 429 */ 430 partition = builtins.partition or (pred: 431 foldr (h: t: 432 if pred h 433 then { right = [h] ++ t.right; wrong = t.wrong; } 434 else { right = t.right; wrong = [h] ++ t.wrong; } 435 ) { right = []; wrong = []; }); 436 437 /* Splits the elements of a list into many lists, using the return value of a predicate. 438 Predicate should return a string which becomes keys of attrset `groupBy` returns. 439 440 `groupBy'` allows to customise the combining function and initial value 441 442 Example: 443 groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ] 444 => { true = [ 5 3 4 ]; false = [ 1 2 ]; } 445 groupBy (x: x.name) [ {name = "icewm"; script = "icewm &";} 446 {name = "xfce"; script = "xfce4-session &";} 447 {name = "icewm"; script = "icewmbg &";} 448 {name = "mate"; script = "gnome-session &";} 449 ] 450 => { icewm = [ { name = "icewm"; script = "icewm &"; } 451 { name = "icewm"; script = "icewmbg &"; } ]; 452 mate = [ { name = "mate"; script = "gnome-session &"; } ]; 453 xfce = [ { name = "xfce"; script = "xfce4-session &"; } ]; 454 } 455 456 groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ] 457 => { true = 12; false = 3; } 458 */ 459 groupBy' = op: nul: pred: lst: mapAttrs (name: foldl op nul) (groupBy pred lst); 460 461 groupBy = builtins.groupBy or ( 462 pred: foldl' (r: e: 463 let 464 key = pred e; 465 in 466 r // { ${key} = (r.${key} or []) ++ [e]; } 467 ) {}); 468 469 /* Merges two lists of the same size together. If the sizes aren't the same 470 the merging stops at the shortest. How both lists are merged is defined 471 by the first argument. 472 473 Type: zipListsWith :: (a -> b -> c) -> [a] -> [b] -> [c] 474 475 Example: 476 zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"] 477 => ["he" "lo"] 478 */ 479 zipListsWith = 480 # Function to zip elements of both lists 481 f: 482 # First list 483 fst: 484 # Second list 485 snd: 486 genList 487 (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd)); 488 489 /* Merges two lists of the same size together. If the sizes aren't the same 490 the merging stops at the shortest. 491 492 Type: zipLists :: [a] -> [b] -> [{ fst :: a; snd :: b; }] 493 494 Example: 495 zipLists [ 1 2 ] [ "a" "b" ] 496 => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ] 497 */ 498 zipLists = zipListsWith (fst: snd: { inherit fst snd; }); 499 500 /* Reverse the order of the elements of a list. 501 502 Type: reverseList :: [a] -> [a] 503 504 Example: 505 506 reverseList [ "b" "o" "j" ] 507 => [ "j" "o" "b" ] 508 */ 509 reverseList = xs: 510 let l = length xs; in genList (n: elemAt xs (l - n - 1)) l; 511 512 /* Depth-First Search (DFS) for lists `list != []`. 513 514 `before a b == true` means that `b` depends on `a` (there's an 515 edge from `b` to `a`). 516 517 Example: 518 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ] 519 == { minimal = "/"; # minimal element 520 visited = [ "/home/user" ]; # seen elements (in reverse order) 521 rest = [ "/home" "other" ]; # everything else 522 } 523 524 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ] 525 == { cycle = "/"; # cycle encountered at this element 526 loops = [ "/" ]; # and continues to these elements 527 visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order) 528 rest = [ "/home" "other" ]; # everything else 529 530 */ 531 listDfs = stopOnCycles: before: list: 532 let 533 dfs' = us: visited: rest: 534 let 535 c = filter (x: before x us) visited; 536 b = partition (x: before x us) rest; 537 in if stopOnCycles && (length c > 0) 538 then { cycle = us; loops = c; inherit visited rest; } 539 else if length b.right == 0 540 then # nothing is before us 541 { minimal = us; inherit visited rest; } 542 else # grab the first one before us and continue 543 dfs' (head b.right) 544 ([ us ] ++ visited) 545 (tail b.right ++ b.wrong); 546 in dfs' (head list) [] (tail list); 547 548 /* Sort a list based on a partial ordering using DFS. This 549 implementation is O(N^2), if your ordering is linear, use `sort` 550 instead. 551 552 `before a b == true` means that `b` should be after `a` 553 in the result. 554 555 Example: 556 557 toposort hasPrefix [ "/home/user" "other" "/" "/home" ] 558 == { result = [ "/" "/home" "/home/user" "other" ]; } 559 560 toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ] 561 == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle 562 loops = [ "/" ]; } # loops back to these elements 563 564 toposort hasPrefix [ "other" "/home/user" "/home" "/" ] 565 == { result = [ "other" "/" "/home" "/home/user" ]; } 566 567 toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; } 568 569 */ 570 toposort = before: list: 571 let 572 dfsthis = listDfs true before list; 573 toporest = toposort before (dfsthis.visited ++ dfsthis.rest); 574 in 575 if length list < 2 576 then # finish 577 { result = list; } 578 else if dfsthis ? cycle 579 then # there's a cycle, starting from the current vertex, return it 580 { cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited); 581 inherit (dfsthis) loops; } 582 else if toporest ? cycle 583 then # there's a cycle somewhere else in the graph, return it 584 toporest 585 # Slow, but short. Can be made a bit faster with an explicit stack. 586 else # there are no cycles 587 { result = [ dfsthis.minimal ] ++ toporest.result; }; 588 589 /* Sort a list based on a comparator function which compares two 590 elements and returns true if the first argument is strictly below 591 the second argument. The returned list is sorted in an increasing 592 order. The implementation does a quick-sort. 593 594 Example: 595 sort (a: b: a < b) [ 5 3 7 ] 596 => [ 3 5 7 ] 597 */ 598 sort = builtins.sort or ( 599 strictLess: list: 600 let 601 len = length list; 602 first = head list; 603 pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (n + 1); in 604 if n == len 605 then acc 606 else if strictLess first el 607 then next { inherit left; right = [ el ] ++ right; } 608 else 609 next { left = [ el ] ++ left; inherit right; }; 610 pivot = pivot' 1 { left = []; right = []; }; 611 in 612 if len < 2 then list 613 else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right)); 614 615 /* Compare two lists element-by-element. 616 617 Example: 618 compareLists compare [] [] 619 => 0 620 compareLists compare [] [ "a" ] 621 => -1 622 compareLists compare [ "a" ] [] 623 => 1 624 compareLists compare [ "a" "b" ] [ "a" "c" ] 625 => -1 626 */ 627 compareLists = cmp: a: b: 628 if a == [] 629 then if b == [] 630 then 0 631 else -1 632 else if b == [] 633 then 1 634 else let rel = cmp (head a) (head b); in 635 if rel == 0 636 then compareLists cmp (tail a) (tail b) 637 else rel; 638 639 /* Sort list using "Natural sorting". 640 Numeric portions of strings are sorted in numeric order. 641 642 Example: 643 naturalSort ["disk11" "disk8" "disk100" "disk9"] 644 => ["disk8" "disk9" "disk11" "disk100"] 645 naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"] 646 => ["10.5.16.62" "10.46.133.149" "10.54.16.25"] 647 naturalSort ["v0.2" "v0.15" "v0.0.9"] 648 => [ "v0.0.9" "v0.2" "v0.15" ] 649 */ 650 naturalSort = lst: 651 let 652 vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s); 653 prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits 654 less = a: b: (compareLists compare (head a) (head b)) < 0; 655 in 656 map (x: elemAt x 1) (sort less prepared); 657 658 /* Return the first (at most) N elements of a list. 659 660 Type: take :: int -> [a] -> [a] 661 662 Example: 663 take 2 [ "a" "b" "c" "d" ] 664 => [ "a" "b" ] 665 take 2 [ ] 666 => [ ] 667 */ 668 take = 669 # Number of elements to take 670 count: sublist 0 count; 671 672 /* Remove the first (at most) N elements of a list. 673 674 Type: drop :: int -> [a] -> [a] 675 676 Example: 677 drop 2 [ "a" "b" "c" "d" ] 678 => [ "c" "d" ] 679 drop 2 [ ] 680 => [ ] 681 */ 682 drop = 683 # Number of elements to drop 684 count: 685 # Input list 686 list: sublist count (length list) list; 687 688 /* Whether the first list is a prefix of the second list. 689 690 Type: hasPrefix :: [a] -> [a] -> bool 691 692 Example: 693 hasPrefix [ 1 2 ] [ 1 2 3 4 ] 694 => true 695 hasPrefix [ 0 1 ] [ 1 2 3 4 ] 696 => false 697 */ 698 hasPrefix = 699 list1: 700 list2: 701 take (length list1) list2 == list1; 702 703 /* Remove the first list as a prefix from the second list. 704 Error if the first list isn't a prefix of the second list. 705 706 Type: removePrefix :: [a] -> [a] -> [a] 707 708 Example: 709 removePrefix [ 1 2 ] [ 1 2 3 4 ] 710 => [ 3 4 ] 711 removePrefix [ 0 1 ] [ 1 2 3 4 ] 712 => <error> 713 */ 714 removePrefix = 715 list1: 716 list2: 717 if hasPrefix list1 list2 then 718 drop (length list1) list2 719 else 720 throw "lib.lists.removePrefix: First argument is not a list prefix of the second argument"; 721 722 /* Return a list consisting of at most `count` elements of `list`, 723 starting at index `start`. 724 725 Type: sublist :: int -> int -> [a] -> [a] 726 727 Example: 728 sublist 1 3 [ "a" "b" "c" "d" "e" ] 729 => [ "b" "c" "d" ] 730 sublist 1 3 [ ] 731 => [ ] 732 */ 733 sublist = 734 # Index at which to start the sublist 735 start: 736 # Number of elements to take 737 count: 738 # Input list 739 list: 740 let len = length list; in 741 genList 742 (n: elemAt list (n + start)) 743 (if start >= len then 0 744 else if start + count > len then len - start 745 else count); 746 747 /* The common prefix of two lists. 748 749 Type: commonPrefix :: [a] -> [a] -> [a] 750 751 Example: 752 commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ] 753 => [ 1 2 ] 754 commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ] 755 => [ 1 2 3 ] 756 commonPrefix [ 1 2 3 ] [ 4 5 6 ] 757 => [ ] 758 */ 759 commonPrefix = 760 list1: 761 list2: 762 let 763 # Zip the lists together into a list of booleans whether each element matches 764 matchings = zipListsWith (fst: snd: fst != snd) list1 list2; 765 # Find the first index where the elements don't match, 766 # which will then also be the length of the common prefix. 767 # If all elements match, we fall back to the length of the zipped list, 768 # which is the same as the length of the smaller list. 769 commonPrefixLength = findFirstIndex id (length matchings) matchings; 770 in 771 take commonPrefixLength list1; 772 773 /* Return the last element of a list. 774 775 This function throws an error if the list is empty. 776 777 Type: last :: [a] -> a 778 779 Example: 780 last [ 1 2 3 ] 781 => 3 782 */ 783 last = list: 784 assert lib.assertMsg (list != []) "lists.last: list must not be empty!"; 785 elemAt list (length list - 1); 786 787 /* Return all elements but the last. 788 789 This function throws an error if the list is empty. 790 791 Type: init :: [a] -> [a] 792 793 Example: 794 init [ 1 2 3 ] 795 => [ 1 2 ] 796 */ 797 init = list: 798 assert lib.assertMsg (list != []) "lists.init: list must not be empty!"; 799 take (length list - 1) list; 800 801 802 /* Return the image of the cross product of some lists by a function. 803 804 Example: 805 crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]] 806 => [ "13" "14" "23" "24" ] 807 */ 808 crossLists = builtins.trace 809 "lib.crossLists is deprecated, use lib.cartesianProductOfSets instead" 810 (f: foldl (fs: args: concatMap (f: map f args) fs) [f]); 811 812 813 /* Remove duplicate elements from the list. O(n^2) complexity. 814 815 Type: unique :: [a] -> [a] 816 817 Example: 818 unique [ 3 2 3 4 ] 819 => [ 3 2 4 ] 820 */ 821 unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) []; 822 823 /* Check if list contains only unique elements. O(n^2) complexity. 824 825 Type: allUnique :: [a] -> bool 826 827 Example: 828 allUnique [ 3 2 3 4 ] 829 => false 830 allUnique [ 3 2 4 1 ] 831 => true 832 */ 833 allUnique = list: (length (unique list) == length list); 834 835 836 /* Intersects list 'e' and another list. O(nm) complexity. 837 838 Example: 839 intersectLists [ 1 2 3 ] [ 6 3 2 ] 840 => [ 3 2 ] 841 */ 842 intersectLists = e: filter (x: elem x e); 843 844 /* Subtracts list 'e' from another list. O(nm) complexity. 845 846 Example: 847 subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ] 848 => [ 1 4 5 ] 849 */ 850 subtractLists = e: filter (x: !(elem x e)); 851 852 /* Test if two lists have no common element. 853 It should be slightly more efficient than (intersectLists a b == []) 854 */ 855 mutuallyExclusive = a: b: length a == 0 || !(any (x: elem x a) b); 856 857}