original ndslice
cached values
array composed of flags that indicates if values are already computed
ndslice, which is internally composed of three ndslices: original, allocated cache and allocated bit-ndslice.
import mir.ndslice.topology: cached, iota, map; import mir.ndslice.allocation: bitSlice, uninitSlice; int[] funCalls; auto v = 5.iota!int .map!((i) { funCalls ~= i; return 2 ^^ i; }); auto flags = v.length.bitSlice; auto cache = v.length.uninitSlice!int; // cached lazy slice: 1 2 4 8 16 auto sl = v.cached(cache, flags); assert(funCalls == []); assert(sl[1] == 2); // remember result assert(funCalls == [1]); assert(sl[1] == 2); // reuse result assert(funCalls == [1]); assert(sl[0] == 1); assert(funCalls == [1, 0]); funCalls = []; // set values directly sl[1 .. 3] = 5; assert(sl[1] == 5); assert(sl[2] == 5); // no function calls assert(funCalls == []);
Cache of immutable elements
import mir.ndslice.slice: DeepElementType; import mir.ndslice.topology: cached, iota, map, as; import mir.ndslice.allocation: bitSlice, uninitSlice; int[] funCalls; auto v = 5.iota!int .map!((i) { funCalls ~= i; return 2 ^^ i; }) .as!(immutable int); auto flags = v.length.bitSlice; auto cache = v.length.uninitSlice!(immutable int); // cached lazy slice: 1 2 4 8 16 auto sl = v.cached(cache, flags); static assert(is(DeepElementType!(typeof(sl)) == immutable int)); assert(funCalls == []); assert(sl[1] == 2); // remember result assert(funCalls == [1]); assert(sl[1] == 2); // reuse result assert(funCalls == [1]); assert(sl[0] == 1); assert(funCalls == [1, 0]);
Creates a random access cache for lazyly computed elements.