Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
313 views
in Technique[技术] by (71.8m points)

immutability - Is using a StringBuilder a right thing to do in F#?

StringBuiler is a mutable object, F# encourages employing immutability as much as possible. So one should use transformation rather than mutation. Does this apply to StringBuilder when it comes to building a string in F#? Is there an F# immutable alternative to it? If so, is this alternative as efficient?

A snippet

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I think that using StringBuilder in F# is perfectly fine - the fact that sb.Append returns the current instance of StringBuilder means that it can be easily used with the fold function. Even though this is still imperative (the object is mutated), it fits reasonably well with the functional style when you do not expose references to StringBuilder.

But equally, you can just construct a list of strings and concatenate them using String.concat - this is almost as efficient as using StringBuilder (it is slower, but not much - and it is significantly faster than concatenating strings using +)

So, lists give you similar performance, but they are immutable (and work well with concurrency etc.) - they would be a good fit if you were building string algorithmically, because you can just append strings to the front of the list - this is very efficient operation on lists (and then reverse the string). Also, using list expressions gives you a very convenient syntax:

// Concatenating strings using + (2.3 seconds)
let s1 = [ for i in 0 .. 25000 -> "Hello " ] |> Seq.reduce (+)
s1.Length

// Creating immutable list and using String.concat (5 ms)
let s2 = [ for i in 0 .. 25000 -> "Hello " ] |> String.concat ""
s2.Length

// Creating a lazy sequence and concatenating using StringBuilder & fold (5 ms)
let s3 = 
  seq { for i in 0 .. 25000 -> "Hello " }
  |> Seq.fold(fun (sb:System.Text.StringBuilder) s -> 
      sb.Append(s)) (new System.Text.StringBuilder())
  |> fun x -> x.ToString()
s3.Length

// Imperative solution using StringBuilder and for loop (1 ms)
let s4 = 
  ( let sb = new System.Text.StringBuilder()
    for i in 0 .. 25000 do sb.Append("Hello ") |> ignore
    sb.ToString() )
s4.Length

The times were measured on my, fairly fast, work machine using #time in F# Interactive - it is quite likely that it would be faster in release build, but I think they are fairly representative.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...