From 7698437885c3e4c9b6b18a62303abe890d85212f Mon Sep 17 00:00:00 2001 From: Tuomas Hietanen Date: Fri, 5 Jul 2024 13:50:11 +0100 Subject: [PATCH] some minor performance optimisations --- src/FSharpAux.Core/Colors.fs | 1 + src/FSharpAux.Core/Dict.fs | 4 ++- src/FSharpAux.Core/Regex.fs | 14 ++++++---- src/FSharpAux.Core/Seq.fs | 7 +++-- src/FSharpAux.Core/String.fs | 47 ++++++++++++++++++-------------- src/FSharpAux.IO/FileIO.fs | 10 +++---- src/FSharpAux.IO/PathFileName.fs | 5 ++-- src/FSharpAux.IO/SchemaReader.fs | 4 +-- src/FSharpAux/PSeq.fs | 8 +++--- 9 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/FSharpAux.Core/Colors.fs b/src/FSharpAux.Core/Colors.fs index 8c4efbf..0558c6d 100644 --- a/src/FSharpAux.Core/Colors.fs +++ b/src/FSharpAux.Core/Colors.fs @@ -19,6 +19,7 @@ module Colors = | B v -> v /// Color structure + [] type Color = { /// The alpha component value of this Color structure. A : byte diff --git a/src/FSharpAux.Core/Dict.fs b/src/FSharpAux.Core/Dict.fs index 1e992bd..54b0457 100644 --- a/src/FSharpAux.Core/Dict.fs +++ b/src/FSharpAux.Core/Dict.fs @@ -54,7 +54,9 @@ module Dict = member s.ContainsKey(k) = d.ContainsKey(Some(k)) member s.TryGetValue(k,r) = let key = Some(k) - if d.ContainsKey(key) then (r <- d.[key]; true) else false + match d.TryGetValue key with + | true, v -> r <- v; true + | false, _ -> false member s.Remove(k : 'Key) = (raise (NotSupportedException(notMutable)) : bool) interface ICollection> with diff --git a/src/FSharpAux.Core/Regex.fs b/src/FSharpAux.Core/Regex.fs index bf2b22b..e6ddae0 100644 --- a/src/FSharpAux.Core/Regex.fs +++ b/src/FSharpAux.Core/Regex.fs @@ -9,10 +9,11 @@ module Regex = //http://stackoverflow.com/questions/5684014/f-mapping-regular-expression-matches-with-active-patterns module Active = /// Returns the first occurencing match of the pattern + [] let (|RegexMatchValue|_|) (regex:Regex) input = let m = regex.Match(input) - if m.Success then Some m.Value - else None + if m.Success then ValueSome m.Value + else ValueNone @@ -68,12 +69,13 @@ module Regex = /// Returns a seq of group values matching the pattern let parseAll regexStr line = let rec loop (m:Match) = - seq { match m.Success with | true -> - yield (List.tail [ for g in m.Groups -> g.Value ]) - yield! loop (m.NextMatch()) - | false -> () } + seq { + yield (List.tail [ for g in m.Groups -> g.Value ]) + yield! loop (m.NextMatch()) + } + | false -> Seq.empty let m = Regex.Match(line,regexStr) loop m diff --git a/src/FSharpAux.Core/Seq.fs b/src/FSharpAux.Core/Seq.fs index c6239b8..a7c2466 100644 --- a/src/FSharpAux.Core/Seq.fs +++ b/src/FSharpAux.Core/Seq.fs @@ -174,9 +174,10 @@ module Seq = keyList |> Seq.map ( fun k -> - if m.ContainsKey(k) then - aggregation m.[k] - else + match m.TryGetValue k with + | true, mk -> + aggregation mk + | false, _ -> defaultValue ) diff --git a/src/FSharpAux.Core/String.fs b/src/FSharpAux.Core/String.fs index 82ca3b6..1d33d4f 100644 --- a/src/FSharpAux.Core/String.fs +++ b/src/FSharpAux.Core/String.fs @@ -79,46 +79,53 @@ module String = // Active patterns & operators for parsing strings - let (@?) (s:string) i = if i >= s.Length then None else Some s.[i] + let (@?) (s:string) i = if i >= s.Length then ValueNone else ValueSome s.[i] - let inline satisfies predicate (charOption:option) = + let inline satisfies predicate (charOption:voption) = match charOption with - | Some c when predicate c -> charOption - | _ -> None + | ValueSome c when predicate c -> charOption + | _ -> ValueNone + [] let (|EOF|_|) = function - | Some _ -> None - | _ -> Some () + | ValueSome _ -> ValueNone + | _ -> ValueSome () + [] let (|LetterDigit|_|) = satisfies Char.IsLetterOrDigit + [] let (|Upper|_|) = satisfies Char.IsUpper + [] let (|Lower|_|) = satisfies Char.IsLower /// Turns a string into a nice PascalCase identifier let niceName (s:string) = if s = s.ToUpper() then s else // Starting to parse a new segment - let rec restart i = seq { + let rec restart i = match s @? i with - | EOF -> () - | LetterDigit _ & Upper _ -> yield! upperStart i (i + 1) - | LetterDigit _ -> yield! consume i false (i + 1) - | _ -> yield! restart (i + 1) } + | EOF -> Seq.empty + | LetterDigit _ & Upper _ -> upperStart i (i + 1) + | LetterDigit _ -> consume i false (i + 1) + | _ -> restart (i + 1) // Parsed first upper case letter, continue either all lower or all upper - and upperStart from i = seq { + and upperStart from i = match s @? i with - | Upper _ -> yield! consume from true (i + 1) - | Lower _ -> yield! consume from false (i + 1) - | _ -> yield! restart (i + 1) } + | Upper _ -> consume from true (i + 1) + | Lower _ -> consume from false (i + 1) + | _ -> restart (i + 1) // Consume are letters of the same kind (either all lower or all upper) - and consume from takeUpper i = seq { + and consume from takeUpper i = match s @? i with - | Lower _ when not takeUpper -> yield! consume from takeUpper (i + 1) - | Upper _ when takeUpper -> yield! consume from takeUpper (i + 1) + | Lower _ when not takeUpper -> consume from takeUpper (i + 1) + | Upper _ when takeUpper -> consume from takeUpper (i + 1) | _ -> - yield from, i - yield! restart i } + let r1 = struct (from, i) + let r2 = restart i + seq { + yield r1 + yield! r2 } // Split string into segments and turn them to PascalCase seq { for i1, i2 in restart 0 do diff --git a/src/FSharpAux.IO/FileIO.fs b/src/FSharpAux.IO/FileIO.fs index 89a9c82..a5382b8 100644 --- a/src/FSharpAux.IO/FileIO.fs +++ b/src/FSharpAux.IO/FileIO.fs @@ -96,8 +96,8 @@ module FileIO = /// Detects whether the given path does not contains invalid characters. let isValidPath (path:string) = Path.GetInvalidPathChars() - |> Array.filter (fun char -> path.Contains(char.ToString())) - |> Array.isEmpty + |> Array.exists (fun char -> path.Contains(char.ToString())) + |> not /// Creates a directory if it does not exist. @@ -140,7 +140,7 @@ module FileIO = let FileEnumerator (filePath) = use reader = File.OpenText(filePath) Seq.unfold(fun line -> - if line = null then + if isNull line then reader.Close() None else @@ -206,11 +206,11 @@ module FileIO = /// Appends a text if the value is not null - let inline appendIfNotNull value s = appendIfTrue (value <> null) (sprintf "%s%A" s value) + let inline appendIfNotNull value s = appendIfTrue (not (isNull value)) (sprintf "%s%A" s value) /// Appends a text if the value is not null - let inline appendStringIfValueIsNotNull value = appendIfTrue (value <> null) + let inline appendStringIfValueIsNotNull value = appendIfTrue (not (isNull value)) /// Appends a text if the value is not null or empty diff --git a/src/FSharpAux.IO/PathFileName.fs b/src/FSharpAux.IO/PathFileName.fs index e3c8d89..4728285 100644 --- a/src/FSharpAux.IO/PathFileName.fs +++ b/src/FSharpAux.IO/PathFileName.fs @@ -14,9 +14,10 @@ module PathFileName = | _ -> failwith "No file or directory given." /// Active Pattern for determining file extension. + [] let (|EndsWith|_|) (extension : string) (file : string) = - if file.EndsWith extension then Some() - else None + if file.EndsWith extension then ValueSome() + else ValueNone /// Active Pattern for determining file name. let (|FileInfoFullName|) (f : FileInfo) = f.FullName diff --git a/src/FSharpAux.IO/SchemaReader.fs b/src/FSharpAux.IO/SchemaReader.fs index eead254..efed62a 100644 --- a/src/FSharpAux.IO/SchemaReader.fs +++ b/src/FSharpAux.IO/SchemaReader.fs @@ -259,7 +259,7 @@ module SchemaReader = let header = convertHeaderLine separator header Seq.unfold(fun line -> - if line = null then + if isNull line then reader.Close() None else @@ -276,7 +276,7 @@ module SchemaReader = | true -> for i = 1 to skipLinesBeforeHeader do reader.ReadLine() |> ignore let tmpLine = reader.ReadLine() - if tmpLine = null then + if isNull tmpLine then reader.Close() String.Empty else diff --git a/src/FSharpAux/PSeq.fs b/src/FSharpAux/PSeq.fs index e2a99f5..ad7bd56 100644 --- a/src/FSharpAux/PSeq.fs +++ b/src/FSharpAux/PSeq.fs @@ -130,7 +130,7 @@ module PSeq = ParallelEnumerable.ElementAt(toP(s), n) let map2 f s1 s2 = - ParallelEnumerable.Zip(toP(s1),toP(s2), Func<_,_,_>(fun x y -> f x y)) + ParallelEnumerable.Zip(toP(s1),toP(s2), Func<_,_,_>(f)) let zip s1 s2 = ParallelEnumerable.Zip(toP(s1),toP(s2), Func<_,_,_>(fun x y -> (x,y))) @@ -167,7 +167,7 @@ module PSeq = ParallelEnumerable.Select(distinct, Func<_,_>(fun (x,px) -> x)) let sort s = - ParallelEnumerable.OrderBy(toP(s), Func<_,_>(fun x -> x), ComparisonIdentity.Structural<_>) :> pseq<'T> + ParallelEnumerable.OrderBy(toP(s), Func<_,_>(id), ComparisonIdentity.Structural<_>) :> pseq<'T> let sortBy (f : 'T -> 'Key) s = ParallelEnumerable.OrderBy(toP(s), Func<_,_>(f), ComparisonIdentity.Structural<_>) :> pseq<'T> @@ -235,7 +235,7 @@ module PSeq = | :? seq as s -> unbox(ParallelEnumerable.Min(toP(s))) | :? seq as s -> unbox(ParallelEnumerable.Min(toP(s))) | :? seq as s -> unbox(ParallelEnumerable.Min(toP(s))) - | _ -> ParallelEnumerable.Min(toP(s), Func<_,_>(fun x -> x)) + | _ -> ParallelEnumerable.Min(toP(s), Func<_,_>(id)) let inline minBy (f : ^T -> ^U) (s : seq< ^T >) : ^T when ^U : comparison = let elemsAndVals = ParallelEnumerable.Select(toP(s), Func<_,_>(fun x -> f x, x)) @@ -250,7 +250,7 @@ module PSeq = | :? seq as s -> unbox(ParallelEnumerable.Max(toP(s))) | :? seq as s -> unbox(ParallelEnumerable.Max(toP(s))) | :? seq as s -> unbox(ParallelEnumerable.Max(toP(s))) - | _ -> ParallelEnumerable.Max(toP(s), Func<_,_>(fun x -> x)) + | _ -> ParallelEnumerable.Max(toP(s), Func<_,_>(id)) let inline maxBy (f : ^T -> ^U) (s : seq< ^T >) : ^T = let elemsAndVals = ParallelEnumerable.Select(toP(s), Func<_,_>(fun x -> f x, x))