You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

513 lines
14 KiB

  1. # General list operations.
  2. { lib }:
  3. with lib.trivial;
  4. rec {
  5. inherit (builtins) head tail length isList elemAt concatLists filter elem genList;
  6. /* Create a list consisting of a single element. `singleton x' is
  7. sometimes more convenient with respect to indentation than `[x]'
  8. when x spans multiple lines.
  9. Example:
  10. singleton "foo"
  11. => [ "foo" ]
  12. */
  13. singleton = x: [x];
  14. /* “right fold” a binary function `op' between successive elements of
  15. `list' with `nul' as the starting value, i.e.,
  16. `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'.
  17. Type:
  18. foldr :: (a -> b -> b) -> b -> [a] -> b
  19. Example:
  20. concat = foldr (a: b: a + b) "z"
  21. concat [ "a" "b" "c" ]
  22. => "abcz"
  23. # different types
  24. strange = foldr (int: str: toString (int + 1) + str) "a"
  25. strange [ 1 2 3 4 ]
  26. => "2345a"
  27. */
  28. foldr = op: nul: list:
  29. let
  30. len = length list;
  31. fold' = n:
  32. if n == len
  33. then nul
  34. else op (elemAt list n) (fold' (n + 1));
  35. in fold' 0;
  36. /* `fold' is an alias of `foldr' for historic reasons */
  37. # FIXME(Profpatsch): deprecate?
  38. fold = foldr;
  39. /* “left fold”, like `foldr', but from the left:
  40. `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`.
  41. Type:
  42. foldl :: (b -> a -> b) -> b -> [a] -> b
  43. Example:
  44. lconcat = foldl (a: b: a + b) "z"
  45. lconcat [ "a" "b" "c" ]
  46. => "zabc"
  47. # different types
  48. lstrange = foldl (str: int: str + toString (int + 1)) ""
  49. strange [ 1 2 3 4 ]
  50. => "a2345"
  51. */
  52. foldl = op: nul: list:
  53. let
  54. len = length list;
  55. foldl' = n:
  56. if n == -1
  57. then nul
  58. else op (foldl' (n - 1)) (elemAt list n);
  59. in foldl' (length list - 1);
  60. /* Strict version of `foldl'.
  61. The difference is that evaluation is forced upon access. Usually used
  62. with small whole results (in contract with lazily-generated list or large
  63. lists where only a part is consumed.)
  64. */
  65. foldl' = builtins.foldl' or foldl;
  66. /* Map with index starting from 0
  67. Example:
  68. imap0 (i: v: "${v}-${toString i}") ["a" "b"]
  69. => [ "a-0" "b-1" ]
  70. */
  71. imap0 = f: list: genList (n: f n (elemAt list n)) (length list);
  72. /* Map with index starting from 1
  73. Example:
  74. imap1 (i: v: "${v}-${toString i}") ["a" "b"]
  75. => [ "a-1" "b-2" ]
  76. */
  77. imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
  78. /* Map and concatenate the result.
  79. Example:
  80. concatMap (x: [x] ++ ["z"]) ["a" "b"]
  81. => [ "a" "z" "b" "z" ]
  82. */
  83. concatMap = f: list: concatLists (map f list);
  84. /* Flatten the argument into a single list; that is, nested lists are
  85. spliced into the top-level lists.
  86. Example:
  87. flatten [1 [2 [3] 4] 5]
  88. => [1 2 3 4 5]
  89. flatten 1
  90. => [1]
  91. */
  92. flatten = x:
  93. if isList x
  94. then concatMap (y: flatten y) x
  95. else [x];
  96. /* Remove elements equal to 'e' from a list. Useful for buildInputs.
  97. Example:
  98. remove 3 [ 1 3 4 3 ]
  99. => [ 1 4 ]
  100. */
  101. remove = e: filter (x: x != e);
  102. /* Find the sole element in the list matching the specified
  103. predicate, returns `default' if no such element exists, or
  104. `multiple' if there are multiple matching elements.
  105. Example:
  106. findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ]
  107. => "multiple"
  108. findSingle (x: x == 3) "none" "multiple" [ 1 3 ]
  109. => 3
  110. findSingle (x: x == 3) "none" "multiple" [ 1 9 ]
  111. => "none"
  112. */
  113. findSingle = pred: default: multiple: list:
  114. let found = filter pred list; len = length found;
  115. in if len == 0 then default
  116. else if len != 1 then multiple
  117. else head found;
  118. /* Find the first element in the list matching the specified
  119. predicate or returns `default' if no such element exists.
  120. Example:
  121. findFirst (x: x > 3) 7 [ 1 6 4 ]
  122. => 6
  123. findFirst (x: x > 9) 7 [ 1 6 4 ]
  124. => 7
  125. */
  126. findFirst = pred: default: list:
  127. let found = filter pred list;
  128. in if found == [] then default else head found;
  129. /* Return true iff function `pred' returns true for at least element
  130. of `list'.
  131. Example:
  132. any isString [ 1 "a" { } ]
  133. => true
  134. any isString [ 1 { } ]
  135. => false
  136. */
  137. any = builtins.any or (pred: foldr (x: y: if pred x then true else y) false);
  138. /* Return true iff function `pred' returns true for all elements of
  139. `list'.
  140. Example:
  141. all (x: x < 3) [ 1 2 ]
  142. => true
  143. all (x: x < 3) [ 1 2 3 ]
  144. => false
  145. */
  146. all = builtins.all or (pred: foldr (x: y: if pred x then y else false) true);
  147. /* Count how many times function `pred' returns true for the elements
  148. of `list'.
  149. Example:
  150. count (x: x == 3) [ 3 2 3 4 6 ]
  151. => 2
  152. */
  153. count = pred: foldl' (c: x: if pred x then c + 1 else c) 0;
  154. /* Return a singleton list or an empty list, depending on a boolean
  155. value. Useful when building lists with optional elements
  156. (e.g. `++ optional (system == "i686-linux") flashplayer').
  157. Example:
  158. optional true "foo"
  159. => [ "foo" ]
  160. optional false "foo"
  161. => [ ]
  162. */
  163. optional = cond: elem: if cond then [elem] else [];
  164. /* Return a list or an empty list, depending on a boolean value.
  165. Example:
  166. optionals true [ 2 3 ]
  167. => [ 2 3 ]
  168. optionals false [ 2 3 ]
  169. => [ ]
  170. */
  171. optionals = cond: elems: if cond then elems else [];
  172. /* If argument is a list, return it; else, wrap it in a singleton
  173. list. If you're using this, you should almost certainly
  174. reconsider if there isn't a more "well-typed" approach.
  175. Example:
  176. toList [ 1 2 ]
  177. => [ 1 2 ]
  178. toList "hi"
  179. => [ "hi "]
  180. */
  181. toList = x: if isList x then x else [x];
  182. /* Return a list of integers from `first' up to and including `last'.
  183. Example:
  184. range 2 4
  185. => [ 2 3 4 ]
  186. range 3 2
  187. => [ ]
  188. */
  189. range = first: last:
  190. if first > last then
  191. []
  192. else
  193. genList (n: first + n) (last - first + 1);
  194. /* Splits the elements of a list in two lists, `right' and
  195. `wrong', depending on the evaluation of a predicate.
  196. Example:
  197. partition (x: x > 2) [ 5 1 2 3 4 ]
  198. => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; }
  199. */
  200. partition = builtins.partition or (pred:
  201. foldr (h: t:
  202. if pred h
  203. then { right = [h] ++ t.right; wrong = t.wrong; }
  204. else { right = t.right; wrong = [h] ++ t.wrong; }
  205. ) { right = []; wrong = []; });
  206. /* Merges two lists of the same size together. If the sizes aren't the same
  207. the merging stops at the shortest. How both lists are merged is defined
  208. by the first argument.
  209. Example:
  210. zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"]
  211. => ["he" "lo"]
  212. */
  213. zipListsWith = f: fst: snd:
  214. genList
  215. (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
  216. /* Merges two lists of the same size together. If the sizes aren't the same
  217. the merging stops at the shortest.
  218. Example:
  219. zipLists [ 1 2 ] [ "a" "b" ]
  220. => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ]
  221. */
  222. zipLists = zipListsWith (fst: snd: { inherit fst snd; });
  223. /* Reverse the order of the elements of a list.
  224. Example:
  225. reverseList [ "b" "o" "j" ]
  226. => [ "j" "o" "b" ]
  227. */
  228. reverseList = xs:
  229. let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
  230. /* Depth-First Search (DFS) for lists `list != []`.
  231. `before a b == true` means that `b` depends on `a` (there's an
  232. edge from `b` to `a`).
  233. Examples:
  234. listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ]
  235. == { minimal = "/"; # minimal element
  236. visited = [ "/home/user" ]; # seen elements (in reverse order)
  237. rest = [ "/home" "other" ]; # everything else
  238. }
  239. listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
  240. == { cycle = "/"; # cycle encountered at this element
  241. loops = [ "/" ]; # and continues to these elements
  242. visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order)
  243. rest = [ "/home" "other" ]; # everything else
  244. */
  245. listDfs = stopOnCycles: before: list:
  246. let
  247. dfs' = us: visited: rest:
  248. let
  249. c = filter (x: before x us) visited;
  250. b = partition (x: before x us) rest;
  251. in if stopOnCycles && (length c > 0)
  252. then { cycle = us; loops = c; inherit visited rest; }
  253. else if length b.right == 0
  254. then # nothing is before us
  255. { minimal = us; inherit visited rest; }
  256. else # grab the first one before us and continue
  257. dfs' (head b.right)
  258. ([ us ] ++ visited)
  259. (tail b.right ++ b.wrong);
  260. in dfs' (head list) [] (tail list);
  261. /* Sort a list based on a partial ordering using DFS. This
  262. implementation is O(N^2), if your ordering is linear, use `sort`
  263. instead.
  264. `before a b == true` means that `b` should be after `a`
  265. in the result.
  266. Examples:
  267. toposort hasPrefix [ "/home/user" "other" "/" "/home" ]
  268. == { result = [ "/" "/home" "/home/user" "other" ]; }
  269. toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ]
  270. == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle
  271. loops = [ "/" ]; } # loops back to these elements
  272. toposort hasPrefix [ "other" "/home/user" "/home" "/" ]
  273. == { result = [ "other" "/" "/home" "/home/user" ]; }
  274. toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; }
  275. */
  276. toposort = before: list:
  277. let
  278. dfsthis = listDfs true before list;
  279. toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
  280. in
  281. if length list < 2
  282. then # finish
  283. { result = list; }
  284. else if dfsthis ? "cycle"
  285. then # there's a cycle, starting from the current vertex, return it
  286. { cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
  287. inherit (dfsthis) loops; }
  288. else if toporest ? "cycle"
  289. then # there's a cycle somewhere else in the graph, return it
  290. toporest
  291. # Slow, but short. Can be made a bit faster with an explicit stack.
  292. else # there are no cycles
  293. { result = [ dfsthis.minimal ] ++ toporest.result; };
  294. /* Sort a list based on a comparator function which compares two
  295. elements and returns true if the first argument is strictly below
  296. the second argument. The returned list is sorted in an increasing
  297. order. The implementation does a quick-sort.
  298. Example:
  299. sort (a: b: a < b) [ 5 3 7 ]
  300. => [ 3 5 7 ]
  301. */
  302. sort = builtins.sort or (
  303. strictLess: list:
  304. let
  305. len = length list;
  306. first = head list;
  307. pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (n + 1); in
  308. if n == len
  309. then acc
  310. else if strictLess first el
  311. then next { inherit left; right = [ el ] ++ right; }
  312. else
  313. next { left = [ el ] ++ left; inherit right; };
  314. pivot = pivot' 1 { left = []; right = []; };
  315. in
  316. if len < 2 then list
  317. else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right));
  318. /* Compare two lists element-by-element.
  319. Example:
  320. compareLists compare [] []
  321. => 0
  322. compareLists compare [] [ "a" ]
  323. => -1
  324. compareLists compare [ "a" ] []
  325. => 1
  326. compareLists compare [ "a" "b" ] [ "a" "c" ]
  327. => 1
  328. */
  329. compareLists = cmp: a: b:
  330. if a == []
  331. then if b == []
  332. then 0
  333. else -1
  334. else if b == []
  335. then 1
  336. else let rel = cmp (head a) (head b); in
  337. if rel == 0
  338. then compareLists cmp (tail a) (tail b)
  339. else rel;
  340. /* Return the first (at most) N elements of a list.
  341. Example:
  342. take 2 [ "a" "b" "c" "d" ]
  343. => [ "a" "b" ]
  344. take 2 [ ]
  345. => [ ]
  346. */
  347. take = count: sublist 0 count;
  348. /* Remove the first (at most) N elements of a list.
  349. Example:
  350. drop 2 [ "a" "b" "c" "d" ]
  351. => [ "c" "d" ]
  352. drop 2 [ ]
  353. => [ ]
  354. */
  355. drop = count: list: sublist count (length list) list;
  356. /* Return a list consisting of at most ‘count’ elements of ‘list’,
  357. starting at index ‘start’.
  358. Example:
  359. sublist 1 3 [ "a" "b" "c" "d" "e" ]
  360. => [ "b" "c" "d" ]
  361. sublist 1 3 [ ]
  362. => [ ]
  363. */
  364. sublist = start: count: list:
  365. let len = length list; in
  366. genList
  367. (n: elemAt list (n + start))
  368. (if start >= len then 0
  369. else if start + count > len then len - start
  370. else count);
  371. /* Return the last element of a list.
  372. Example:
  373. last [ 1 2 3 ]
  374. => 3
  375. */
  376. last = list:
  377. assert list != []; elemAt list (length list - 1);
  378. /* Return all elements but the last
  379. Example:
  380. init [ 1 2 3 ]
  381. => [ 1 2 ]
  382. */
  383. init = list: assert list != []; take (length list - 1) list;
  384. /* FIXME(zimbatm) Not used anywhere
  385. */
  386. crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
  387. /* Remove duplicate elements from the list. O(n^2) complexity.
  388. Example:
  389. unique [ 3 2 3 4 ]
  390. => [ 3 2 4 ]
  391. */
  392. unique = list:
  393. if list == [] then
  394. []
  395. else
  396. let
  397. x = head list;
  398. xs = unique (drop 1 list);
  399. in [x] ++ remove x xs;
  400. /* Intersects list 'e' and another list. O(nm) complexity.
  401. Example:
  402. intersectLists [ 1 2 3 ] [ 6 3 2 ]
  403. => [ 3 2 ]
  404. */
  405. intersectLists = e: filter (x: elem x e);
  406. /* Subtracts list 'e' from another list. O(nm) complexity.
  407. Example:
  408. subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ]
  409. => [ 1 4 5 ]
  410. */
  411. subtractLists = e: filter (x: !(elem x e));
  412. /* Test if two lists have no common element.
  413. It should be slightly more efficient than (intersectLists a b == [])
  414. */
  415. mutuallyExclusive = a: b:
  416. (builtins.length a) == 0 ||
  417. (!(builtins.elem (builtins.head a) b) &&
  418. mutuallyExclusive (builtins.tail a) b);
  419. }