Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit 900a6dd

Browse files
abelbraaksmacartermp
authored andcommitted
Improve performance of String.concat for seq of type array or list (dotnet#9483)
* Move `String.length` to the top of its module so that the `length` function is in scope * Improve performance of String.concat for arrays and lists * Make concatArray local to String.concat * Testing String.concat, make sure the new three paths are covered * Remove "foo", "bax", "bar" in tests, making them more readable Co-authored-by: Phillip Carter <[email protected]>
1 parent dac287b commit 900a6dd

File tree

2 files changed

+41
-26
lines changed

2 files changed

+41
-26
lines changed

src/fsharp/FSharp.Core/string.fs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace Microsoft.FSharp.Core
88
open Microsoft.FSharp.Core.Operators
99
open Microsoft.FSharp.Core.Operators.Checked
1010
open Microsoft.FSharp.Collections
11+
open Microsoft.FSharp.Primitives.Basics
1112

1213
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
1314
[<RequireQualifiedAccess>]
@@ -22,7 +23,25 @@ namespace Microsoft.FSharp.Core
2223

2324
[<CompiledName("Concat")>]
2425
let concat sep (strings : seq<string>) =
25-
String.Join(sep, strings)
26+
27+
let concatArray sep (strings: string []) =
28+
match length sep with
29+
| 0 -> String.Concat strings
30+
// following line should be used when this overload becomes part of .NET Standard (it's only in .NET Core)
31+
//| 1 -> String.Join(sep.[0], strings, 0, strings.Length)
32+
| _ -> String.Join(sep, strings, 0, strings.Length)
33+
34+
match strings with
35+
| :? array<string> as arr ->
36+
concatArray sep arr
37+
38+
| :? list<string> as lst ->
39+
lst
40+
|> List.toArray
41+
|> concatArray sep
42+
43+
| _ ->
44+
String.Join(sep, strings)
2645

2746
[<CompiledName("Iterate")>]
2847
let iter (action : (char -> unit)) (str:string) =

tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/StringModule.fs

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,27 @@ type StringModule() =
2323

2424
[<Test>]
2525
member this.Concat() =
26-
let e1 = String.concat null ["foo"]
27-
Assert.AreEqual("foo", e1)
28-
29-
let e2 = String.concat "" []
30-
Assert.AreEqual("", e2)
31-
32-
let e3 = String.concat "foo" []
33-
Assert.AreEqual("", e3)
34-
35-
let e4 = String.concat "" [null]
36-
Assert.AreEqual("", e4)
37-
38-
let e5 = String.concat "" [""]
39-
Assert.AreEqual("", e5)
40-
41-
let e6 = String.concat "foo" ["bar"]
42-
Assert.AreEqual("bar", e6)
43-
44-
let e7 = String.concat "foo" ["bav";"baz"]
45-
Assert.AreEqual("bavfoobaz", e7)
46-
47-
let e8 = String.concat "foo" [null;"baz";null;"bar"]
48-
Assert.AreEqual("foobazfoofoobar", e8)
49-
50-
CheckThrowsArgumentNullException(fun () -> String.concat "foo" null |> ignore)
26+
/// This tests the three paths of String.concat w.r.t. array, list, seq
27+
let execTest f expected arg =
28+
let r1 = f (List.toSeq arg)
29+
Assert.AreEqual(expected, r1)
30+
31+
let r2 = f (List.toArray arg)
32+
Assert.AreEqual(expected, r2)
33+
34+
let r3 = f arg
35+
Assert.AreEqual(expected, r3)
36+
37+
do execTest (String.concat null) "world" ["world"]
38+
do execTest (String.concat "") "" []
39+
do execTest (String.concat "|||") "" []
40+
do execTest (String.concat "") "" [null]
41+
do execTest (String.concat "") "" [""]
42+
do execTest (String.concat "|||") "apples" ["apples"]
43+
do execTest (String.concat " ") "happy together" ["happy"; "together"]
44+
do execTest (String.concat "Me! ") "Me! No, you. Me! Me! Oh, them." [null;"No, you. ";null;"Oh, them."]
45+
46+
CheckThrowsArgumentNullException(fun () -> String.concat "%%%" null |> ignore)
5147

5248
[<Test>]
5349
member this.Iter() =

0 commit comments

Comments
 (0)