From dd9fa60258836d1aecdb3d6471c34a872857014e Mon Sep 17 00:00:00 2001 From: Tuomas Hietanen Date: Fri, 5 Jul 2024 15:47:46 +0100 Subject: [PATCH 1/2] Minor refactoring: x = null to isNull x, and faster nicePascalName https://latkin.org/blog/2015/05/18/null-checking-considerations-in-f-its-harder-than-you-think/ --- docs/content/samples/presidents.fsx | 2 +- docs/tools/formatters.fsx | 2 +- src/Deedle.Excel/Excel.fs | 30 ++++----- src/Deedle/Common/BinomialHeap.fs | 4 +- src/Deedle/Common/Common.fs | 6 +- src/Deedle/FSharp.Data/CommonRuntime/IO.fs | 3 +- .../FSharp.Data/CommonRuntime/NameUtils.fs | 65 ++++++++++++------- .../CommonRuntime/StructuralInference.fs | 4 +- .../FSharp.Data/CommonRuntime/TextRuntime.fs | 4 +- src/Deedle/FSharp.Data/Net/Http.fs | 12 ++-- src/Deedle/Frame.fs | 3 +- src/Deedle/FrameExtensions.fs | 14 ++-- src/Deedle/FrameUtils.fs | 12 ++-- src/Deedle/Indices/LinearIndex.fs | 4 +- src/Deedle/Series.fs | 2 +- src/Deedle/Vectors/VectorHelpers.fs | 7 +- 16 files changed, 97 insertions(+), 77 deletions(-) diff --git a/docs/content/samples/presidents.fsx b/docs/content/samples/presidents.fsx index 1707b6b6..46f501a9 100644 --- a/docs/content/samples/presidents.fsx +++ b/docs/content/samples/presidents.fsx @@ -85,7 +85,7 @@ let presidentTerms = if string pos.``Basic title`` = "President" then // Get start and end year of the position let starty = DateTime.Parse(pos.From).Year - let endy = if pos.To = null then 2013 else + let endy = if isNull pos.To then 2013 else DateTime.Parse(pos.To).Year // Get their party let dem = pres.Party |> Seq.exists (fun p -> p.Party.Name = "Democratic Party") diff --git a/docs/tools/formatters.fsx b/docs/tools/formatters.fsx index cd8f5232..a253ff58 100644 --- a/docs/tools/formatters.fsx +++ b/docs/tools/formatters.fsx @@ -63,7 +63,7 @@ open FSharp.Charting.ChartTypes /// Extract values from any series using reflection let (|SeriesValues|_|) (value:obj) = let iser = value.GetType().GetInterface("ISeries`1") - if iser <> null then + if not (isNull iser) then let keys = value.GetType().GetProperty("Keys").GetValue(value) :?> System.Collections.IEnumerable let vector = value.GetType().GetProperty("Vector").GetValue(value) :?> IVector Some(Seq.zip (Seq.cast keys) vector.ObjectSequence) diff --git a/src/Deedle.Excel/Excel.fs b/src/Deedle.Excel/Excel.fs index 953b28dc..dfed394e 100644 --- a/src/Deedle.Excel/Excel.fs +++ b/src/Deedle.Excel/Excel.fs @@ -1,4 +1,4 @@ -module Deedle.Excel +module Deedle.Excel open Deedle @@ -87,21 +87,21 @@ let openNewExcelApplication () = excelApp let private assertInstance () = - if excelApp <> null then + if not (isNull excelApp) then let wbs = excelApp.Workbooks if wbs.Count = 0 then wbs.Add(Enums.XlWBATemplate.xlWBATWorksheet) |> ignore - if excelApp = null then + if isNull excelApp then openNewExcelApplication () |> ignore - elif activeWb <> null then + elif not (isNull activeWb) then activeWb.Activate() let private openWorkbook (readonly : bool) filename = excelApp.get_Workbooks().Open(filename, null, readonly) let getActiveWorkbook () = - if activeWb = null then + if isNull activeWb then excelApp.ActiveWorkbook else activeWb @@ -146,7 +146,7 @@ let getRealRange (range : obj) = assertInstance () match range with | :? string as str -> - let idx = str.IndexOf("!") + let idx = str.IndexOf '!' if idx > 0 then switchSheet (str.Substring(0, idx)) | _ -> () excelApp.Range(range) @@ -171,7 +171,7 @@ let private unionRanges r1 r2 = RealExcel (excelApp.Union(real1, real2)) let private putArray isH (arr : obj [,]) startRange = - if arr = null || Array2D.length1 arr = 0 || Array2D.length2 arr = 0 then + if isNull arr || Array2D.length1 arr = 0 || Array2D.length2 arr = 0 then startRange else let (height, width) = arr |> arraySize @@ -330,7 +330,7 @@ module internal XlHelper = |> aptest showCols (putArray false t.ColumnHeaders) |> putArray false t.DataArray |> aptest showRows (moveLeftCols 1 >> skipHistory 1) - |> aptest (tableStyle <> null) (applyToHistory (fun r-> + |> aptest (not (isNull tableStyle)) (applyToHistory (fun r-> ExcelStyles.ApplyTableStyle(r,tableStyle, showFilter, showRows))) // ---------------------------------------------------------------------------------------- @@ -361,7 +361,7 @@ module internal XlHelper = | _ -> box "" let formatExcelHeader (value:obj) = - if value = null then box "" else + if isNull value then box "" else match formatMap.TryGetValue(value.GetType()) with | true, f -> f value | _ -> box (value.ToString()) @@ -404,7 +404,7 @@ type Xl = for cf in fs do for c in cf.Columns do let colRange = range.Find(c,null,null,Enums.XlLookAt.xlWhole,null) - if colRange <> null then + if not (isNull colRange) then {state with LastRange = (RealExcel (colRange |> resize(rows,1)))} |> cf.Format |> ignore state @@ -415,7 +415,7 @@ type Xl = for cf in fs do for c in cf.Columns do let colRange = range.Find(c,null,null,Enums.XlLookAt.xlWhole,null) - if colRange <> null then + if not (isNull colRange) then {state with LastRange = (RealExcel colRange.EntireColumn)} |> cf.Format |> ignore state @@ -447,7 +447,7 @@ type Xl = let range = (lastRange |> convertRange) for cd in ds do let colRange = range.Find(cd.Column,null,null,Enums.XlLookAt.xlWhole,null) - if colRange <> null then + if not (isNull colRange) then let c = colRange.AddComment(cd.Description) c.Visible <- false c.Shape.TextFrame.AutoSize <- true @@ -459,7 +459,7 @@ type Xl = let range = (lastRange |> convertRange) let startRange = range.Find(cStart,null,null,Enums.XlLookAt.xlWhole,null) let endRange = range.Find(cEnd,null,null,Enums.XlLookAt.xlWhole,null) - if startRange <> null && endRange <> null then + if not( isNull startRange) && not (isNull endRange) then let startAddress = startRange.Address let endAddress = endRange.Address let r = getRealRange (startAddress + ":" + endAddress) @@ -528,7 +528,7 @@ type DynamicExcel(app, ?keepInSync) = and set (choice) = keepInSync <- choice member private this.createInstance() = - if localExcelApp = null then + if isNull localExcelApp then localExcelApp <- openNewExcelApplication() else () @@ -644,7 +644,7 @@ let GetFsiSeriesAndFrames (knownTypes : ICollection) = && pi.PropertyType <> typeof then let pv = pi.GetValue(null, Array.empty) - if pv <> null then + if not (isNull pv) then match pv with | GenericSeries argTypes | GenericFrame argTypes -> yield { name = pi.Name; type_ = pi.PropertyType; value = pi.GetValue(null, Array.empty); } diff --git a/src/Deedle/Common/BinomialHeap.fs b/src/Deedle/Common/BinomialHeap.fs index 8a618ad4..a2745c61 100755 --- a/src/Deedle/Common/BinomialHeap.fs +++ b/src/Deedle/Common/BinomialHeap.fs @@ -1,4 +1,4 @@ -/// Purely functional Binomial Heap implementation +/// Purely functional Binomial Heap implementation /// Based on: http://cs.hubfs.net/topic/None/56608 /// /// Characteristics: @@ -40,7 +40,7 @@ module BinomialHeap = { Comparer = comparer; Heap = [] } /// Returns true when the specified heap is emtpy - let isEmpty heap = heap.Heap = [] + let isEmpty heap = List.isEmpty heap.Heap /// Returns the rank of the specified tree node let internal rank (Node (r,_,_)) = r diff --git a/src/Deedle/Common/Common.fs b/src/Deedle/Common/Common.fs index c8fa3f12..9bb2d835 100644 --- a/src/Deedle/Common/Common.fs +++ b/src/Deedle/Common/Common.fs @@ -820,7 +820,7 @@ module Seq = // Walk over all windows; use 'f' to determine if the item // should be added - if so, add it, otherwise yield window let win = ref windows.First - while win.Value <> null do + while not (isNull win.Value) do let start, items = win.Value.Value let next = win.Value.Next if f start v then win.Value.Value <- start, v::items @@ -1004,7 +1004,7 @@ module Seq = // Walk over all windows; use 'f' to determine if the item // should be added - if so, add it, otherwise yield window let win = ref windows.First - while win.Value <> null do + while not (isNull win.Value) do let value, startIdx, endIdx = win.Value.Value let next = win.Value.Next if f value v then win.Value.Value <- value, startIdx, !index @@ -1467,7 +1467,7 @@ module Convert = else System.Convert.ChangeType(value, typeof<'T>) :?> 'T | ConversionKind.Safe -> if value :? 'T then value :?> 'T - elif value <> null then + elif not (isNull value) then match sourcesByTarget.TryGetValue(typeof<'T>) with | true, sources when sources.ContainsKey(value.GetType()) -> System.Convert.ChangeType(value, typeof<'T>) :?> 'T diff --git a/src/Deedle/FSharp.Data/CommonRuntime/IO.fs b/src/Deedle/FSharp.Data/CommonRuntime/IO.fs index 73dce85f..ee8ba16c 100644 --- a/src/Deedle/FSharp.Data/CommonRuntime/IO.fs +++ b/src/Deedle/FSharp.Data/CommonRuntime/IO.fs @@ -1,4 +1,4 @@ -/// Helper functions called from the generated code for working with files +/// Helper functions called from the generated code for working with files module FSharp.Data.Runtime.IO open System @@ -7,6 +7,7 @@ open System.IO open System.Text open FSharp.Data +[] type internal UriResolutionType = | DesignTime | Runtime diff --git a/src/Deedle/FSharp.Data/CommonRuntime/NameUtils.fs b/src/Deedle/FSharp.Data/CommonRuntime/NameUtils.fs index 5d962b25..46a98658 100644 --- a/src/Deedle/FSharp.Data/CommonRuntime/NameUtils.fs +++ b/src/Deedle/FSharp.Data/CommonRuntime/NameUtils.fs @@ -1,4 +1,4 @@ -/// Tools for generating nice member names that follow F# & .NET naming conventions +/// Tools for generating nice member names that follow F# & .NET naming conventions module FSharp.Data.Runtime.NameUtils open System @@ -9,6 +9,7 @@ open FSharp.Data.Runtime // -------------------------------------------------------------------------------------- // Active patterns & operators for parsing strings +// Todo: Convert to ValueTuple and [] when F# 6 is available let private tryAt (s:string) i = if i >= s.Length then None else Some s.[i] let private sat f (c:option) = match c with Some c when f c -> Some c | _ -> None let private (|EOF|_|) c = match c with Some _ -> None | _ -> Some () @@ -18,41 +19,59 @@ let private (|Lower|_|) = sat (fun c -> Char.IsLower c || Char.IsDigit c) // -------------------------------------------------------------------------------------- +let inline internal forall predicate (source : ReadOnlySpan<_>) = + let mutable state = true + let mutable e = source.GetEnumerator() + while state && e.MoveNext() do + state <- predicate e.Current + state + /// Turns a given non-empty string into a nice 'PascalCase' identifier let nicePascalName (s:string) = if s.Length = 1 then s.ToUpperInvariant() else // Starting to parse a new segment - let rec restart i = seq { + let rec restart i = match tryAt 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 tryAt s i with - | Upper _ -> yield! consume from true (i + 1) - | Lower _ -> yield! consume from false (i + 1) + | Upper _ -> consume from true (i + 1) + | Lower _ -> consume from false (i + 1) | _ -> - yield from, i - yield! restart (i + 1) } + let r1 = struct (from, i) + let r2 = restart (i + 1) + seq { + yield r1 + yield! r2 + } // Consume are letters of the same kind (either all lower or all upper) - and consume from takeUpper i = seq { - match tryAt s i with - | Lower _ when not takeUpper -> yield! consume from takeUpper (i + 1) - | Upper _ when takeUpper -> yield! consume from takeUpper (i + 1) - | Lower _ when takeUpper -> - yield from, (i - 1) - yield! restart (i - 1) + and consume from takeUpper i = + match takeUpper, tryAt s i with + | false, Lower _ -> consume from takeUpper (i + 1) + | true, Upper _ -> consume from takeUpper (i + 1) + | true, Lower _ -> + let r1 = struct (from, (i - 1)) + let r2 = restart (i - 1) + seq { + yield r1 + yield! r2 + } | _ -> - 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 - let sub = s.Substring(i1, i2 - i1) - if Array.forall Char.IsLetterOrDigit (sub.ToCharArray()) then - yield sub.[0].ToString().ToUpperInvariant() + sub.ToLowerInvariant().Substring(1) } + let sub = s.AsSpan(i1, i2 - i1) + if forall Char.IsLetterOrDigit sub then + yield Char.ToUpperInvariant(sub.[0]).ToString() + sub.Slice(1).ToString().ToLowerInvariant() } |> String.Concat /// Turns a given non-empty string into a nice 'camelCase' identifier diff --git a/src/Deedle/FSharp.Data/CommonRuntime/StructuralInference.fs b/src/Deedle/FSharp.Data/CommonRuntime/StructuralInference.fs index c94c9286..e6df78ae 100644 --- a/src/Deedle/FSharp.Data/CommonRuntime/StructuralInference.fs +++ b/src/Deedle/FSharp.Data/CommonRuntime/StructuralInference.fs @@ -303,7 +303,7 @@ let parseUnitOfMeasure (provider:IUnitsOfMeasureProvider) (str:string) = if str.EndsWith suffix then let baseUnitStr = str.[..str.Length - suffix.Length - 1] let baseUnit = provider.SI baseUnitStr - if baseUnit = null then + if isNull baseUnit then None else baseUnit |> trans provider |> Some @@ -313,4 +313,4 @@ let parseUnitOfMeasure (provider:IUnitsOfMeasureProvider) (str:string) = | Some _ -> unit | None -> let unit = provider.SI str - if unit = null then None else Some unit + if isNull unit then None else Some unit diff --git a/src/Deedle/FSharp.Data/CommonRuntime/TextRuntime.fs b/src/Deedle/FSharp.Data/CommonRuntime/TextRuntime.fs index e1525792..bc071cca 100644 --- a/src/Deedle/FSharp.Data/CommonRuntime/TextRuntime.fs +++ b/src/Deedle/FSharp.Data/CommonRuntime/TextRuntime.fs @@ -1,4 +1,4 @@ -namespace FSharp.Data.Runtime +namespace FSharp.Data.Runtime open System open System.Globalization @@ -19,7 +19,7 @@ type TextRuntime = then CultureInfo.InvariantCulture else let mutable cache = TextRuntime.cultureInfoCache - if cache = null then + if isNull cache then cache <- Collections.Generic.Dictionary () TextRuntime.cultureInfoCache <- cache match cache.TryGetValue cultureStr with diff --git a/src/Deedle/FSharp.Data/Net/Http.fs b/src/Deedle/FSharp.Data/Net/Http.fs index 6996b198..512c1387 100644 --- a/src/Deedle/FSharp.Data/Net/Http.fs +++ b/src/Deedle/FSharp.Data/Net/Http.fs @@ -1199,7 +1199,7 @@ module private HttpHelpers = source.Dispose () } - let runningOnMono = try System.Type.GetType("Mono.Runtime") <> null with e -> false + let runningOnMono = try not(isNull (System.Type.GetType "Mono.Runtime")) with e -> false let writeBody (req:HttpWebRequest) (data: Stream) = async { @@ -1212,7 +1212,7 @@ module private HttpHelpers = let reraisePreserveStackTrace (e:Exception) = try let remoteStackTraceString = typeof.GetField("_remoteStackTraceString", BindingFlags.Instance ||| BindingFlags.NonPublic); - if remoteStackTraceString <> null then + if not (isNull remoteStackTraceString) then remoteStackTraceString.SetValue(e, e.StackTrace + Environment.NewLine) with _ -> () raise e @@ -1223,7 +1223,7 @@ module private HttpHelpers = with // If an exception happens, augment the message with the response | :? WebException as exn -> - if exn.Response = null then reraisePreserveStackTrace exn + if isNull exn.Response then reraisePreserveStackTrace exn let responseExn = try let newResponse = new WebResponse(exn.Response) @@ -1332,7 +1332,7 @@ module private HttpHelpers = return! getResponseAsync req with | :? WebException as exc -> - if exc.Response <> null then + if not (isNull exc.Response) then return exc.Response else reraisePreserveStackTrace exc @@ -1601,14 +1601,14 @@ type Http private() = let cookies = CookieHandling.getCookiesAndManageCookieContainer uri resp.ResponseUri headers cookieContainer addCookiesFromHeadersToCookieContainer (defaultArg silentCookieErrors false) - let contentType = if resp.ContentType = null then "application/octet-stream" else resp.ContentType + let contentType = if isNull resp.ContentType then "application/octet-stream" else resp.ContentType let statusCode, characterSet = match resp with | :? HttpWebResponse as resp -> int resp.StatusCode, resp.CharacterSet | _ -> 0, "" - let characterSet = if characterSet = null then "" else characterSet + let characterSet = if isNull characterSet then "" else characterSet let stream = resp.GetResponseStream() diff --git a/src/Deedle/Frame.fs b/src/Deedle/Frame.fs index d67c373a..d984d1d6 100644 --- a/src/Deedle/Frame.fs +++ b/src/Deedle/Frame.fs @@ -1662,8 +1662,7 @@ and Frame<'TRowKey, 'TColumnKey when 'TRowKey : equality and 'TColumnKey : equal |> Seq.zip offsets // seq of (srcloc, label) |> Seq.zip frame.RowKeys // seq of (rowkey, (srcloc, label)) |> Seq.groupBy (fun (rk, (i, l)) -> l) // seq of (label, seq of (rowkey, (srcloc, label))) - |> Seq.map (fun (k, s) -> s) // seq of (seq of (rowkey, (srcloc, label))) - |> Seq.concat // seq of (rowkey, (srcloc, label)) + |> Seq.collect (fun (k, s) -> s) // seq of (rowkey, (srcloc, label)) |> Seq.zip offsets // seq of (dstloc, (rowkey, (srcloc, label))) |> Seq.map (fun (dst, (rowkey, (src, grp))) -> (grp, rowkey), (dst, src)) // seq of (label, rowkey), (dstloc, srcloc) diff --git a/src/Deedle/FrameExtensions.fs b/src/Deedle/FrameExtensions.fs index 24a09cc8..f8b4d914 100644 --- a/src/Deedle/FrameExtensions.fs +++ b/src/Deedle/FrameExtensions.fs @@ -96,7 +96,7 @@ type Frame = (if inferTypes.HasValue then Some inferTypes.Value else None) (if inferRows.HasValue then Some inferRows.Value else None) (Some schema) (Some missingValues) - (if separators = null then None else Some separators) (Some culture) + (if isNull separators then None else Some separators) (Some culture) (if maxRows.HasValue then Some maxRows.Value else None) (Some preferOptions) @@ -137,7 +137,7 @@ type Frame = (if inferTypes.HasValue then Some inferTypes.Value else None) (if inferRows.HasValue then Some inferRows.Value else None) (Some schema) (Some missingValues) - (if separators = null then None else Some separators) (Some culture) + (if isNull separators then None else Some separators) (Some culture) (if maxRows.HasValue then Some maxRows.Value else None) (if preferOptions.HasValue then Some preferOptions.Value else None) @@ -979,7 +979,7 @@ type FrameExtensions = /// [category:Data structure manipulation] [] static member Nest(frame:Frame, 'TColumnKey>) = - frame |> Frame.mapRowKeys (fun t -> t) |> Frame.nest + frame |> Frame.mapRowKeys id |> Frame.nest /// Given a data frame whose row index has two levels, create a series /// whose keys are the unique results of the keyselector projection, and @@ -1021,7 +1021,7 @@ type FrameExtensions = [] static member SaveCsv(frame:Frame<'R, 'C>, writer: TextWriter, [] includeRowKeys, [] keyNames, [] separator, [] culture) = let separator = if separator = '\000' then None else Some separator - let culture = if culture = null then None else Some culture + let culture = if isNull culture then None else Some culture let keyNames = if keyNames = Unchecked.defaultof<_> then None else Some keyNames FrameUtils.writeCsv (writer) None separator culture (Some includeRowKeys) keyNames frame @@ -1042,7 +1042,7 @@ type FrameExtensions = [] static member SaveCsv(frame:Frame<'R, 'C>, path:string, [] includeRowKeys, [] keyNames, [] separator, [] culture) = let separator = if separator = '\000' then None else Some separator - let culture = if culture = null then None else Some culture + let culture = if isNull culture then None else Some culture let keyNames = if keyNames = Unchecked.defaultof<_> then None else Some keyNames use writer = new StreamWriter(path) FrameUtils.writeCsv writer (Some path) separator culture (Some includeRowKeys) keyNames frame @@ -1064,7 +1064,7 @@ type FrameExtensions = static member SaveCsv(frame:Frame<'R, 'C>, path:string, keyNames, [] separator, [] culture) = use writer = new StreamWriter(path) let separator = if separator = '\000' then None else Some separator - let culture = if culture = null then None else Some culture + let culture = if isNull culture then None else Some culture FrameUtils.writeCsv writer (Some path) separator culture (Some true) (Some keyNames) frame /// Returns the data of the frame as a .NET `DataTable` object. The column keys are @@ -1395,7 +1395,7 @@ type FrameExtensions = [] static member SaveCsv(frame:Frame<'R, 'C>, stream:Stream, [] includeRowKeys, [] keyNames, [] separator, [] culture) = let separator = if separator = '\000' then None else Some separator - let culture = if culture = null then None else Some culture + let culture = if isNull culture then None else Some culture let keyNames = if keyNames = Unchecked.defaultof<_> then None else Some keyNames use writer = new StreamWriter(stream) FrameUtils.writeCsv (writer) None separator culture (Some includeRowKeys) keyNames frame diff --git a/src/Deedle/FrameUtils.fs b/src/Deedle/FrameUtils.fs index e5d8f282..d787b587 100644 --- a/src/Deedle/FrameUtils.fs +++ b/src/Deedle/FrameUtils.fs @@ -104,7 +104,7 @@ module internal Reflection = /// Given value, return names, types and values of all its IDictionary contents (or None) let expandDictionary (value:obj) = - if value = null then None else + if isNull value then None else match value with | :? ISeries as sstr -> seq { for key in sstr.Index.Keys do @@ -231,7 +231,7 @@ module internal Reflection = /// implements the interface, but we do not know it statically) let convertRecordSequenceUntyped(data:System.Collections.IEnumerable) = let seqTy = data.GetType().GetInterface("System.Collections.Generic.IEnumerable`1") - if seqTy = null then invalidOp "convertRecordSequenceUntyped: Argument must implement seq." + if isNull seqTy then invalidOp "convertRecordSequenceUntyped: Argument must implement seq." let argTy = seqTy.GetGenericArguments().[0] let bindFlags = BindingFlags.NonPublic ||| BindingFlags.Static ||| BindingFlags.Public let mi = typeof.GetMethod("ConvertRecordSequence", bindFlags).MakeGenericMethod(argTy) @@ -276,7 +276,7 @@ module internal FrameUtils = // Create flat headers (if there is a hierarchical column index) let flattenKeys keys = keys |> Seq.map (fun objs -> - objs |> Seq.map (fun o -> if o = null then "" else o.ToString()) + objs |> Seq.map (fun o -> if isNull o then "" else o.ToString()) |> String.concat " - ") /// Store data frame to a CSV file using the specified information @@ -419,12 +419,12 @@ module internal FrameUtils = // from C#, we get `Some default(T)` and so we need to check for both here! let inferRows = defaultArg inferRows 100 let schema = defaultArg schema "" - let schema = if schema = null then "" else schema + let schema = if isNull schema then "" else schema let culture = defaultArg culture "" - let culture = if culture = null then "" else culture + let culture = if isNull culture then "" else culture let cultureInfo = System.Globalization.CultureInfo.GetCultureInfo(culture) let missingValues = defaultArg missingValues TextConversions.DefaultMissingValues - let missingValues = if missingValues = null then TextConversions.DefaultMissingValues else missingValues + let missingValues = if isNull missingValues then TextConversions.DefaultMissingValues else missingValues let preferOptionals = defaultArg preferOptions false // Default parameters that cannot be overriden (Frames can always contain NAs) diff --git a/src/Deedle/Indices/LinearIndex.fs b/src/Deedle/Indices/LinearIndex.fs index 86e9c74d..874a9cde 100644 --- a/src/Deedle/Indices/LinearIndex.fs +++ b/src/Deedle/Indices/LinearIndex.fs @@ -1,4 +1,4 @@ -// -------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------- // A concrete implementation of an index. Represents an index where the values are sto- // red in an array (or similar structure) with linearly ordered addresses without holes. // -------------------------------------------------------------------------------------- @@ -60,7 +60,7 @@ type LinearIndex<'K when 'K : equality> let mutable lookup = null let ensureLookup () = - if lookup = null then + if isNull lookup then let dict = Dictionary<'K, Address>() let mutable idx = 0L for k in keys do diff --git a/src/Deedle/Series.fs b/src/Deedle/Series.fs index 4686fb28..a3f9f1b2 100644 --- a/src/Deedle/Series.fs +++ b/src/Deedle/Series.fs @@ -835,7 +835,7 @@ and let ks key = x.TryGet(key) |> OptionalValue.map (fun v -> keySelector.Invoke(KeyValuePair(key, v))) let cmd = indexBuilder.GroupBy(index, ks, VectorConstruction.Return 0) let newIndex = Index.ofKeys (cmd |> ReadOnlyCollection.map fst) - let newGroups = cmd |> Seq.map snd |> Seq.map (fun sc -> + let newGroups = cmd |> Seq.map (snd >> fun sc -> Series(fst sc, vectorBuilder.Build(newIndex.AddressingScheme, snd sc, [| x.Vector |]), vectorBuilder, indexBuilder)) Series<'TNewKey, _>(newIndex, Vector.ofValues newGroups, vectorBuilder, indexBuilder) diff --git a/src/Deedle/Vectors/VectorHelpers.fs b/src/Deedle/Vectors/VectorHelpers.fs index a3a7dc60..c0a19098 100644 --- a/src/Deedle/Vectors/VectorHelpers.fs +++ b/src/Deedle/Vectors/VectorHelpers.fs @@ -295,7 +295,7 @@ let tryConvertType<'R> conversionKind (vector:IVector) : OptionalValue Seq.choose (fun v -> - if v.HasValue && (box v.Value) <> null + if v.HasValue && not(isNull (box v.Value)) then Some (box v.Value) else None) |> Seq.headOrNone |> Option.map (Convert.canConvertType<'R> conversionKind) @@ -434,6 +434,7 @@ module Inference = // type, we choose 'int', 'int64', 'float' (for numbers) or 'string' (for strings and characters) // In principle, we could do better and find "least upper bound" of the conversion relation // but choosing one of the common types seems to be good enough. + /// Could utilize some [] when F# 6 let inline isType types t = if List.exists ((=) t) types then Some() else None let intTypes = [ typeof; typeof; typeof; typeof; typeof ] let int64Types = intTypes @ [ typeof; typeof ] @@ -441,7 +442,7 @@ module Inference = let stringTypes = [ typeof; typeof ] /// Classsify type as one of the supported primitives - let (|Top|_|) (ty:System.Type) = if ty = null then Some() else None + let (|Top|_|) (ty:System.Type) = if isNull ty then Some() else None let (|Bottom|_|) ty = if ty = typeof then Some() else None let (|AsInt|_|) ty = isType intTypes ty let (|AsInt64|_|) ty = isType int64Types ty @@ -496,7 +497,7 @@ let findCommonSupertype types = /// Given object array, create a typed vector of the best possible type let createInferredTypeVector (builder:IVectorBuilder) (data:obj[]) = let vectorType = data |> Seq.map (fun v -> - if v = null then Inference.Top else v.GetType()) |> findCommonSupertype + if isNull v then Inference.Top else v.GetType()) |> findCommonSupertype createTypedVector builder vectorType data // -------------------------------------------------------------------------------------- From 1c3d03550522eb39cbbce71f5e94115975e97856 Mon Sep 17 00:00:00 2001 From: Tuomas Hietanen Date: Sun, 7 Jul 2024 22:29:57 +0100 Subject: [PATCH 2/2] yield need to be this way for tail-recursion --- .../FSharp.Data/CommonRuntime/NameUtils.fs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Deedle/FSharp.Data/CommonRuntime/NameUtils.fs b/src/Deedle/FSharp.Data/CommonRuntime/NameUtils.fs index 46a98658..b1b8b0be 100644 --- a/src/Deedle/FSharp.Data/CommonRuntime/NameUtils.fs +++ b/src/Deedle/FSharp.Data/CommonRuntime/NameUtils.fs @@ -42,11 +42,9 @@ let nicePascalName (s:string) = | Upper _ -> consume from true (i + 1) | Lower _ -> consume from false (i + 1) | _ -> - let r1 = struct (from, i) - let r2 = restart (i + 1) seq { - yield r1 - yield! r2 + yield struct (from, i) + yield! restart (i + 1) } // Consume are letters of the same kind (either all lower or all upper) and consume from takeUpper i = @@ -54,18 +52,14 @@ let nicePascalName (s:string) = | false, Lower _ -> consume from takeUpper (i + 1) | true, Upper _ -> consume from takeUpper (i + 1) | true, Lower _ -> - let r1 = struct (from, (i - 1)) - let r2 = restart (i - 1) seq { - yield r1 - yield! r2 + yield struct (from, (i - 1)) + yield! restart (i - 1) } | _ -> - let r1 = struct(from, i) - let r2 = restart i seq { - yield r1 - yield! r2 } + yield struct(from, i) + yield! restart i } // Split string into segments and turn them to PascalCase seq { for i1, i2 in restart 0 do