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

▼魔方 西西 提交于 2019-12-01 07:39:41

问题


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 support insertion natively. Bonus for open-source, non-(L?A?)GPL, bonus for F#, but I can translate from Haskell/OCaml/SML if needed.

Suggestions for algorithms welcome.


回答1:


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).




回答2:


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.



来源:https://stackoverflow.com/questions/8336059/is-there-an-open-source-non-gpl-implementation-of-a-functional-version-of-net

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!