The Scala/Clojure approach for persistent arrays works great with our `reset/reuse`. We seem to be much more efficient than their implementations because of `reset/reuse`. The new approach also seems better than the old one implemented in the runtime, and has a few advantages: 1- The reroot procedure used in the old approach required synchronization for multi-threaded code, or we would need to perform deep copies when sending `parray` objects between threads. 2- We don't need any runtime extension for the new approach. 3- The old approach used "trail lists" for undoing array updates. This works well for bactracking search use cases, but it is bad in use cases where we are simultaneously updating the persistent arrays that have shared nodes.
28 lines
779 B
Text
28 lines
779 B
Text
import init.data.persistentarray
|
|
|
|
abbrev MyArray := PersistentArray Nat
|
|
-- abbrev MyArray := Array Nat
|
|
|
|
def mkMyArray (n : Nat) : MyArray :=
|
|
n.fold (λ i s, s.push i) { PersistentArray . }
|
|
-- n.fold (λ i s, s.push i) Array.empty
|
|
|
|
def check (n : Nat) (p : Nat → Nat → Bool) (s : MyArray) : IO Unit :=
|
|
n.mfor $ λ i, unless (p i (s.get i)) (throw (IO.userError ("failed at " ++ toString i ++ " " ++ toString (s.get i))))
|
|
|
|
def inc1 (n : Nat) (s : MyArray) : MyArray :=
|
|
n.fold (λ i s, s.set i (s.get i + 1)) s
|
|
|
|
def checkId (n : Nat) (s : MyArray) : IO Unit :=
|
|
check n (==) s
|
|
|
|
def main (xs : List String) : IO Unit :=
|
|
do
|
|
let n := xs.head.toNat,
|
|
let t := mkMyArray n,
|
|
checkId n t,
|
|
let t := inc1 n t,
|
|
check n (λ i v, v == i + 1) t,
|
|
IO.println t.size,
|
|
IO.println t.stats,
|
|
pure ()
|