Skip to content

Commit 8aabf53

Browse files
xperiandriclaude
andcommitted
fixup! feat: IndexedList and IIndexedList modules with the vocabulary of the FSharp.Core List module
Follows the reworked ADR 0001 (D2, D5) and the review of #31: - `IndexedList`, `IIndexedList`, their `Option` and `Tuple` modules and `IndexedListBuilder` are the root modules of their files. The `IndexedList<'T>` abbreviation moves to `IndexedList/IndexedListTypes.fs`, which needs no signature file. - `binarySearch`, the sorts of both modules and the sort of the builder compare with the cached `DefaultComparer<'T>.Instance`, so a call no longer creates a comparer. - `IndexedList.rev` reverses a copy of the elements with `Array.Reverse` and builds the result with `ImmutableList.CreateRange`. At 100 000 elements, this is about 2.5 times faster than `ImmutableList.Reverse`, with the same allocation. `IIndexedList.rev` reverses its copy in place too and keeps the implementation of the input. - `removeManyAt` of both modules removes nothing when `index + count` overflows, as `List.removeManyAt` does. Before, the range went to `RemoveRange`, which threw. - `Helper.fs` gets the internal `Lengths` module for `Queue`, `Stack` and their interface modules: `queueUpTo` and `stackUpTo` count a queue or a stack only up to a limit. - Two crefs name the right members: `FastGenericComparer` is a method, and the cref of `ofBuilder` writes the nested generic builder as the documentation ID does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent f6fff46 commit 8aabf53

16 files changed

Lines changed: 5757 additions & 5716 deletions

‎src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<Compile Include="FlatList\FlatListBuilder.fs" />
3333
<Compile Include="FlatList\FlatListCE.fsi" />
3434
<Compile Include="FlatList\FlatListCE.fs" />
35+
<Compile Include="IndexedList\IndexedListTypes.fs" />
3536
<Compile Include="IndexedList\IndexedList.fsi" />
3637
<Compile Include="IndexedList\IndexedList.fs" />
3738
<Compile Include="IndexedList\IndexedListOption.fsi" />

‎src/FSharp.Collections.Immutable/Helper.fs‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,3 +851,68 @@ module internal Snapshot =
851851
let ofSeq (name : string) (source : seq<'T>) : 'T array =
852852
checkNotNull name source
853853
Enumerable.ToArray source
854+
855+
/// <summary>
856+
/// The lengths of the stacks and the queues, which do not store their length, counted only as far as a caller needs them
857+
/// (ADR 0001, D5 and D6).
858+
/// </summary>
859+
/// <remarks>
860+
/// <para>
861+
/// The checks of <c>ListChecks</c> take a length, but most of them only tell an empty input from one with elements, or an
862+
/// input of one element from a longer one, and the sorts return an input of at most one element unchanged. A count of a
863+
/// whole stack or queue would make each of them O(n).
864+
/// </para>
865+
/// <para>
866+
/// The modules of <c>Queue</c>, <c>Stack</c> and their interface modules pass a limit instead: 1 to tell an empty input
867+
/// from one with elements, 2 to tell an input of one element from a longer one. The functions take the interfaces, which
868+
/// the concrete stack and queue implement, so that one function serves a module and its interface module.
869+
/// </para>
870+
/// </remarks>
871+
module internal Lengths =
872+
873+
/// Counts the elements up to the limit, stepping from a collection to the collection without its first element. The step
874+
/// runs at most limit - 1 times, so a limit of 1 only reads whether the collection is empty.
875+
let inline private countUpTo
876+
([<InlineIfLambda>] isEmpty : 'Collection -> bool)
877+
([<InlineIfLambda>] rest : 'Collection -> 'Collection)
878+
(limit : int)
879+
(collection : 'Collection)
880+
=
881+
let mutable remaining = collection
882+
let mutable count = 0
883+
884+
while count < limit && not (isEmpty remaining) do
885+
count <- count + 1
886+
887+
if count < limit then
888+
remaining <- rest remaining
889+
890+
count
891+
892+
/// <summary>The number of elements of a queue, counted up to a limit: the length, or the limit for a longer queue.</summary>
893+
/// <param name="limit">The largest number to count; a limit that is not positive gives 0.</param>
894+
/// <param name="queue">The queue, which the caller has checked for <see langword="null"/>.</param>
895+
/// <returns>The smaller of the length of the queue and the limit, and 0 for a limit that is not positive.</returns>
896+
/// <remarks>
897+
/// O(1) for a limit of 1, which only reads <see cref="P:System.Collections.Immutable.IImmutableQueue`1.IsEmpty"/>. A
898+
/// larger limit calls <see cref="M:System.Collections.Immutable.IImmutableQueue`1.Dequeue"/> at most limit - 1 times.
899+
/// Each call allocates a queue, and the call that takes the last element of the front of an
900+
/// <see cref="T:System.Collections.Immutable.ImmutableQueue`1"/> reverses its back once, so the function suits small
901+
/// limits.
902+
/// </remarks>
903+
let queueUpTo (limit : int) (queue : IImmutableQueue<'T>) =
904+
countUpTo (fun (queue : IImmutableQueue<'T>) -> queue.IsEmpty) (fun queue -> queue.Dequeue ()) limit queue
905+
906+
/// <summary>The number of elements of a stack, counted up to a limit: the length, or the limit for a longer stack.</summary>
907+
/// <param name="limit">
908+
/// The largest number to count; a limit that is not positive gives 0, and <see cref="F:System.Int32.MaxValue"/> counts
909+
/// the whole stack.
910+
/// </param>
911+
/// <param name="stack">The stack, which the caller has checked for <see langword="null"/>.</param>
912+
/// <returns>The smaller of the length of the stack and the limit, and 0 for a limit that is not positive.</returns>
913+
/// <remarks>
914+
/// O(min(n, limit)), without an allocation: <see cref="M:System.Collections.Immutable.IImmutableStack`1.Pop"/> returns
915+
/// the stack below the top element, which exists already.
916+
/// </remarks>
917+
let stackUpTo (limit : int) (stack : IImmutableStack<'T>) =
918+
countUpTo (fun (stack : IImmutableStack<'T>) -> stack.IsEmpty) (fun stack -> stack.Pop ()) limit stack

0 commit comments

Comments
 (0)