Skip to content

Instantly share code, notes, and snippets.

@swlaschin
Created August 30, 2015 12:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save swlaschin/77fadc19acb8cc850276 to your computer and use it in GitHub Desktop.
Save swlaschin/77fadc19acb8cc850276 to your computer and use it in GitHub Desktop.
Creating a listing from a file system. Related blog post: http://fsharpforfunandprofit.com/posts/recursive-types-and-folds-3b/
(*
RecursiveTypesAndFold-3b-listing.fsx
Example: Creating a listing from a file system
Related blog post: http://fsharpforfunandprofit.com/posts/recursive-types-and-folds-3b/
*)
// ==============================================
// PART 3b - File System Listing
// ==============================================
// ==============================================
// Tree implementation
// ==============================================
type Tree<'LeafData,'INodeData> =
| LeafNode of 'LeafData
| InternalNode of 'INodeData * Tree<'LeafData,'INodeData> seq
module Tree =
let rec cata fLeaf fNode (tree:Tree<'LeafData,'INodeData>) :'r =
let recurse = cata fLeaf fNode
match tree with
| LeafNode leafInfo ->
fLeaf leafInfo
| InternalNode (nodeInfo,subtrees) ->
fNode nodeInfo (subtrees |> Seq.map recurse)
let rec fold fLeaf fNode acc (tree:Tree<'LeafData,'INodeData>) :'r =
let recurse = fold fLeaf fNode
match tree with
| LeafNode leafInfo ->
fLeaf acc leafInfo
| InternalNode (nodeInfo,subtrees) ->
// determine the local accumulator at this level
let localAccum = fNode acc nodeInfo
// thread the local accumulator through all the subitems using Seq.fold
let finalAccum = subtrees |> Seq.fold recurse localAccum
// ... and return it
finalAccum
let rec map fLeaf fNode (tree:Tree<'LeafData,'INodeData>) =
let recurse = map fLeaf fNode
match tree with
| LeafNode leafInfo ->
let newLeafInfo = fLeaf leafInfo
LeafNode newLeafInfo
| InternalNode (nodeInfo,subtrees) ->
let newSubtrees = subtrees |> Seq.map recurse
let newNodeInfo = fNode nodeInfo
InternalNode (newNodeInfo, newSubtrees)
let rec iter fLeaf fNode (tree:Tree<'LeafData,'INodeData>) =
let recurse = iter fLeaf fNode
match tree with
| LeafNode leafInfo ->
fLeaf leafInfo
| InternalNode (nodeInfo,subtrees) ->
subtrees |> Seq.iter recurse
fNode nodeInfo
// ==============================================
// IO FileSystem as Tree
// ==============================================
module IOFileSystem_Tree =
open System
open System.IO
type FileSystemTree = Tree<IO.FileInfo,IO.DirectoryInfo>
let fromFile (fileInfo:FileInfo) =
LeafNode fileInfo
let rec fromDir (dirInfo:DirectoryInfo) =
let subItems = seq{
yield! dirInfo.EnumerateFiles() |> Seq.map fromFile
yield! dirInfo.EnumerateDirectories() |> Seq.map fromDir
}
InternalNode (dirInfo,subItems)
// ----------------------------
// Define "totalSize"
// ----------------------------
let totalSize fileSystemItem =
let fFile acc (file:FileInfo) =
acc + file.Length
let fDir acc (dir:DirectoryInfo)=
acc
Tree.fold fFile fDir 0L fileSystemItem
// ----------------------------
// Define "largestFile"
// ----------------------------
let largestFile fileSystemItem =
let fFile (largestSoFarOpt:FileInfo option) (file:FileInfo) =
match largestSoFarOpt with
| None ->
Some file
| Some largestSoFar ->
if largestSoFar.Length > file.Length then
Some largestSoFar
else
Some file
let fDir largestSoFarOpt dirInfo =
largestSoFarOpt
// call the fold
Tree.fold fFile fDir None fileSystemItem
// ----------------------------
// Define "dirListing"
// ----------------------------
let dirListing fileSystemItem =
let printDate (d:DateTime) = d.ToString()
let mapFile (fi:FileInfo) =
sprintf "%10i %s %-s" fi.Length (printDate fi.LastWriteTime) fi.Name
let mapDir (di:DirectoryInfo) =
di.FullName
Tree.map mapFile mapDir fileSystemItem
// ---------------------------------
// testing
// ---------------------------------
// set the current directory to the current source directory
Directory.SetCurrentDirectory __SOURCE_DIRECTORY__
// get the current directory as a Tree
let currentDir = fromDir (DirectoryInfo("."))
// get the size of the directory tree
currentDir |> totalSize
// get the largestFile in the directory tree
currentDir |> largestFile
currentDir
|> dirListing
|> Tree.iter (printfn "%s") (printfn "\n%s")
// example output
(*
8315 10/08/2015 23:37:41 Fold.fsx
3680 11/08/2015 23:59:01 FoldAndRecursiveTypes.fsproj
1010 11/08/2015 01:19:07 FoldAndRecursiveTypes.sln
1107 11/08/2015 23:59:01 HtmlDom.fsx
79 11/08/2015 01:21:54 LinkedList.fsx
\git_repos\FoldAndRecursiveTypes\bin\Debug
\git_repos\FoldAndRecursiveTypes\bin
*)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment