Skip to content

Commit 3de64ba

Browse files
authored
feat(stdlib): Optimize List.init to support large lists (#2249)
1 parent 3186155 commit 3de64ba

File tree

3 files changed

+59
-52
lines changed

3 files changed

+59
-52
lines changed

compiler/test/stdlib/list.test.gr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,13 @@ assert sub(0, 2, listSub) == [1, 2]
265265
assert sub(1, 2, listSub) == [2, 3]
266266

267267
// List.init
268+
let largeListSize = 500_000
269+
let mut largeList = []
270+
for (let mut i = largeListSize - 1; i >= 0; i -= 1) {
271+
largeList = [i, ...largeList]
272+
}
268273

274+
assert init(largeListSize, i => i) == largeList
269275
assert init(4, idx => idx) == [0, 1, 2, 3]
270276
assert init(0, idx => fail "Shouldn't be called") == []
271277
assert init(-10, idx => fail "Shouldn't be called") == []

stdlib/list.gr

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,24 @@ from "runtime/unsafe/memory" include Memory
1313
from "runtime/unsafe/wasmi32" include WasmI32
1414
from "runtime/dataStructures" include DataStructures
1515

16+
/**
17+
* Creates a new list with all elements in reverse order.
18+
*
19+
* @param list: The list to reverse
20+
* @returns The new list
21+
*
22+
* @since v0.1.0
23+
*/
24+
provide let reverse = list => {
25+
let rec revHelp = (list, acc) => {
26+
match (list) {
27+
[] => acc,
28+
[first, ...rest] => revHelp(rest, [first, ...acc]),
29+
}
30+
}
31+
revHelp(list, [])
32+
}
33+
1634
/**
1735
* Creates a new list of the specified length where each element is
1836
* initialized with the result of an initializer function. The initializer
@@ -27,17 +45,18 @@ from "runtime/dataStructures" include DataStructures
2745
* @since v0.3.0
2846
*/
2947
provide let init = (length, fn) => {
30-
// This method can be further improved by checking the length against a specific size
31-
// and determining if it can be made tail-recursive, then use List.reverse on it.
32-
// Which would be the same as OCaml does it in https://github.com/ocaml/ocaml/blob/03839754f46319aa36d9dad56940a6f3c3bcb48a/stdlib/list.ml#L79
33-
let rec iter = (idx, length) => {
34-
if (idx >= length) {
35-
[]
36-
} else {
37-
[fn(idx), ...iter(idx + 1, length)]
48+
if (length < 2048) {
49+
// For small lists, use a faster non-tail-recursive version
50+
let rec iter = (idx, length) => {
51+
if (idx >= length) [] else [fn(idx), ...iter(idx + 1, length)]
52+
}
53+
iter(0, length)
54+
} else {
55+
let rec iter = (idx, length, acc) => {
56+
if (idx >= length) acc else iter(idx + 1, length, [fn(idx), ...acc])
3857
}
58+
reverse(iter(0, length, []))
3959
}
40-
iter(0, length)
4160
}
4261

4362
/**
@@ -74,24 +93,6 @@ provide let isEmpty = list => {
7493
}
7594
}
7695

77-
/**
78-
* Creates a new list with all elements in reverse order.
79-
*
80-
* @param list: The list to reverse
81-
* @returns The new list
82-
*
83-
* @since v0.1.0
84-
*/
85-
provide let reverse = list => {
86-
let rec iter = (list, acc) => {
87-
match (list) {
88-
[] => acc,
89-
[first, ...rest] => iter(rest, [first, ...acc]),
90-
}
91-
}
92-
iter(list, [])
93-
}
94-
9596
/**
9697
* Creates a new list with the elements of the first list followed by
9798
* the elements of the second list.

stdlib/list.md

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,31 @@ from "list" include List
2525

2626
Functions and constants included in the List module.
2727

28+
### List.**reverse**
29+
30+
<details disabled>
31+
<summary tabindex="-1">Added in <code>0.1.0</code></summary>
32+
No other changes yet.
33+
</details>
34+
35+
```grain
36+
reverse : (list: List<a>) => List<a>
37+
```
38+
39+
Creates a new list with all elements in reverse order.
40+
41+
Parameters:
42+
43+
|param|type|description|
44+
|-----|----|-----------|
45+
|`list`|`List<a>`|The list to reverse|
46+
47+
Returns:
48+
49+
|type|description|
50+
|----|-----------|
51+
|`List<a>`|The new list|
52+
2853
### List.**init**
2954

3055
<details disabled>
@@ -116,31 +141,6 @@ Returns:
116141
|----|-----------|
117142
|`Bool`|`true` if the list is empty and `false` otherwise|
118143

119-
### List.**reverse**
120-
121-
<details disabled>
122-
<summary tabindex="-1">Added in <code>0.1.0</code></summary>
123-
No other changes yet.
124-
</details>
125-
126-
```grain
127-
reverse : (list: List<a>) => List<a>
128-
```
129-
130-
Creates a new list with all elements in reverse order.
131-
132-
Parameters:
133-
134-
|param|type|description|
135-
|-----|----|-----------|
136-
|`list`|`List<a>`|The list to reverse|
137-
138-
Returns:
139-
140-
|type|description|
141-
|----|-----------|
142-
|`List<a>`|The new list|
143-
144144
### List.**append**
145145

146146
<details disabled>

0 commit comments

Comments
 (0)