Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@
<Compile Include="..\lib.fs">
<Link>Utilities\lib.fs</Link>
</Compile>
<Compile Include="..\block.fsi">
<Link>Utilities\block.fsi</Link>
</Compile>
<Compile Include="..\block.fs">
<Link>Utilities\block.fs</Link>
</Compile>
<Compile Include="..\rational.fsi">
<Link>Utilities\rational.fsi</Link>
</Compile>
Expand Down
187 changes: 187 additions & 0 deletions src/fsharp/block.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
module Internal.Utilities.Library.Block

open System.Collections.Immutable

type block<'T> = ImmutableArray<'T>
type blockbuilder<'T> = ImmutableArray<'T>.Builder

[<RequireQualifiedAccess>]
module BlockBuilder =

let create size : blockbuilder<'T> =
ImmutableArray.CreateBuilder(size)

[<RequireQualifiedAccess>]
module Block =

[<GeneralizableValue>]
let empty<'T> = ImmutableArray<'T>.Empty

let init n (f: int -> 'T) : block<_> =
match n with
| 0 -> ImmutableArray.Empty
| 1 -> ImmutableArray.Create(f 0)
| n ->
if n < 0 then
invalidArg "n" "Below zero."

let builder = ImmutableArray.CreateBuilder(n)
for i = 0 to n - 1 do
builder.Add(f i)
builder.MoveToImmutable()

let iter f (arr: block<'T>) =
for i = 0 to arr.Length - 1 do
f arr.[i]

let iteri f (arr: block<'T>) =
for i = 0 to arr.Length - 1 do
f i arr.[i]

let iter2 f (arr1: block<'T1>) (arr2: block<'T2>) =
if arr1.Length <> arr2.Length then
invalidOp "Block lengths do not match."

for i = 0 to arr1.Length - 1 do
f arr1.[i] arr2.[i]

let iteri2 f (arr1: block<'T1>) (arr2: block<'T2>) =
if arr1.Length <> arr2.Length then
invalidOp "Block lengths do not match."

for i = 0 to arr1.Length - 1 do
f i arr1.[i] arr2.[i]

let map (mapper: 'T -> 'U) (arr: block<'T>) : block<_> =
match arr.Length with
| 0 -> ImmutableArray.Empty
| 1 -> ImmutableArray.Create(mapper arr.[0])
| _ ->
let builder = ImmutableArray.CreateBuilder(arr.Length)
for i = 0 to arr.Length - 1 do
builder.Add(mapper arr.[i])
builder.MoveToImmutable()

let mapi (mapper: int -> 'T -> 'U) (arr: block<'T>) : block<_> =
match arr.Length with
| 0 -> ImmutableArray.Empty
| 1 -> ImmutableArray.Create(mapper 0 arr.[0])
| _ ->
let builder = ImmutableArray.CreateBuilder(arr.Length)
for i = 0 to arr.Length - 1 do
builder.Add(mapper i arr.[i])
builder.MoveToImmutable()

let map2 (mapper: 'T1 -> 'T2 -> 'T) (arr1: block<'T1>) (arr2: block<'T2>) : block<_> =
if arr1.Length <> arr2.Length then
invalidOp "Block lengths do not match."

match arr1.Length with
| 0 -> ImmutableArray.Empty
| 1 -> ImmutableArray.Create(mapper arr1.[0] arr2.[0])
| n ->
let builder = ImmutableArray.CreateBuilder(n)
for i = 0 to n - 1 do
builder.Add(mapper arr1.[i] arr2.[i])
builder.MoveToImmutable()

let mapi2 (mapper: int -> 'T1 -> 'T2 -> 'T) (arr1: block<'T1>) (arr2: block<'T2>) : block<_> =
if arr1.Length <> arr2.Length then
invalidOp "Block lengths do not match."

match arr1.Length with
| 0 -> ImmutableArray.Empty
| 1 -> ImmutableArray.Create(mapper 0 arr1.[0] arr2.[0])
| n ->
let builder = ImmutableArray.CreateBuilder(n)
for i = 0 to n - 1 do
builder.Add(mapper i arr1.[i] arr2.[i])
builder.MoveToImmutable()

let concat (arrs: block<block<'T>>) : block<'T> =
match arrs.Length with
| 0 -> ImmutableArray.Empty
| 1 -> arrs.[0]
| 2 -> arrs.[0].AddRange(arrs.[1])
| _ ->
let mutable acc = 0
for h in arrs do
acc <- acc + h.Length

let builder = ImmutableArray.CreateBuilder(acc)
for i = 0 to arrs.Length - 1 do
builder.AddRange(arrs.[i])
builder.MoveToImmutable()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing capacity <- count or ToImmutable here, as capacity could be anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine. acc is the total length of the final array.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I completely missed the the length summing loop, yep it's fine!


let forall predicate (arr: block<'T>) =
let len = arr.Length
let rec loop i = i >= len || (predicate arr.[i] && loop (i+1))
loop 0

let forall2 predicate (arr1: block<'T1>) (arr2: block<'T2>) =
if arr1.Length <> arr2.Length then
invalidOp "Block lengths do not match."

let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(predicate)
let len1 = arr1.Length
let rec loop i = i >= len1 || (f.Invoke(arr1.[i], arr2.[i]) && loop (i+1))
loop 0

let tryFind predicate (arr: block<'T>) =
let rec loop i =
if i >= arr.Length then None else
if predicate arr.[i] then Some arr.[i] else loop (i+1)
loop 0

let tryFindIndex predicate (arr: block<'T>) =
let len = arr.Length
let rec go n = if n >= len then None elif predicate arr.[n] then Some n else go (n+1)
go 0

let tryPick chooser (arr: block<'T>) =
let rec loop i =
if i >= arr.Length then None else
match chooser arr.[i] with
| None -> loop(i+1)
| res -> res
loop 0

let ofSeq (xs: 'T seq) =
ImmutableArray.CreateRange(xs)

let append (arr1: block<'T1>) (arr2: block<'T1>) : block<_> =
arr1.AddRange(arr2)

let createOne (item: 'T) : block<_> =
ImmutableArray.Create(item)

let filter predicate (arr: block<'T>) : block<'T> =
let builder = ImmutableArray.CreateBuilder(arr.Length)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test for empty initially

for i = 0 to arr.Length - 1 do
if predicate arr.[i] then
builder.Add(arr.[i])
builder.Capacity <- builder.Count
builder.MoveToImmutable()

let exists predicate (arr: block<'T>) =
let len = arr.Length
let rec loop i = i < len && (predicate arr.[i] || loop (i+1))
len > 0 && loop 0

let choose (chooser: 'T -> 'U option) (arr: block<'T>) : block<'U> =
let builder = ImmutableArray.CreateBuilder(arr.Length)
for i = 0 to arr.Length - 1 do
let result = chooser arr.[i]
if result.IsSome then
builder.Add(result.Value)
builder.Capacity <- builder.Count
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't know the length it doesn't matter too much if you do ToImmutable. You'll do a copy by changing capacity anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the capacity will only copy if the count is different.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough :)

builder.MoveToImmutable()

let isEmpty (arr: block<_>) = arr.IsEmpty

let fold folder state (arr: block<_>) =
let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(folder)
let mutable state = state
for i = 0 to arr.Length - 1 do
state <- f.Invoke(state, arr.[i])
state
63 changes: 63 additions & 0 deletions src/fsharp/block.fsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
[<AutoOpen>]
module internal Internal.Utilities.Library.Block

open System.Collections.Immutable

/// Type alias for System.Collections.Immutable.ImmutableArray<'T>
type block<'T> = ImmutableArray<'T>

/// Type alias for System.Collections.Immutable.ImmutableArray<'T>.Builder
type blockbuilder<'T> = ImmutableArray<'T>.Builder

[<RequireQualifiedAccess>]
module BlockBuilder =

val create : size: int -> blockbuilder<'T>

[<RequireQualifiedAccess>]
module Block =

[<GeneralizableValue>]
val empty<'T> : block<'T>

val init : n: int -> f: (int -> 'T) -> block<'T>

val iter : f: ('T -> unit) -> block<'T> -> unit

val iteri : f: (int -> 'T -> unit) -> block<'T> -> unit

val iter2 : f: ('T1 -> 'T2 -> unit) -> block<'T1> -> block<'T2> -> unit

val iteri2 : f: (int -> 'T1 -> 'T2 -> unit) -> block<'T1> -> block<'T2> -> unit

val map : mapper: ('T1 -> 'T2) -> block<'T1> -> block<'T2>

val mapi : mapper: (int -> 'T1 -> 'T2) -> block<'T1> -> block<'T2>

val concat : block<block<'T>> -> block<'T>

val forall : predicate: ('T -> bool) -> block<'T> -> bool

val forall2 : predicate: ('T1 -> 'T2 -> bool) -> block<'T1> -> block<'T2> -> bool

val tryFind : predicate: ('T -> bool) -> block<'T> -> 'T option

val tryFindIndex : predicate: ('T -> bool) -> block<'T> -> int option

val tryPick : chooser: ('T1 -> 'T2 option) -> block<'T1> -> 'T2 option

val ofSeq : seq<'T> -> block<'T>

val append : block<'T> -> block<'T> -> block<'T>

val createOne : 'T -> block<'T>

val filter : predicate: ('T -> bool) -> block<'T> -> block<'T>

val exists : predicate: ('T -> bool) -> block<'T> -> bool

val choose : chooser: ('T -> 'U option) -> block<'T> -> block<'U>

val isEmpty : block<'T> -> bool

val fold : folder: ('State -> 'T -> 'State) -> 'State -> block<'T> -> 'State
1 change: 1 addition & 0 deletions src/fsharp/lib.fs
Original file line number Diff line number Diff line change
Expand Up @@ -604,3 +604,4 @@ module ArrayParallel =

let inline map f (arr: 'T []) =
arr |> mapi (fun _ item -> f item)

Loading