Created
August 30, 2015 12:56
-
-
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/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* | |
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