Skip to content

Commit c4bf6ba

Browse files
committed
docs: add time complexity to relevant primops
1 parent 3645671 commit c4bf6ba

File tree

1 file changed

+184
-1
lines changed

1 file changed

+184
-1
lines changed

src/libexpr/primops.cc

Lines changed: 184 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2999,6 +2999,12 @@ static RegisterPrimOp primop_attrNames({
29992999
Return the names of the attributes in the set *set* in an
30003000
alphabetically sorted list. For instance, `builtins.attrNames { y
30013001
= 1; x = "foo"; }` evaluates to `[ "x" "y" ]`.
3002+
3003+
# Time Complexity
3004+
3005+
- O(n log n), where:
3006+
3007+
n = number of attributes in the set
30023008
)",
30033009
.fun = prim_attrNames,
30043010
});
@@ -3031,6 +3037,12 @@ static RegisterPrimOp primop_attrValues({
30313037
.doc = R"(
30323038
Return the values of the attributes in the set *set* in the order
30333039
corresponding to the sorted attribute names.
3040+
3041+
# Time Complexity
3042+
3043+
- O(n log n), where:
3044+
3045+
n = number of attributes in the set
30343046
)",
30353047
.fun = prim_attrValues,
30363048
});
@@ -3056,6 +3068,10 @@ static RegisterPrimOp primop_getAttr({
30563068
aborts if the attribute doesn’t exist. This is a dynamic version of
30573069
the `.` operator, since *s* is an expression rather than an
30583070
identifier.
3071+
3072+
# Time Complexity
3073+
3074+
O(log n) where n = number of attributes in the set
30593075
)",
30603076
.fun = prim_getAttr,
30613077
});
@@ -3144,6 +3160,10 @@ static RegisterPrimOp primop_hasAttr({
31443160
`hasAttr` returns `true` if *set* has an attribute named *s*, and
31453161
`false` otherwise. This is a dynamic version of the `?` operator,
31463162
since *s* is an expression rather than an identifier.
3163+
3164+
# Time Complexity
3165+
3166+
O(log n) where n = number of attributes in the set
31473167
)",
31483168
.fun = prim_hasAttr,
31493169
});
@@ -3203,6 +3223,13 @@ static RegisterPrimOp primop_removeAttrs({
32033223
```
32043224
32053225
evaluates to `{ y = 2; }`.
3226+
3227+
# Time Complexity
3228+
3229+
O(n + k log k) where:
3230+
3231+
n = number of attributes in input set
3232+
k = number of attribute names to remove
32063233
)",
32073234
.fun = prim_removeAttrs,
32083235
});
@@ -3290,6 +3317,10 @@ static RegisterPrimOp primop_listToAttrs({
32903317
```nix
32913318
{ foo = 123; bar = 456; }
32923319
```
3320+
3321+
# Time Complexity
3322+
3323+
O(n log n) where n = number of list elements
32933324
)",
32943325
.fun = prim_listToAttrs,
32953326
});
@@ -3366,7 +3397,12 @@ static RegisterPrimOp primop_intersectAttrs({
33663397
Return a set consisting of the attributes in the set *e2* which have the
33673398
same name as some attribute in *e1*.
33683399
3369-
Performs in O(*n* log *m*) where *n* is the size of the smaller set and *m* the larger set's size.
3400+
# Time Complexity
3401+
3402+
O(n * log m) where:
3403+
3404+
n = number of attributes in the smaller set
3405+
m = number of attributes in the larger set
33703406
)",
33713407
.fun = prim_intersectAttrs,
33723408
});
@@ -3406,6 +3442,13 @@ static RegisterPrimOp primop_catAttrs({
34063442
```
34073443
34083444
evaluates to `[1 2]`.
3445+
3446+
# Time Complexity
3447+
3448+
O(n * log m) where:
3449+
3450+
n = number of sets in input list
3451+
m = number of attributes per set
34093452
)",
34103453
.fun = prim_catAttrs,
34113454
});
@@ -3449,6 +3492,10 @@ static RegisterPrimOp primop_functionArgs({
34493492
"Formal argument" here refers to the attributes pattern-matched by
34503493
the function. Plain lambdas are not included, e.g. `functionArgs (x:
34513494
...) = { }`.
3495+
3496+
# Time Complexity
3497+
3498+
O(n) where n = number of formal arguments
34523499
)",
34533500
.fun = prim_functionArgs,
34543501
});
@@ -3481,6 +3528,14 @@ static RegisterPrimOp primop_mapAttrs({
34813528
```
34823529
34833530
evaluates to `{ a = 10; b = 20; }`.
3531+
3532+
# Time Complexity
3533+
3534+
O(n) where:
3535+
3536+
n = number of attributes
3537+
3538+
Calls to `f` are performed afterwards, when needed
34843539
)",
34853540
.fun = prim_mapAttrs,
34863541
});
@@ -3568,6 +3623,15 @@ static RegisterPrimOp primop_zipAttrsWith({
35683623
b = { name = "b"; values = [ "z" ]; };
35693624
}
35703625
```
3626+
3627+
# Time Complexity
3628+
3629+
O(n * k * log k) worst case, where:
3630+
3631+
n = number of attribute sets in input list
3632+
k = number of unique keys across all sets
3633+
3634+
More precisely: O(n * m * log k) where m ≤ k is average number of attributes per set
35713635
)",
35723636
.fun = prim_zipAttrsWith,
35733637
});
@@ -3633,6 +3697,10 @@ static RegisterPrimOp primop_head({
36333697
Return the first element of a list; abort evaluation if the argument
36343698
isn’t a list or is an empty list. You can test whether a list is
36353699
empty by comparing it with `[]`.
3700+
3701+
# Time Complexity
3702+
3703+
O(1)
36363704
)",
36373705
.fun = prim_head,
36383706
});
@@ -3664,6 +3732,10 @@ static RegisterPrimOp primop_tail({
36643732
> This function should generally be avoided since it's inefficient:
36653733
> unlike Haskell's `tail`, it takes O(n) time, so recursing over a
36663734
> list by repeatedly calling `tail` takes O(n^2) time.
3735+
3736+
# Time Complexity
3737+
3738+
O(n) where n = list length (copies n-1 elements)
36673739
)",
36683740
.fun = prim_tail,
36693741
});
@@ -3698,6 +3770,14 @@ static RegisterPrimOp primop_map({
36983770
```
36993771
37003772
evaluates to `[ "foobar" "foobla" "fooabc" ]`.
3773+
3774+
# Time Complexity
3775+
3776+
O(n) where:
3777+
3778+
n = list length
3779+
3780+
Calls to `f` are performed afterwards when needed.
37013781
)",
37023782
.fun = prim_map,
37033783
});
@@ -3747,6 +3827,13 @@ static RegisterPrimOp primop_filter({
37473827
.doc = R"(
37483828
Return a list consisting of the elements of *list* for which the
37493829
function *f* returns `true`.
3830+
3831+
# Time Complexity
3832+
3833+
O(n * T_f) where:
3834+
3835+
n = list length
3836+
T_f = predicate evaluation time
37503837
)",
37513838
.fun = prim_filter,
37523839
});
@@ -3770,6 +3857,15 @@ static RegisterPrimOp primop_elem({
37703857
.doc = R"(
37713858
Return `true` if a value equal to *x* occurs in the list *xs*, and
37723859
`false` otherwise.
3860+
3861+
# Time Complexity
3862+
3863+
O(n * T) (worst case) where:
3864+
3865+
n = list length
3866+
T = time to compare two elements
3867+
3868+
returns early if the elements is found
37733869
)",
37743870
.fun = prim_elem,
37753871
});
@@ -3792,6 +3888,12 @@ static RegisterPrimOp primop_concatLists({
37923888
.args = {"lists"},
37933889
.doc = R"(
37943890
Concatenate a list of lists into a single list.
3891+
3892+
# Time Complexity
3893+
3894+
O(N) where:
3895+
3896+
N = total number of elements across all lists
37953897
)",
37963898
.fun = prim_concatLists,
37973899
});
@@ -3808,6 +3910,10 @@ static RegisterPrimOp primop_length({
38083910
.args = {"e"},
38093911
.doc = R"(
38103912
Return the length of the list *e*.
3913+
3914+
# Time Complexity
3915+
3916+
O(1)
38113917
)",
38123918
.fun = prim_length,
38133919
});
@@ -3851,6 +3957,17 @@ static RegisterPrimOp primop_foldlStrict({
38513957
argument is the current element being processed. The return value
38523958
of each application of `op` is evaluated immediately, even for
38533959
intermediate values.
3960+
3961+
# Time Complexity
3962+
3963+
O(n * T_op) where:
3964+
3965+
n = list length
3966+
T_op = `op` call evaluation time
3967+
3968+
Note: Because foldl' evaluates results only to [WHNF](@docroot@/language/evaluation.md#values),
3969+
deeper parts of values returned by op may be evaluated later.
3970+
Their cost (and errors) therefore may not clearly belong to T_op or to foldl' itself.
38543971
)",
38553972
.fun = prim_foldlStrict,
38563973
});
@@ -3889,6 +4006,15 @@ static RegisterPrimOp primop_any({
38894006
.doc = R"(
38904007
Return `true` if the function *pred* returns `true` for at least one
38914008
element of *list*, and `false` otherwise.
4009+
4010+
# Time Complexity
4011+
4012+
O(n * T_pred) where:
4013+
4014+
- n = `list` length
4015+
- T_pred = `pred` call evaluation time
4016+
4017+
returns early when `pred` returns `true`
38924018
)",
38934019
.fun = prim_any,
38944020
});
@@ -3904,6 +4030,13 @@ static RegisterPrimOp primop_all({
39044030
.doc = R"(
39054031
Return `true` if the function *pred* returns `true` for all elements
39064032
of *list*, and `false` otherwise.
4033+
4034+
# Time Complexity
4035+
4036+
O(n * T_f) where:
4037+
4038+
- n = list length
4039+
- T_f = predicate evaluation time
39074040
)",
39084041
.fun = prim_all,
39094042
});
@@ -3942,6 +4075,14 @@ static RegisterPrimOp primop_genList({
39424075
```
39434076
39444077
returns the list `[ 0 1 4 9 16 ]`.
4078+
4079+
# Time Complexity
4080+
4081+
O(n) where:
4082+
4083+
n = requested length.
4084+
4085+
Calls to `generator` are performed afterwards, when needed.
39454086
)",
39464087
.fun = prim_genList,
39474088
});
@@ -4036,6 +4177,13 @@ static RegisterPrimOp primop_sort({
40364177
40374178
If the *comparator* violates any of these properties, then `builtins.sort`
40384179
reorders elements in an unspecified manner.
4180+
4181+
# Time Complexity
4182+
4183+
O(n log n * T_cmp), where:
4184+
4185+
n = `list` length
4186+
T_cmp = `comparator` call evaluation time
40394187
)",
40404188
.fun = prim_sort,
40414189
});
@@ -4097,6 +4245,13 @@ static RegisterPrimOp primop_partition({
40974245
```nix
40984246
{ right = [ 23 42 ]; wrong = [ 1 9 3 ]; }
40994247
```
4248+
4249+
# Time Complexity
4250+
4251+
O(n * T_pred) where:
4252+
4253+
n = list length
4254+
T_pred = `pred` call evaluation time
41004255
)",
41014256
.fun = prim_partition,
41024257
});
@@ -4150,6 +4305,14 @@ static RegisterPrimOp primop_groupBy({
41504305
```nix
41514306
{ b = [ "bar" "baz" ]; f = [ "foo" ]; }
41524307
```
4308+
4309+
# Time Complexity
4310+
4311+
O(N * T_f + N * log k) where:
4312+
4313+
N = number of `list` elements
4314+
T_f = `f` call evaluation time
4315+
k = number of unique groups
41534316
)",
41544317
.fun = prim_groupBy,
41554318
});
@@ -4192,6 +4355,14 @@ static RegisterPrimOp primop_concatMap({
41924355
.doc = R"(
41934356
This function is equivalent to `builtins.concatLists (map f list)`
41944357
but is more efficient.
4358+
4359+
# Time Complexity
4360+
4361+
O(k * T_f + N) where:
4362+
4363+
k = length of input list
4364+
T_f = time to call `f` on an element
4365+
N = total number of elements returned by `f` calls
41954366
)",
41964367
.fun = prim_concatMap,
41974368
});
@@ -4888,6 +5059,10 @@ static RegisterPrimOp primop_concatStringsSep({
48885059
Concatenate a list of strings with a separator between each
48895060
element, e.g. `concatStringsSep "/" ["usr" "local" "bin"] ==
48905061
"usr/local/bin"`.
5062+
5063+
# Time Complexity
5064+
5065+
O(n) where n = total length of output string
48915066
)",
48925067
.fun = prim_concatStringsSep,
48935068
});
@@ -4972,6 +5147,14 @@ static RegisterPrimOp primop_replaceStrings({
49725147
```
49735148
49745149
evaluates to `"fabir"`.
5150+
5151+
# Time Complexity
5152+
5153+
O(n * k * c) where:
5154+
5155+
n = length of input string
5156+
k = number of replacement patterns
5157+
c = average length of patterns in 'from' list
49755158
)",
49765159
.fun = prim_replaceStrings,
49775160
});

0 commit comments

Comments
 (0)