Skip to content

Commit dad3ccf

Browse files
committed
* added several slicing combinators and some tests for them
1 parent 2145d36 commit dad3ccf

9 files changed

Lines changed: 371 additions & 10 deletions

File tree

RELEASE_NOTES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### 1.2.7
2+
* several AList bugfixes
3+
* added AList slicing utilities
4+
15
### 1.2.6
26
* added `AMap.choose2(V)` and derived combinators (`intersectWith`, `intersect(V)`)
37

src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,68 @@ module internal AdaptiveIndexListImplementation =
12711271
|> IndexListDelta
12721272

12731273

1274+
[<Sealed>]
1275+
type SkipReader<'a>(input : alist<'a>, offset : aval<int>) =
1276+
inherit AbstractReader<IndexListDelta<'a>>(IndexListDelta.empty)
1277+
1278+
let reader = input.GetReader()
1279+
let mutable state = MapExt.empty
1280+
let mutable minIndex = Index.zero
1281+
1282+
override x.Compute(token : AdaptiveToken) =
1283+
let ops = reader.GetChanges token
1284+
let offset = offset.GetValue token
1285+
1286+
if MapExt.isEmpty state then
1287+
let part = reader.State.Content.SliceAt(offset, reader.State.Count - 1)
1288+
state <- part
1289+
if not (MapExt.isEmpty state) then
1290+
minIndex <- MapExt.minKey state
1291+
part |> MapExt.map (fun _ v -> Set v) |> IndexListDelta
1292+
1293+
else
1294+
let part = reader.State.Content.SliceAt(offset, reader.State.Count - 1)
1295+
if MapExt.isEmpty part then
1296+
let delta =
1297+
state
1298+
|> MapExt.map (fun _ _ -> Remove)
1299+
|> IndexListDelta
1300+
state <- MapExt.empty
1301+
minIndex <- Index.zero
1302+
delta
1303+
else
1304+
let newMin = MapExt.minKey part
1305+
let newMax = MapExt.maxKey part
1306+
1307+
if newMax < minIndex then
1308+
let ops =
1309+
MapExt.union
1310+
(state |> MapExt.map (fun _ _ -> Remove))
1311+
(part |> MapExt.map (fun _ v -> Set v))
1312+
state <- part
1313+
minIndex <- newMin
1314+
IndexListDelta ops
1315+
else
1316+
1317+
let mutable delta = ops.Content.[newMin .. newMax]
1318+
1319+
1320+
let lDelta =
1321+
if minIndex < newMin then
1322+
reader.State.Content.WithMin(minIndex).WithMaxExclusive(newMin)
1323+
|> MapExt.map (fun _ _ -> Remove)
1324+
elif newMin < minIndex then
1325+
reader.State.Content.WithMin(newMin).WithMaxExclusive(minIndex)
1326+
|> MapExt.map (fun _ v -> Set v)
1327+
else
1328+
MapExt.empty
1329+
1330+
minIndex <- newMin
1331+
state <- part
1332+
MapExt.union lDelta delta
1333+
|> IndexListDelta
1334+
1335+
12741336

12751337

12761338

@@ -1615,6 +1677,22 @@ module AList =
16151677
let sub (offset: int) (count: int) (list: alist<'T>) =
16161678
subA (AVal.constant offset) (AVal.constant count) list
16171679

1680+
let take (count: int) (list: alist<'T>) =
1681+
sub 0 count list
1682+
1683+
let takeA (count: aval<int>) (list: alist<'T>) =
1684+
subA (AVal.constant 0) count list
1685+
1686+
let skipA (count: aval<int>) (list: alist<'T>) =
1687+
if count.IsConstant && list.IsConstant then
1688+
constant (fun () -> IndexList.skip (AVal.force count) (force list))
1689+
else
1690+
ofReader (fun () -> SkipReader(list, count))
1691+
1692+
let skip (count: int) (list: alist<'T>) =
1693+
skipA (AVal.constant count) list
1694+
1695+
16181696

16191697
/// Adaptively reverses the list
16201698
let rev (list: alist<'T>) =
@@ -1808,3 +1886,4 @@ module AList =
18081886
/// Adaptively computes the product of all entries in the list.
18091887
let inline product (s : alist<'a>) =
18101888
reduce (AdaptiveReduction.product()) s
1889+

src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fsi

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,23 @@ module AList =
190190
/// Returns a list of each element tupled with its successor and the last element tupled with the first.
191191
val pairwiseCyclic : list: alist<'T> -> alist<'T * 'T>
192192

193+
/// adaptively skips `offset` elements and takes `count`
193194
val subA : offset : aval<int> -> count : aval<int> -> alist<'T> -> alist<'T>
195+
196+
/// adaptively skips `offset` elements and takes `count`
194197
val sub : offset : int -> count : int -> alist<'T> -> alist<'T>
198+
199+
/// adaptively takes `count` elements
200+
val take : count : int -> alist<'T> -> alist<'T>
201+
202+
/// adaptively takes `count` elements
203+
val takeA : count : aval<int> -> alist<'T> -> alist<'T>
204+
205+
/// adaptively skips `count` elements
206+
val skip : count : int -> alist<'T> -> alist<'T>
207+
208+
/// adaptively skips `count` elements
209+
val skipA : count : aval<int> -> alist<'T> -> alist<'T>
195210

196211
/// Tries to get the element associated to a specific Index from the list.
197212
/// Note that this operation should not be used extensively since its resulting

src/FSharp.Data.Adaptive/CollectionExtensions.fs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,33 @@ open FSharp.Data.Traceable
66
[<AutoOpen>]
77
module CollectionExtensions =
88

9+
type IAdaptiveIndexList<'T> with
10+
member x.GetSlice(min : option<int>, max : option<int>) =
11+
match min with
12+
| Some min ->
13+
match max with
14+
| Some max -> AList.sub min (1 + max - min) x
15+
| None -> AList.skip min x
16+
| None ->
17+
match max with
18+
| Some max -> AList.take (1 + max) x
19+
| None -> x
20+
21+
member x.GetSlice(min : option<aval<int>>, max : option<aval<int>>) =
22+
match min with
23+
| Some min ->
24+
match max with
25+
| Some max ->
26+
let cnt = (min, max) ||> AVal.map2 (fun min max -> 1 + max - min)
27+
AList.subA min cnt x
28+
| None -> AList.skipA min x
29+
| None ->
30+
match max with
31+
| Some max ->
32+
let cnt = max |> AVal.map (fun max -> 1 + max)
33+
AList.takeA cnt x
34+
| None -> x
35+
936
[<AutoOpen>]
1037
module internal Readers =
1138
/// Reader for ASet.sortBy

src/FSharp.Data.Adaptive/Datastructures/IndexList.fs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ type IndexList< [<EqualityConditionalOn>] 'T> internal(l : Index, h : Index, con
130130
if c.IsEmpty then IndexList.Empty
131131
else IndexList(MapExt.minKey c, MapExt.maxKey c, c)
132132

133+
134+
133135
/// Appends the given element to the list.
134136
member x.Add(element : 'T) =
135137
if content.Count = 0 then
@@ -883,6 +885,7 @@ module IndexList =
883885
| struct(ValueSome lmax, ValueSome rmin) ->
884886
IndexList<'T>(list.MinIndex, lmax, l), s, IndexList<'T>(rmin, list.MaxIndex, r)
885887

888+
886889
/// updates or creates the element at the given index.
887890
/// note that out-of-bounds-indices will be ineffective.
888891
let inline setAt (index : int) (v : 'T) (list : IndexList<'T>) =
@@ -959,22 +962,39 @@ module IndexList =
959962
elif index >= list.Count then
960963
list, None, empty
961964
else
962-
let struct(index,_) = list.Content.GetItemV(index)
963-
split index list
965+
let (l, s, r) = list.Content.SplitAt(index)
966+
ofMap l, Option.map snd s, ofMap r
967+
968+
/// splits the list at the given index and returns both (possibly empty) halves and (optional) splitting element.
969+
let splitAti (index : int) (list : IndexList<'T>) =
970+
if index < 0 then
971+
empty, None, list
972+
elif index >= list.Count then
973+
list, None, empty
974+
else
975+
let (l, s, r) = list.Content.SplitAt(index)
976+
ofMap l, s, ofMap r
977+
964978

965-
966-
967979
/// gets the optional min-index used by the list.
968-
let tryFirstIndex (list : IndexList<'T>) = list.Content.TryMinKey()
980+
let tryFirstIndex (list : IndexList<'T>) =
981+
if list.IsEmpty then None
982+
else Some list.MinIndex
969983

970984
/// gets the optional max-index used by the list.
971-
let tryLastIndex (list : IndexList<'T>) = list.Content.TryMaxKey()
985+
let tryLastIndex (list : IndexList<'T>) =
986+
if list.IsEmpty then None
987+
else Some list.MaxIndex
972988

973989
/// gets the min-index used by the list or fails if empty.
974-
let firstIndex (list : IndexList<'T>) = MapExt.minKey list.Content
990+
let firstIndex (list : IndexList<'T>) =
991+
if list.IsEmpty then failwith "empty list"
992+
else list.MinIndex
975993

976994
/// gets the max-index used by the list or fails if empty.
977-
let lastIndex (list : IndexList<'T>) = MapExt.maxKey list.Content
995+
let lastIndex (list : IndexList<'T>) =
996+
if list.IsEmpty then failwith "empty list"
997+
else list.MaxIndex
978998

979999
/// gets the optional first element from the list.
9801000
let tryFirst (list : IndexList<'T>) = list.Content.TryMinValue()
@@ -1115,6 +1135,14 @@ module IndexList =
11151135
let c = list.Content.Skip n
11161136
IndexList<'T>(MapExt.minKey c, list.MaxIndex, c)
11171137

1138+
/// skips `offset` elements and takes `count`
1139+
let sub (offset : int) (count : int) (list : IndexList<'T>) =
1140+
if count <= 0 then empty
1141+
elif offset >= list.Count then empty
1142+
else
1143+
list.Content.SliceAt(offset, offset + count - 1)
1144+
|> ofMap
1145+
11181146
/// creates a list containing a single element.
11191147
let single (v : 'T) =
11201148
let t = Index.after Index.zero

src/FSharp.Data.Adaptive/Datastructures/MapExt.fs

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,32 @@ module internal MapExtImplementation =
12511251
let hasValue = traverse node
12521252
struct(hasValue, left, self, right)
12531253

1254+
1255+
let rec splitAt
1256+
(index : int)
1257+
(node : Node<'Key, 'Value>) =
1258+
if index < 0 then
1259+
struct(null, None, node)
1260+
elif isNull node then
1261+
struct(null, None, null)
1262+
elif node.Height = 1uy then
1263+
if index < 0 then struct(null, None, node)
1264+
elif index > 0 then struct(node, None, null)
1265+
else struct(null, Some(node.Key, node.Value), null)
1266+
else
1267+
let node = node :?> Inner<'Key, 'Value>
1268+
1269+
let lc = count node.Left
1270+
if index = lc then
1271+
struct(node.Left, Some(node.Key, node.Value), node.Right)
1272+
elif index > lc then
1273+
let struct(r0, s, r1) = splitAt (index - lc - 1) node.Right
1274+
struct(binary node.Left node.Key node.Value r0, s, r1)
1275+
else
1276+
let struct(l0, s, l1) = splitAt index node.Left
1277+
struct(l0, s, binary l1 node.Key node.Value node.Right)
1278+
1279+
12541280
let rec private changeWithLeft
12551281
(cmp : IComparer<'Key>)
12561282
(key : 'Key)
@@ -1640,6 +1666,58 @@ module internal MapExtImplementation =
16401666
else (* cMax < 0 *)
16411667
// max smaller than split-key
16421668
slice cmp minKey maxKey node.Left
1669+
1670+
let rec sliceEx (cmp : IComparer<'Key>) (minKey : 'Key) (minInclusive : bool) (maxKey : 'Key) (maxInclusive : bool) (node : Node<'Key, 'Value>) =
1671+
if isNull node then
1672+
node
1673+
elif node.Height = 1uy then
1674+
let cMin = cmp.Compare(minKey, node.Key)
1675+
if cMin < 0 then
1676+
let cMax = cmp.Compare(maxKey, node.Key)
1677+
if cMax > 0 then node
1678+
elif cMax < 0 then null
1679+
elif maxInclusive then node
1680+
else null
1681+
elif cMin > 0 then null
1682+
elif minInclusive then node
1683+
else null
1684+
else
1685+
let node = node :?> Inner<'Key, 'Value>
1686+
let cMin = cmp.Compare(minKey, node.Key)
1687+
let cMax = cmp.Compare(maxKey, node.Key)
1688+
1689+
if cMin < 0 && cMax > 0 then
1690+
// split-key contained
1691+
let left =
1692+
if minInclusive then withMin cmp minKey node.Left
1693+
else let (n,_,_) = withMinExclusiveN cmp minKey node.Left in n
1694+
1695+
let right =
1696+
if maxInclusive then withMax cmp maxKey node.Right
1697+
else let (n,_,_) = withMaxExclusiveN cmp maxKey node.Right in n
1698+
1699+
binary left node.Key node.Value right
1700+
1701+
elif cMin = 0 then
1702+
let right =
1703+
if maxInclusive then withMax cmp maxKey node.Right
1704+
else let (n,_,_) = withMaxExclusiveN cmp maxKey node.Right in n
1705+
if minInclusive then unsafeAddMinimum node.Key node.Value right
1706+
else right
1707+
1708+
elif cMax = 0 then
1709+
let left =
1710+
if minInclusive then withMin cmp minKey node.Left
1711+
else let (n,_,_) = withMinExclusiveN cmp minKey node.Left in n
1712+
if maxInclusive then unsafeAddMaximum node.Key node.Value left
1713+
else left
1714+
1715+
elif cMin > 0 then
1716+
// min larger than split-key
1717+
sliceEx cmp minKey minInclusive maxKey maxInclusive node.Right
1718+
else (* cMax < 0 *)
1719+
// max smaller than split-key
1720+
sliceEx cmp minKey minInclusive maxKey maxInclusive node.Left
16431721

16441722
let rec take (n : int) (node : Node<'Key, 'Value>) =
16451723
if n <= 0 || isNull node then
@@ -3286,9 +3364,15 @@ type internal MapExt<'Key, 'Value when 'Key : comparison>(comparer : IComparer<'
32863364
let n, _, _ = MapExtImplementation.withMaxExclusiveN comparer min root
32873365
MapExt(comparer, n)
32883366

3289-
member x.Slice(min : 'Key, max : 'Key) = MapExt(comparer, MapExtImplementation.slice comparer min max root)
3290-
member x.SliceAt(min : int, max : int) = MapExt(comparer, MapExtImplementation.sliceAt min max root)
3367+
member x.Slice(min : 'Key, max : 'Key) =
3368+
MapExt(comparer, MapExtImplementation.slice comparer min max root)
3369+
3370+
member x.SliceAt(min : int, max : int) =
3371+
MapExt(comparer, MapExtImplementation.sliceAt min max root)
32913372

3373+
member x.Slice(min : 'Key, minInclusive : bool, max : 'Key, maxInclusive : bool) =
3374+
MapExt(comparer, MapExtImplementation.sliceEx comparer min minInclusive max maxInclusive root)
3375+
32923376

32933377
member x.Take(n : int) =
32943378
MapExt(comparer, MapExtImplementation.take n root)
@@ -3310,6 +3394,17 @@ type internal MapExt<'Key, 'Value when 'Key : comparison>(comparer : IComparer<'
33103394
else
33113395
struct(MapExt(comparer, l), ValueNone, MapExt(comparer, r))
33123396

3397+
3398+
member x.SplitAt(index : int) =
3399+
if index < 0 then
3400+
empty, None, x
3401+
elif index >= MapExtImplementation.count root then
3402+
x, None, empty
3403+
else
3404+
let struct(l, s, r) = MapExtImplementation.splitAt index root
3405+
(MapExt(comparer, l), s, MapExt(comparer, r))
3406+
3407+
33133408

33143409

33153410
member x.ReplaceRangeV(min : 'Key, max : 'Key, replace : voption<struct('Key * 'Value)> -> voption<struct('Key * 'Value)> -> struct(voption<'Value> * voption<'Value>)) =

0 commit comments

Comments
 (0)