Monday, September 19, 2011

Some F# early learning and reference

I'm translating my bulk code detector from c# to f#, to learn f#. It's a linqpad c# program that recurses a directory tree organizing code

  • by
    • highest line count 
    • highest line count by directory 
    • highest line count by base filename (the stuff before the .) 
  • includes 
    • non-whitespace count 
    • potential magic numbers count 
    • " count


//path returns string
let path :string= 
  let userPath=Util.ReadLine("SourceDirectory?",@"D:\projects\")
  let exists=System.IO.Directory.Exists(userPath)
  if not(exists) then //guard clause
    raise(DirectoryNotFoundException(userPath))
  userPath
  
let doTestFileExclude = false

//fileExclude= Func<string,bool> a=>
let fileExclude  (a:string):bool = 
  a.EndsWith("designer.cs",StringComparison.CurrentCultureIgnoreCase)||
  a.StartsWith("jquery-",StringComparison.CurrentCultureIgnoreCase)||
  a.StartsWith("AssemblyInfo");
  
let pathExclude (a:string) :bool =
  a.Contains(@"\Database\")||
  a.Contains("Service References")||
  a.Contains("Web References") ||
  a.Contains("PackageTmp") ||
  a.StartsWith(@"~\Web\Scripts\Mvc3")

//record, class, struct, or discriminated union?  
type  CountSettings = {
  Path: string
  Patterns: IEnumerable<string>
  FileExclude: string -> bool
  PathExclude: string-> bool
  }
//instance of above type
let currentSettings:CountSettings=  {
  Path=path
  Patterns=["*.cs";"*.js"]
  FileExclude=fileExclude
  PathExclude=pathExclude
  }

//demonstrating access modifier  
let private testFileExclude() =
  let testCases = ["test";"test.designer.cs";"test.Designer.cs"]
  let mapped = testCases |>
    List.map(fun (fn:string) ->
      (fn,currentSettings.FileExclude(fn))
    )
  mapped.Dump("testFileExclude done")
  
if doTestFileExclude then
  do testFileExclude()

//set cwd (not a functional call, imperative?)
System.Environment.CurrentDirectory=currentSettings.Path
//public ~IEnumerable<T> visitDir(string dir,T1 dirFilter, T2 patterns, T3 fileFilter)
let rec visitAll (dir:string) patterns=seq{
  for pattern in patterns do
    yield! Directory.EnumerateFiles(dir,pattern)
  for subdir in Directory.EnumerateDirectories(dir) do
    yield! visitAll subdir patterns
  }
  
visitAll currentSettings.Path currentSettings.Patterns 
  |> Seq.length 
  |> fun x->x.Dump("LengthAll")


let rec visitDir (dir:string) dirFilter patterns fileFilter=
  seq{
  for pattern in patterns do
    for file in Directory.EnumerateFiles(dir,pattern) do
      if not(fileFilter(file)) then
        yield file
  for subdir in Directory.EnumerateDirectories(dir) do
    if not(dirFilter(subdir)) then
      yield! visitDir subdir dirFilter patterns fileFilter
  }
  
let visitDirResult= 
  visitDir currentSettings.Path currentSettings.PathExclude
    currentSettings.Patterns
      currentSettings.FileExclude
visitDirResult |> Seq.length |> fun x->x.Dump("visitDirLength")
visitDirResult |> fun x ->x.Dump("visitDirResult")

//Extension method on string
type System.String with
  member x.Right(index) = x.Substring(x.Length - index)

No comments:

Post a Comment