List
list<T> is a growable sequence and the workhorse container. Its length grows and shrinks with additions and removals, and elements can be read and written by index.
The lowercase list is a built-in name, usable without any import. The standard library declares the capitalized List<T> (inside the std::Collections module).
Creation
Two ways in: a constructor plus Add, or a collection literal constructed to the target type:
val xs = new list<int>();
xs.Add(3);
xs.Add(1);
xs.Add(2);
print(xs, xs.Count);
val ys: list<int> = [4, 5];[3, 1, 2] 3A collection literal with no annotation is inferred as an array by default; to get a list you must annotate list<T> explicitly.
Add, remove, update
xs.Insert(1, 99);
print(xs);
xs.Set(0, 30);
xs[1] = 10;
print(xs);
xs.Remove(30);
xs.RemoveAt(0);
print(xs);[3, 99, 1, 2]
[30, 10, 1, 2]
[1, 2]Insert(index, value) inserts at an index; Set(index, value) is equivalent to the indexer write xs[index] = value; Remove(value) deletes the first match, RemoveAt(index) deletes by position. Use AddRange(other) for bulk appends and Clear() to empty the list.
Reading and lookup
print(xs[1], xs.Contains(2), xs.IndexOf(2));
print(try? xs.Get(9));2 True 1
nullIndexOf returns -1 on a miss.
Containers expose two ways to read a value:
xs[1] // assertion form: out of range crashes outright; use it when you are sure the index exists
try xs.Get(9) // fallible form: out of range raises IndexOutOfRange; must be consumed with a try form
try? xs.Get(9) // swallow the failure → null
try? xs.Get(9) ?? -1 // or handle the null directly and land on a defaultStandard combinations:
val hp = try? enemies.Get(i)?.Hp ?? 0; // safe read past the end
if try? items.Get(k) is Item it { use(it); } // is-type check + narrowing
val name = try? names.Get(0) ?? "?"; // ?? as the fallbackHigher-order methods
Search, transformation, and aggregation all have ready-made methods that take a lambda:
xs.Insert(0, 30);
xs.Insert(1, 10);
print(xs);
print(xs.Where(n => n > 5), xs.Map(n => n * 2));
print(xs.Filter(n => n % 2 == 0), xs.Reduce((a, b) => a + b, 0));
print(xs.Min(), xs.Max(), xs.Sum());
print(xs.Any(n => n > 100), xs.All(n => n > 0));
print(try? xs.First(n => n > 50));[30, 10, 1, 2]
[30, 10] [60, 20, 2, 4]
[30, 10, 2] 43
1 30 43
False True
nullWhereandFilterare synonyms; both return a new list.Maptransforms element by element;Reducefolds with a seed.First(pred)fails withSeqEmptyon no match.FindIndex(pred)andLastIndexOf(v)each handle one kind of search (both-1on a miss).
The idiom for taking the head of a possibly empty list:
val singles: list<int> = [7];
print(try? singles.First() ?? 0);
val empty = new list<int>();
print(try? empty.First() ?? 0, try? empty.Last() ?? -1);7
0 -1Sorting and rearranging
print(xs.Sorted(), xs.Reversed(), xs.Distinct());
print(xs.Take(2), xs.Skip(1));[1, 2, 10, 30] [2, 1, 10, 30] [30, 10, 1, 2]
[30, 10] [10, 1, 2]Sorted() / Reversed() / Distinct() / Take(n) / Skip(n) all return new lists and leave the original untouched. To sort in place use Sort(), to reverse in place use Reverse().
Joining, zipping, and conversion
print(xs.Join("-"));
val zs: list<int> = [7];
print(xs.Zip(zs));
print(xs.ToArray().Length);30-10-1-2
[(30, 7)]
4Join(separator) glues the elements into a string; Zip(other) pairs two lists position-wise into a list of tuples, with the result as long as the shorter side; ToList() copies into a fresh list, ToArray() converts to a fixed-length array, and ToSet() converts to a set, deduplicating.
Notes
- A printed list is just square brackets and commas:
[1, 2, 3]. for x in xsiterates the elements directly; see the iterator protocol.- Element equality and container equality (value semantics compare by value, reference semantics by identity) are covered in equality and hashing.
