at 18.09-beta 17 kB view raw
1# General list operations. 2{ lib }: 3with lib.trivial; 4let 5 inherit (lib.strings) toInt; 6in 7rec { 8 9 inherit (builtins) head tail length isList elemAt concatLists filter elem genList; 10 11 /* Create a list consisting of a single element. `singleton x' is 12 sometimes more convenient with respect to indentation than `[x]' 13 when x spans multiple lines. 14 15 Example: 16 singleton "foo" 17 => [ "foo" ] 18 */ 19 singleton = x: [x]; 20 21 /* right fold a binary function `op' between successive elements of 22 `list' with `nul' as the starting value, i.e., 23 `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. 24 Type: 25 foldr :: (a -> b -> b) -> b -> [a] -> b 26 27 Example: 28 concat = foldr (a: b: a + b) "z" 29 concat [ "a" "b" "c" ] 30 => "abcz" 31 # different types 32 strange = foldr (int: str: toString (int + 1) + str) "a" 33 strange [ 1 2 3 4 ] 34 => "2345a" 35 */ 36 foldr = op: nul: list: 37 let 38 len = length list; 39 fold' = n: 40 if n == len 41 then nul 42 else op (elemAt list n) (fold' (n + 1)); 43 in fold' 0; 44 45 /* `fold' is an alias of `foldr' for historic reasons */ 46 # FIXME(Profpatsch): deprecate? 47 fold = foldr; 48 49 50 /* left fold, like `foldr', but from the left: 51 `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`. 52 53 Type: 54 foldl :: (b -> a -> b) -> b -> [a] -> b 55 56 Example: 57 lconcat = foldl (a: b: a + b) "z" 58 lconcat [ "a" "b" "c" ] 59 => "zabc" 60 # different types 61 lstrange = foldl (str: int: str + toString (int + 1)) "" 62 strange [ 1 2 3 4 ] 63 => "a2345" 64 */ 65 foldl = op: nul: list: 66 let 67 foldl' = n: 68 if n == -1 69 then nul 70 else op (foldl' (n - 1)) (elemAt list n); 71 in foldl' (length list - 1); 72 73 /* Strict version of `foldl'. 74 75 The difference is that evaluation is forced upon access. Usually used 76 with small whole results (in contract with lazily-generated list or large 77 lists where only a part is consumed.) 78 */ 79 foldl' = builtins.foldl' or foldl; 80 81 /* Map with index starting from 0 82 83 Example: 84 imap0 (i: v: "${v}-${toString i}") ["a" "b"] 85 => [ "a-0" "b-1" ] 86 */ 87 imap0 = f: list: genList (n: f n (elemAt list n)) (length list); 88 89 /* Map with index starting from 1 90 91 Example: 92 imap1 (i: v: "${v}-${toString i}") ["a" "b"] 93 => [ "a-1" "b-2" ] 94 */ 95 imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list); 96 97 /* Map and concatenate the result. 98 99 Example: 100 concatMap (x: [x] ++ ["z"]) ["a" "b"] 101 => [ "a" "z" "b" "z" ] 102 */ 103 concatMap = builtins.concatMap or (f: list: concatLists (map f list)); 104 105 /* Flatten the argument into a single list; that is, nested lists are 106 spliced into the top-level lists. 107 108 Example: 109 flatten [1 [2 [3] 4] 5] 110 => [1 2 3 4 5] 111 flatten 1 112 => [1] 113 */ 114 flatten = x: 115 if isList x 116 then concatMap (y: flatten y) x 117 else [x]; 118 119 /* Remove elements equal to 'e' from a list. Useful for buildInputs. 120 121 Example: 122 remove 3 [ 1 3 4 3 ] 123 => [ 1 4 ] 124 */ 125 remove = e: filter (x: x != e); 126 127 /* Find the sole element in the list matching the specified 128 predicate, returns `default' if no such element exists, or 129 `multiple' if there are multiple matching elements. 130 131 Example: 132 findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ] 133 => "multiple" 134 findSingle (x: x == 3) "none" "multiple" [ 1 3 ] 135 => 3 136 findSingle (x: x == 3) "none" "multiple" [ 1 9 ] 137 => "none" 138 */ 139 findSingle = pred: default: multiple: list: 140 let found = filter pred list; len = length found; 141 in if len == 0 then default 142 else if len != 1 then multiple 143 else head found; 144 145 /* Find the first element in the list matching the specified 146 predicate or returns `default' if no such element exists. 147 148 Example: 149 findFirst (x: x > 3) 7 [ 1 6 4 ] 150 => 6 151 findFirst (x: x > 9) 7 [ 1 6 4 ] 152 => 7 153 */ 154 findFirst = pred: default: list: 155 let found = filter pred list; 156 in if found == [] then default else head found; 157 158 /* Return true iff function `pred' returns true for at least element 159 of `list'. 160 161 Example: 162 any isString [ 1 "a" { } ] 163 => true 164 any isString [ 1 { } ] 165 => false 166 */ 167 any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false); 168 169 /* Return true iff function `pred' returns true for all elements of 170 `list'. 171 172 Example: 173 all (x: x < 3) [ 1 2 ] 174 => true 175 all (x: x < 3) [ 1 2 3 ] 176 => false 177 */ 178 all = builtins.all or (pred: foldr (x: y: if pred x then y else false) true); 179 180 /* Count how many times function `pred' returns true for the elements 181 of `list'. 182 183 Example: 184 count (x: x == 3) [ 3 2 3 4 6 ] 185 => 2 186 */ 187 count = pred: foldl' (c: x: if pred x then c + 1 else c) 0; 188 189 /* Return a singleton list or an empty list, depending on a boolean 190 value. Useful when building lists with optional elements 191 (e.g. `++ optional (system == "i686-linux") flashplayer'). 192 193 Example: 194 optional true "foo" 195 => [ "foo" ] 196 optional false "foo" 197 => [ ] 198 */ 199 optional = cond: elem: if cond then [elem] else []; 200 201 /* Return a list or an empty list, depending on a boolean value. 202 203 Example: 204 optionals true [ 2 3 ] 205 => [ 2 3 ] 206 optionals false [ 2 3 ] 207 => [ ] 208 */ 209 optionals = cond: elems: if cond then elems else []; 210 211 212 /* If argument is a list, return it; else, wrap it in a singleton 213 list. If you're using this, you should almost certainly 214 reconsider if there isn't a more "well-typed" approach. 215 216 Example: 217 toList [ 1 2 ] 218 => [ 1 2 ] 219 toList "hi" 220 => [ "hi "] 221 */ 222 toList = x: if isList x then x else [x]; 223 224 /* Return a list of integers from `first' up to and including `last'. 225 226 Example: 227 range 2 4 228 => [ 2 3 4 ] 229 range 3 2 230 => [ ] 231 */ 232 range = first: last: 233 if first > last then 234 [] 235 else 236 genList (n: first + n) (last - first + 1); 237 238 /* Splits the elements of a list in two lists, `right' and 239 `wrong', depending on the evaluation of a predicate. 240 241 Example: 242 partition (x: x > 2) [ 5 1 2 3 4 ] 243 => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; } 244 */ 245 partition = builtins.partition or (pred: 246 foldr (h: t: 247 if pred h 248 then { right = [h] ++ t.right; wrong = t.wrong; } 249 else { right = t.right; wrong = [h] ++ t.wrong; } 250 ) { right = []; wrong = []; }); 251 252 /* Splits the elements of a list into many lists, using the return value of a predicate. 253 Predicate should return a string which becomes keys of attrset `groupBy' returns. 254 255 `groupBy'' allows to customise the combining function and initial value 256 257 Example: 258 groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ] 259 => { true = [ 5 3 4 ]; false = [ 1 2 ]; } 260 groupBy (x: x.name) [ {name = "icewm"; script = "icewm &";} 261 {name = "xfce"; script = "xfce4-session &";} 262 {name = "icewm"; script = "icewmbg &";} 263 {name = "mate"; script = "gnome-session &";} 264 ] 265 => { icewm = [ { name = "icewm"; script = "icewm &"; } 266 { name = "icewm"; script = "icewmbg &"; } ]; 267 mate = [ { name = "mate"; script = "gnome-session &"; } ]; 268 xfce = [ { name = "xfce"; script = "xfce4-session &"; } ]; 269 } 270 271 272 groupBy' allows to customise the combining function and initial value 273 274 Example: 275 groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ] 276 => { true = 12; false = 3; } 277 */ 278 groupBy' = op: nul: pred: lst: 279 foldl' (r: e: 280 let 281 key = pred e; 282 in 283 r // { ${key} = op (r.${key} or nul) e; } 284 ) {} lst; 285 286 groupBy = groupBy' (sum: e: sum ++ [e]) []; 287 288 /* Merges two lists of the same size together. If the sizes aren't the same 289 the merging stops at the shortest. How both lists are merged is defined 290 by the first argument. 291 292 Example: 293 zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"] 294 => ["he" "lo"] 295 */ 296 zipListsWith = f: fst: snd: 297 genList 298 (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd)); 299 300 /* Merges two lists of the same size together. If the sizes aren't the same 301 the merging stops at the shortest. 302 303 Example: 304 zipLists [ 1 2 ] [ "a" "b" ] 305 => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ] 306 */ 307 zipLists = zipListsWith (fst: snd: { inherit fst snd; }); 308 309 /* Reverse the order of the elements of a list. 310 311 Example: 312 313 reverseList [ "b" "o" "j" ] 314 => [ "j" "o" "b" ] 315 */ 316 reverseList = xs: 317 let l = length xs; in genList (n: elemAt xs (l - n - 1)) l; 318 319 /* Depth-First Search (DFS) for lists `list != []`. 320 321 `before a b == true` means that `b` depends on `a` (there's an 322 edge from `b` to `a`). 323 324 Examples: 325 326 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ] 327 == { minimal = "/"; # minimal element 328 visited = [ "/home/user" ]; # seen elements (in reverse order) 329 rest = [ "/home" "other" ]; # everything else 330 } 331 332 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ] 333 == { cycle = "/"; # cycle encountered at this element 334 loops = [ "/" ]; # and continues to these elements 335 visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order) 336 rest = [ "/home" "other" ]; # everything else 337 338 */ 339 340 listDfs = stopOnCycles: before: list: 341 let 342 dfs' = us: visited: rest: 343 let 344 c = filter (x: before x us) visited; 345 b = partition (x: before x us) rest; 346 in if stopOnCycles && (length c > 0) 347 then { cycle = us; loops = c; inherit visited rest; } 348 else if length b.right == 0 349 then # nothing is before us 350 { minimal = us; inherit visited rest; } 351 else # grab the first one before us and continue 352 dfs' (head b.right) 353 ([ us ] ++ visited) 354 (tail b.right ++ b.wrong); 355 in dfs' (head list) [] (tail list); 356 357 /* Sort a list based on a partial ordering using DFS. This 358 implementation is O(N^2), if your ordering is linear, use `sort` 359 instead. 360 361 `before a b == true` means that `b` should be after `a` 362 in the result. 363 364 Examples: 365 366 toposort hasPrefix [ "/home/user" "other" "/" "/home" ] 367 == { result = [ "/" "/home" "/home/user" "other" ]; } 368 369 toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ] 370 == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle 371 loops = [ "/" ]; } # loops back to these elements 372 373 toposort hasPrefix [ "other" "/home/user" "/home" "/" ] 374 == { result = [ "other" "/" "/home" "/home/user" ]; } 375 376 toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; } 377 378 */ 379 380 toposort = before: list: 381 let 382 dfsthis = listDfs true before list; 383 toporest = toposort before (dfsthis.visited ++ dfsthis.rest); 384 in 385 if length list < 2 386 then # finish 387 { result = list; } 388 else if dfsthis ? "cycle" 389 then # there's a cycle, starting from the current vertex, return it 390 { cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited); 391 inherit (dfsthis) loops; } 392 else if toporest ? "cycle" 393 then # there's a cycle somewhere else in the graph, return it 394 toporest 395 # Slow, but short. Can be made a bit faster with an explicit stack. 396 else # there are no cycles 397 { result = [ dfsthis.minimal ] ++ toporest.result; }; 398 399 /* Sort a list based on a comparator function which compares two 400 elements and returns true if the first argument is strictly below 401 the second argument. The returned list is sorted in an increasing 402 order. The implementation does a quick-sort. 403 404 Example: 405 sort (a: b: a < b) [ 5 3 7 ] 406 => [ 3 5 7 ] 407 */ 408 sort = builtins.sort or ( 409 strictLess: list: 410 let 411 len = length list; 412 first = head list; 413 pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (n + 1); in 414 if n == len 415 then acc 416 else if strictLess first el 417 then next { inherit left; right = [ el ] ++ right; } 418 else 419 next { left = [ el ] ++ left; inherit right; }; 420 pivot = pivot' 1 { left = []; right = []; }; 421 in 422 if len < 2 then list 423 else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right)); 424 425 /* Compare two lists element-by-element. 426 427 Example: 428 compareLists compare [] [] 429 => 0 430 compareLists compare [] [ "a" ] 431 => -1 432 compareLists compare [ "a" ] [] 433 => 1 434 compareLists compare [ "a" "b" ] [ "a" "c" ] 435 => 1 436 */ 437 compareLists = cmp: a: b: 438 if a == [] 439 then if b == [] 440 then 0 441 else -1 442 else if b == [] 443 then 1 444 else let rel = cmp (head a) (head b); in 445 if rel == 0 446 then compareLists cmp (tail a) (tail b) 447 else rel; 448 449 /* Sort list using "Natural sorting". 450 Numeric portions of strings are sorted in numeric order. 451 452 Example: 453 naturalSort ["disk11" "disk8" "disk100" "disk9"] 454 => ["disk8" "disk9" "disk11" "disk100"] 455 naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"] 456 => ["10.5.16.62" "10.46.133.149" "10.54.16.25"] 457 naturalSort ["v0.2" "v0.15" "v0.0.9"] 458 => [ "v0.0.9" "v0.2" "v0.15" ] 459 */ 460 naturalSort = lst: 461 let 462 vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s); 463 prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits 464 less = a: b: (compareLists compare (head a) (head b)) < 0; 465 in 466 map (x: elemAt x 1) (sort less prepared); 467 468 /* Return the first (at most) N elements of a list. 469 470 Example: 471 take 2 [ "a" "b" "c" "d" ] 472 => [ "a" "b" ] 473 take 2 [ ] 474 => [ ] 475 */ 476 take = count: sublist 0 count; 477 478 /* Remove the first (at most) N elements of a list. 479 480 Example: 481 drop 2 [ "a" "b" "c" "d" ] 482 => [ "c" "d" ] 483 drop 2 [ ] 484 => [ ] 485 */ 486 drop = count: list: sublist count (length list) list; 487 488 /* Return a list consisting of at most count elements of list, 489 starting at index start. 490 491 Example: 492 sublist 1 3 [ "a" "b" "c" "d" "e" ] 493 => [ "b" "c" "d" ] 494 sublist 1 3 [ ] 495 => [ ] 496 */ 497 sublist = start: count: list: 498 let len = length list; in 499 genList 500 (n: elemAt list (n + start)) 501 (if start >= len then 0 502 else if start + count > len then len - start 503 else count); 504 505 /* Return the last element of a list. 506 507 Example: 508 last [ 1 2 3 ] 509 => 3 510 */ 511 last = list: 512 assert list != []; elemAt list (length list - 1); 513 514 /* Return all elements but the last 515 516 Example: 517 init [ 1 2 3 ] 518 => [ 1 2 ] 519 */ 520 init = list: assert list != []; take (length list - 1) list; 521 522 523 /* return the image of the cross product of some lists by a function 524 525 Example: 526 crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]] 527 => [ "13" "14" "23" "24" ] 528 */ 529 crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f]; 530 531 532 /* Remove duplicate elements from the list. O(n^2) complexity. 533 534 Example: 535 536 unique [ 3 2 3 4 ] 537 => [ 3 2 4 ] 538 */ 539 unique = list: 540 if list == [] then 541 [] 542 else 543 let 544 x = head list; 545 xs = unique (drop 1 list); 546 in [x] ++ remove x xs; 547 548 /* Intersects list 'e' and another list. O(nm) complexity. 549 550 Example: 551 intersectLists [ 1 2 3 ] [ 6 3 2 ] 552 => [ 3 2 ] 553 */ 554 intersectLists = e: filter (x: elem x e); 555 556 /* Subtracts list 'e' from another list. O(nm) complexity. 557 558 Example: 559 subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ] 560 => [ 1 4 5 ] 561 */ 562 subtractLists = e: filter (x: !(elem x e)); 563 564 /* Test if two lists have no common element. 565 It should be slightly more efficient than (intersectLists a b == []) 566 */ 567 mutuallyExclusive = a: b: 568 (builtins.length a) == 0 || 569 (!(builtins.elem (builtins.head a) b) && 570 mutuallyExclusive (builtins.tail a) b); 571 572}