Is there an open-source (non GPL) implementation of a functional version of .NET StringBuilder?

前端 未结 2 1902
感动是毒
感动是毒 2021-01-15 12:28

I\'m looking for a functional (as in, non-imperative) implementation of StringBuilder or equivalent. I\'ve seen a couple of functional arrays implementation, but they don\'t

相关标签:
2条回答
  • 2021-01-15 12:46

    StringBuilders advantage over string is due to minimizing allocations. It pre-allocates a buffer to avoid allocating for every insert/append. This requires mutability--some object must own (and mutate) the buffer.

    Incidentally, System.String already fits (what I can make of) your description: it's immutable and supports concatenation, insertionMSDN, and removalMSDN.

    UPDATE

    Tomas' idea intrigued me. Taking his idea, here's what I came up with

    type StringBuilder =
      private
      | Empty
      | StringBuilder of int * string * int * StringBuilder
      member this.Length =
        match this with 
        | Empty -> 0
        | StringBuilder(_, _, n, _) -> n
      override this.ToString() =
        let rec rev acc = function
          | Empty -> acc
          | StringBuilder(idx, str, _, bldr) -> rev ((idx, str)::acc) bldr
        let buf = ResizeArray(this.Length)
        for idx, str in rev [] this do buf.InsertRange(idx, str)
        System.String(buf.ToArray())
    
    [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
    [<RequireQualifiedAccess>]
    module StringBuilder =
      let empty = Empty
      let length (bldr:StringBuilder) = bldr.Length
      let insert index str bldr = 
        if index < 0 || index > (length bldr) then invalidArg "index" "out of range"
        StringBuilder(index, str, str.Length + bldr.Length, bldr)
      let create str = insert 0 str empty
      let append str bldr = insert (length bldr) str bldr
      let remove index count (bldr:StringBuilder) = create <| bldr.ToString().Remove(index, count)
    

    Usage

    let bldr = 
      StringBuilder.create "abcdef"
      |> StringBuilder.insert 1 "xyz"
      |> StringBuilder.append "123"
      |> StringBuilder.remove 1 2
    
    bldr.ToString() //azbcdef123
    

    It's persistent and insertion is O(1).

    0 讨论(0)
  • 2021-01-15 13:04

    I don't know about any implementation that would do exactly what you want. However, I don't think you can ever get O(1) complexity of inserting (at arbitrary index) and O(n) complexity of iteration over the results.

    If you're happy to sacrifice the complexity of insertion, then you can use just string as Daniel suggests. On the other side, if you're willing to sacrifice the complexity of toString, then you can make immutable data structure with O(1) insertion at any location by using a list of strings and indices:

    type InsertList = IL of (int * string) list 
    
    // Insert string 'str' at the specified index 
    let insertAt idx str (IL items) = IL (idx, str)::items
    
    // Create insert list from a string
    let ofString str = IL [str]
    

    The conversion to string is a bit more tricky. However, I think you can get O(n log n) complexity by using a mutable LinkedList and inserting individual characters at the right location by iterating over the insertions from the end. The use of LinkedList is going to be locallized to toString, so the data structure is still purely functional.

    0 讨论(0)
提交回复
热议问题