かたちづくり

つれづれに、だらだらと、おきらくに

immutable な配列クラス作ってみた #fsharp

需要あるのか分からないけど、カッとなって作った。昨晩作り始めて、先ほど出張帰りの新幹線で完成。テストはしていないッ(キリッ

名前は迷ったんだけど、あまり長い名前にしたくなかったので Immutarray というちょっと変な名前(勝手な造語)にした。ImmutableArray だと immutable collections と被るみたいだし。心のなかでは「イミュータレイ」って読んでる。

コピペしてご自由にお使いください。名前等、気に食わなければ好きなように変更してどうぞ。

※ よく分からず inline 付けまくってみたけどいいんかいな?

namespace (好きな名前を入れてね)
open System

type private ImmutarrayData<'a> = { RawArray : 'a[] }

type Immutarray<'a when 'a : equality> (rawArray : 'a[]) =
  let data = { RawArray = rawArray }
  member inline private this.Data = data
  member this.Length = data.RawArray.Length
  member this.Item with get i = data.RawArray.[i]
  member this.__RAW_ARRAY__ = data.RawArray
  member this.ToArray() = Array.copy data.RawArray
  member this.Equals (x : Immutarray<'a>) = (data :> IEquatable<_>).Equals x.Data
  override this.Equals x = match x with :? Immutarray<'a> as x -> this.Equals x | _ -> false
  override this.GetHashCode () = data.GetHashCode()
  interface IEquatable<Immutarray<'a>> with
    member this.Equals x = this.Equals x
  interface seq<'a> with
    member this.GetEnumerator() = (data.RawArray :> 'a seq).GetEnumerator()
  interface Collections.IEnumerable with
    member this.GetEnumerator() = data.RawArray.GetEnumerator()

[<Sealed; AbstractClass; Runtime.CompilerServices.Extension>]
type Immutarray =
  static member inline private Raw (array : Immutarray<_>) = array.__RAW_ARRAY__
  static member ForEach       (array, action)     = Array.ForEach (Immutarray.Raw array, action)
  static member ConvertAll    (array, converter)  = Immutarray (Array.ConvertAll (Immutarray.Raw array, converter))
  static member Exists        (array, predicate)  = Array.Exists        (Immutarray.Raw array, predicate)
  static member TrueForAll    (array, predicate)  = Array.TrueForAll    (Immutarray.Raw array, predicate)
  static member Find          (array, predicate)  = Array.Find          (Immutarray.Raw array, predicate)
  static member FindIndex     (array, predicate)  = Array.FindIndex     (Immutarray.Raw array, predicate)
  static member FindLast      (array, predicate)  = Array.FindLast      (Immutarray.Raw array, predicate)
  static member FindLastIndex (array, predicate)  = Array.FindLastIndex (Immutarray.Raw array, predicate)
  static member FindAll       (array, predicate)  = Immutarray(Array.FindAll (Immutarray.Raw array, predicate))
  static member IndexOf       (array, value)      = Array.IndexOf     (Immutarray.Raw array, value)
  static member LastIndexOf   (array, value)      = Array.LastIndexOf (Immutarray.Raw array, value)
  static member BinarySearch  (array, value)                          = Array.BinarySearch (Immutarray.Raw array, value)
  static member BinarySearch  (array, value, comparer)                = Array.BinarySearch (Immutarray.Raw array, value, comparer)
  static member BinarySearch  (array, index, length, value)           = Array.BinarySearch (Immutarray.Raw array, index, length, value)
  static member BinarySearch  (array, index, length, value, comparer) = Array.BinarySearch (Immutarray.Raw array, index, length, value, comparer)
  [<Runtime.CompilerServices.Extension>]
  static member ToImmutarray source = Immutarray (Seq.toArray source)

[<CompilationRepresentation( CompilationRepresentationFlags.ModuleSuffix ); RequireQualifiedAccess>]  
module Immutarray =
  let inline private raw (a : Immutarray<'a>) = a.__RAW_ARRAY__
  
  let inline ofArray array  = Immutarray array
  let inline ofList  list   = Array.ofList list   |> ofArray
  let inline ofSeq   source = Array.ofSeq  source |> ofArray
    
  let inline toArray array  = Array.copy   (raw array)
  let inline toList  array  = Array.toList (raw array)
  let inline toSeq   array  = Array.toSeq  (raw array)

  let inline length array = raw array |> Array.length
  let inline get (array : Immutarray<'a>) index = array.[index]

  let inline empty<'a when 'a : equality> = Immutarray<'a> [||]
  let inline init       count initializer = Array.init       count initializer |> ofArray
  let inline create     count value       = Array.create     count value       |> ofArray
  let inline zeroCreate count             = Array.zeroCreate count             |> ofArray

  let inline map      mapping array         = Array.map     mapping (raw array) |> ofArray
  let inline mapi     mapping array         = Array.mapi    mapping (raw array) |> ofArray
  let inline map2     mapping array1 array2 = Array.map2    mapping (raw array1) (raw array2) |> ofArray
  let inline mapi2    mapping array1 array2 = Array.mapi2   mapping (raw array1) (raw array2) |> ofArray
  let inline collect  mapping array         = Array.collect mapping (raw array) |> ofArray
    
  let inline filter predicate array     = raw array |> Array.filter predicate |> ofArray
  let inline choose chooser   array     = raw array |> Array.choose chooser   |> ofArray
  let inline rev  array                 = raw array |> Array.rev  |> ofArray
  let inline sort array                 = raw array |> Array.sort |> ofArray
  let inline sortBy   projection array  = raw array |> Array.sortBy projection |> ofArray
  let inline sortWith comparer   array  = raw array |> Array.sortWith comparer |> ofArray
  let inline permutate indexMap array   = raw array |> Array.permute indexMap  |> ofArray
  let inline sub array startIndex count = Array.sub (raw array) startIndex count |> ofArray

  let inline iter   action array          = Array.iter   action (raw array)
  let inline iteri  action array          = Array.iteri  action (raw array)
  let inline iter2  action array1 array2  = Array.iter2  action (raw array1) (raw array2)
  let inline iteri2 action array1 array2  = Array.iteri2 action (raw array1) (raw array2)

  let inline isEmpty array = Array.isEmpty (raw array)
  let inline forall  predicate array          = Array.forall  predicate (raw array)
  let inline exists  predicate array          = Array.exists  predicate (raw array)
  let inline forall2 predicate array1 array2  = Array.forall2 predicate (raw array1) (raw array2)
  let inline exists2 predicate array1 array2  = Array.exists2 predicate (raw array1) (raw array2)

  let inline max     array = Array.max (raw array)
  let inline min     array = Array.min (raw array)
  let inline sum     array = Array.sum (raw array)
  let inline average array = Array.average (raw array)

  let inline maxBy     projection array = Array.maxBy projection (raw array)
  let inline minBy     projection array = Array.minBy projection (raw array)
  let inline sumBy     projection array = Array.sumBy projection (raw array)
  let inline averageBy projection array = Array.averageBy projection (raw array)

  let inline fold      folder state array         = Array.fold      folder state (raw array)
  let inline foldBack  folder array state         = Array.foldBack  folder (raw array) state
  let inline fold2     folder state array1 array2 = Array.fold2     folder state (raw array1) (raw array2)
  let inline foldBack2 folder array1 array2 state = Array.foldBack2 folder (raw array1) (raw array2) state
  let inline reduce     reduction array   = Array.reduce     reduction (raw array)
  let inline reduceBack reduction array   = Array.reduceBack reduction (raw array)
  let inline scan     folder state array  = Array.scan     folder state (raw array) |> ofArray
  let inline scanBack folder array state  = Array.scanBack folder (raw array) state |> ofArray

  let inline find         predicate array = Array.find         predicate (raw array)
  let inline findIndex    predicate array = Array.findIndex    predicate (raw array)
  let inline tryFind      predicate array = Array.tryFind      predicate (raw array)
  let inline tryFindIndex predicate array = Array.tryFindIndex predicate (raw array)
  let inline pick    chooser array = Array.pick    chooser (raw array)
  let inline tryPick chooser array = Array.tryPick chooser (raw array)

  let inline append array1 array2 = Array.append (raw array1) (raw array2) |> ofArray
  let inline concat arrays        = arrays |> Seq.map raw |> Array.concat |> ofArray
  let inline partition predicate array = let a1, a2 = Array.partition predicate (raw array) in Immutarray a1, Immutarray a2
  let inline zip  array1 array2         = Array.zip  (raw array1) (raw array2) |> ofArray
  let inline zip3 array1 array2 array3  = Array.zip3 (raw array1) (raw array2) (raw array3) |> ofArray
  let inline unzip  array = let a1, a2     = Array.unzip  (raw array) in Immutarray a1, Immutarray a2
  let inline unzip3 array = let a1, a2, a3 = Array.unzip3 (raw array) in Immutarray a1, Immutarray a2, Immutarray a3

[<CompilationRepresentation( CompilationRepresentationFlags.ModuleSuffix ); RequireQualifiedAccess>]  
module Array =
  let toImmutarray = Immutarray.ofArray
  let ofImmutarray = Immutarray.toArray

[<CompilationRepresentation( CompilationRepresentationFlags.ModuleSuffix ); RequireQualifiedAccess>]  
module List =
  let toImmutarray = Immutarray.ofList
  let ofImmutarray = Immutarray.toList

[<CompilationRepresentation( CompilationRepresentationFlags.ModuleSuffix ); RequireQualifiedAccess>]  
module Seq =
  let toImmutarray = Immutarray.ofSeq
  let ofImmutarray = Immutarray.toSeq

[<CompilationRepresentation( CompilationRepresentationFlags.ModuleSuffix ); RequireQualifiedAccess>]  
module Set =
  let ofImmutarray (array : Immutarray<_>) = Set.ofArray array.__RAW_ARRAY__
  let toImmutarray set = Immutarray (Set.toArray set)

[<CompilationRepresentation( CompilationRepresentationFlags.ModuleSuffix ); RequireQualifiedAccess>]  
module Map =
  let ofImmutarray (array : Immutarray<_>) = Map.ofArray array.__RAW_ARRAY__
  let toImmutarray map = Immutarray (Map.toArray map)