Skip to content

Commit 0aacf26

Browse files
Merge pull request #434 from Thorium/niceName-opt
Faster and less-memory-consuming niceName
2 parents 30d2a2b + 26e2938 commit 0aacf26

File tree

1 file changed

+39
-16
lines changed

1 file changed

+39
-16
lines changed

src/FSharpx.Extras/Strings.fs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,31 +91,54 @@ module Strings =
9191
let (|Upper|_|) = satisfies Char.IsUpper
9292
let (|Lower|_|) = satisfies Char.IsLower
9393

94+
/// Faster patterns with ValueOption instead of Option
95+
[<RequireQualifiedAccess>]
96+
module ValuePatterns =
97+
// Active patterns & operators for parsing strings
98+
let inline take (s:string) i = if i >= s.Length then ValueNone else ValueSome s.[i]
99+
100+
let inline internal satisfies predicate (charOption:voption<char>) =
101+
match charOption with
102+
| ValueSome c when predicate c -> charOption
103+
| _ -> ValueNone
104+
105+
[<return: Struct>]
106+
let (|EOF|_|) = function
107+
| ValueSome _ -> ValueNone
108+
| _ -> ValueSome ()
109+
[<return: Struct>]
110+
let (|LetterDigit|_|) = satisfies Char.IsLetterOrDigit
111+
[<return: Struct>]
112+
let (|Upper|_|) = satisfies Char.IsUpper
113+
[<return: Struct>]
114+
let (|Lower|_|) = satisfies Char.IsLower
115+
94116
/// Turns a string into a nice PascalCase identifier
95117
[<CompiledName("NiceName")>]
96118
let niceName (s:string) =
97119
if s = s.ToUpper() then s else
98120
// Starting to parse a new segment
99-
let rec restart i = seq {
100-
match s @? i with
101-
| EOF -> ()
102-
| LetterDigit _ & Upper _ -> yield! upperStart i (i + 1)
103-
| LetterDigit _ -> yield! consume i false (i + 1)
104-
| _ -> yield! restart (i + 1) }
121+
let rec restart i =
122+
match ValuePatterns.take s i with
123+
| ValuePatterns.EOF -> Seq.empty
124+
| ValuePatterns.LetterDigit _ & ValuePatterns.Upper _ -> upperStart i (i + 1)
125+
| ValuePatterns.LetterDigit _ -> consume i false (i + 1)
126+
| _ -> restart (i + 1)
105127

106128
// Parsed first upper case letter, continue either all lower or all upper
107-
and upperStart from i = seq {
108-
match s @? i with
109-
| Upper _ -> yield! consume from true (i + 1)
110-
| Lower _ -> yield! consume from false (i + 1)
111-
| _ -> yield! restart (i + 1) }
129+
and upperStart from i =
130+
match ValuePatterns.take s i with
131+
| ValuePatterns.Upper _ -> consume from true (i + 1)
132+
| ValuePatterns.Lower _ -> consume from false (i + 1)
133+
| _ -> restart (i + 1)
112134
// Consume are letters of the same kind (either all lower or all upper)
113-
and consume from takeUpper i = seq {
114-
match s @? i with
115-
| Lower _ when not takeUpper -> yield! consume from takeUpper (i + 1)
116-
| Upper _ when takeUpper -> yield! consume from takeUpper (i + 1)
135+
and consume from takeUpper i =
136+
match ValuePatterns.take s i with
137+
| ValuePatterns.Lower _ when not takeUpper -> consume from takeUpper (i + 1)
138+
| ValuePatterns.Upper _ when takeUpper -> consume from takeUpper (i + 1)
117139
| _ ->
118-
yield from, i
140+
seq {
141+
yield struct(from, i)
119142
yield! restart i }
120143

121144
// Split string into segments and turn them to PascalCase

0 commit comments

Comments
 (0)