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