Dynamic arrays gave you growth; this page gives
you order — and its opposite, on purpose. The arrays unit sorts and
copies pieces; the random unit shuffles and rolls, and its disorder is
the reproducible kind: seeded, defined by pure integer arithmetic, the same
on every machine at every tier.
The example is examples/OrderAndDisorder
in the tutorial repository.
Getting the file
make -C mica-container/examples/OrderAndDisorder runOrderAndDisorder
sorted 1 3 4 7 8 9
middle three 4 7 8
shuffled(7) 3 9 1 4 8 7 - sum still 32
three dice 4 5 1Run it on another machine, another architecture, another optimization tier: the shuffled line and the dice are the same. That is the page’s headline, and it is not an accident.
The example, walked
Sort: the guarantees a systems library owes
Sort(address ns);Sort is a native generic heapsort over any dynamic array whose elements
are ordered. The comparison is the element type’s own < — the generics
machinery clones the routine per element type and compares in line, so
there is no comparator to hand over and nothing the type checker cannot
see. The properties are the ones worth promising: it sorts in place with
no allocation at all, so it works unchanged where no heap exists; its
worst case equals its best at O(n log n), so no adversarial input degrades
it; and its stack use is a handful of locals however large the array grows.
Sort does not keep equal elements in the order they had. Over numbers and
strings that is seldom visible, because equal values look alike; it matters
the moment records sort by one of their fields, which is what the next
section shows.
Sorting records by a key, and keeping equal keys in order
A record has no order of its own, so a sort by one of its fields takes that
field’s values as a key array: one key per element, at the same position,
filled by your own loop. SortByKeys orders the keys and carries every
element along with its key, in place and with no allocation, by the same
heapsort as Sort. StableSortByKeys does the same and keeps the elements
of equal keys in the order they had, so a roster already in name order stays
in name order inside each age:
{ Roster sorts people by age and keeps the people of one age in name order. }
program Roster;
imp
WriteLn, Append, Length : std;
StableSortByKeys, List, SortList : arrays;
type
{ a person is a name and an age }
Person = record
name : string;
age : int64;
end;
var
people : List of Person;
ages : SortList of int64;
i : int64;
{ The roster grows by one person. }
procedure Add(name : string, age : int64);
var
person : Person;
begin
person.name := name;
person.age := age;
Append(address people, person);
end;
begin
Add("Ada", 36);
Add("Barbara", 60);
Add("Charles", 36);
Add("Edsger", 60);
Add("Grace", 85);
{ the key array holds each person's age at that person's position }
for i := 0 to Length(people) - 1 do begin
Append(address ages, people[i].age);
end;
StableSortByKeys(address people, address ages);
for i := 0 to Length(people) - 1 do begin
WriteLn("%lld %ls", people[i].age, people[i].name);
end;
end.Run it with mica Roster.mica:
36 Ada
36 Charles
60 Barbara
60 Edsger
85 GraceAfter the sort the keys ascend and each one still stands beside its own
person, so a second sort by another field takes a new key array filled from
the sorted records. StableSort is the stable twin of Sort for an array
of ordered elements. The stable sorts are merge sorts, and their price is
memory: one buffer of half the array’s elements (and of half its keys),
taken when the sort starts and given back when it returns, and none for an
array of sixteen elements or fewer. Their worst case is O(n log n) like
Sort’s, and an array already in order costs one comparison per merge.
Where memory has to stay flat, Sort and SortByKeys are the in-place
road.
SubArray: the named piece
SubArray(address ns, 2, 3, address piece);Elements 2, 3 and 4, copied into a list the caller owns — the value rule’s spelling of a slice, consistent with everything the slicing page taught: a piece you name is a copy you own.
Shuffle: disorder you can rerun
rng := Seeded(7);
Shuffle(address ns, address rng);The generator behind Shuffle is seeded and deterministic: its whole
definition is integer arithmetic, so the same seed yields the same
sequence on every architecture at every tier — the determinism the rest of
the language promises, extended to randomness. A repeatable test, a
simulation you can bisect, a training run you can reproduce: all of them
want exactly this. The state is a plain value you own and pass by
address, the same visible-mutation marker as everywhere else — no
ambient generator, no hidden global to make two tests interfere.
RangeInt draws from an inclusive range, Float from [0, 1), Next
answers raw words. And the printed sum beside the shuffled line is the
completeness check in this page’s spirit: rearranged, never lost.
One boundary, stated plainly: this generator is for simulation, testing and games. It is deliberately not cryptographic — key material comes from the platform’s own entropy door, not from here.
Try it
1. Change the seed. Seeded(8) — a different order, equally fixed.
Rerun twice; it does not budge. Then put 7 back and watch the page’s
exact line return.
2. Sort the shuffle. Add a second Sort(address ns); after the
shuffle and print again: 1 3 4 7 8 9. Order and disorder are inverse
verbs over one value, which is the page’s quiet point — neither is special,
both are yours.
Next
Dynamic arrays for the container these verbs serve, and WYSIWYG slicing for pieces of shaped values. Every example lives in the tutorial repository.