Within this article I want to show you a little application which recursive searches all files in directories and subdirectories and write the results to a text file. This article contains two possible solutions for this application. One solution is based on lists and the other one uses sequences.
Aim of the application
The application should find all files in all directories relative to the location where the application was executed. The list of files should be written to a text file. The file names should contain the relative path according to the application.
For example if the application was executed in directory: “C:\MyDir” it should find all files in all subdirectories of “C:\MyDir”. If there exists the following file “C:\MyDir\MySubDir\myFile.txt” then this file will be found. As output of the application a text file will be created which contains the relative path of the files. Therefore in this example the relative name “MySubDir\myFile.txt” will be written to the output file.
Solution with List data type
The following source code shows a possible solution. This application uses lists as data type.
open System; open System.IO; let applicationDirectory = Environment.CurrentDirectory; let rec getAllFilesNames directories = match directories with | [] -> [] | x::xs -> (Directory.GetFiles(x) |> Array.toList) @ (Directory.GetDirectories(x) |> Array.toList |> getAllFilesNames) @ getAllFilesNames xs let writeToFile (fileName:string) (values:string list) = let outFile = new StreamWriter(fileName) let rec writeValuesToFile (values:string list) = match values with | [] -> ignore | x::xs -> outFile.WriteLine(x.Substring(applicationDirectory.Length+1)) writeValuesToFile(xs) writeValuesToFile values |> ignore outFile.Close() let fileNames = Directory.GetDirectories(applicationDirectory) |> Array.toList |> getAllFilesNames writeToFile (applicationDirectory + @"\documents.txt") fileNames
The function getAllFilesNames has an input parameter with a list of all directories which should be searched through. By using pattern matching it would be checked if this list is empty or if it contains content. If the list is empty, an empty list will be returned. This will finish the recursive call of the function. If the list contains elements, the first list element will be used to find all files and all directories within this directory. The files found will be added to the resulting list and the directories found will be passed to a recursive call of getAllFilesNames to find all files in this directories. Furthermore another recursive call to getAllFilesNames is necessary for the remaining part of the directory list.
The function writeToFile will create a stream writer to write the content of the given list to a text file. Within the scope of this function another function is created to do a recursive loop over the list. Of course you can also use a for-loop, but this is only syntactic sugar.
for x=1 to values |> List.length do outFile.WriteLine(values.[x].Substring(applicationDirectory.Length+1))
Solution with Sequence data type
At next I want to show you another solution of the same task. In the second solution the sequence data type is used. Furthermore a lot more of syntactic sugar is used. This will result in a shorter application.
open System; open System.IO; let applicationDirectory = Environment.CurrentDirectory; let rec getAllFilesNames directory = seq { yield! Directory.EnumerateFiles(directory) for d in Directory.EnumerateDirectories(directory) do yield! getAllFilesNames d} let outFile = new StreamWriter(applicationDirectory + @"\documents.txt") seq{for d in Directory.EnumerateDirectories(applicationDirectory) do yield! getAllFilesNames d} |> Seq.iter (fun (x:string) -> outFile.WriteLine(x.Substring(applicationDirectory.Length+1))) outFile.Close()
Both solutions create the same result file but they are implemented very different. In my opinion there is no better or worse solution. Maybe they differ in execution speed, but I think this difference is very small. Therefore, which solution you prefer, is more a choice of you coding style. I like the first solution because it shows the pure functional programming style with immutable data types, recursive function calls and pattern matching.
Summary
By using F# such a little application may be created very easily by writing only a few lines of code. Furthermore there are several possible solutions which may use different data types or use different features of the programming language.