This is the third part in a “Questions and Answers”-series about the F# BootCamp in Leipzig. This time we will look at structural comparison and structural equality.
Question 6: Please describe the terms “Structural Comparison” and “Structural Equality”.
This was a simple question. Basically the answer is F# provides a default implementation for IComparable and .Equals() for all custom types. This default implementation compares all public fields of the two instances. Let’s consider some samples:
Tuples
let a = 3,4,"foo"
let b = 3,4,"bar"
printfn "a > b = %b" (a > b) // true
printfn "a < b = %b" (a < b) // false
Records
type MySimpleRecord =
{ a: int;
b: int;}
type MyCompositeRecord =
{ x: string;
y: int;
z: MySimpleRecord}
let a =
{ x = "Test";
y = 3;
z = {a = 1; b = 4;}}
let b =
{ x = "Test";
y = 3;
z = {a = 1; b = 2;}}
// Structural comparison
printfn "a > b = %b" (a > b) // true
printfn "a < b = %b" (a < b) // false
printfn "Min(a,b) = %A" (min a b)
printfn "compare(a,b) = %d" (compare a b)
Lists
let a = [3; 2; 4; 5]
let b = [3; 2; 4; 3; 3]
// Structural comparison
printfn "a > b = %b" (a > b) // true
printfn "a < b = %b" (a < b) // false
printfn "Min(a,b) = %A" (min a b)
printfn "compare(a,b) = %d" (compare a b)
Tags:
.NET User Group Leipzig,
F#,
Functional Programming
One of the nice properties of functional programming languages is the easy extensibility of custom functions. Let’s consider a simple F# function (from “FAKE – F# Make”) for a recursive directory copy:
open System
open System.IO
/// Copies a directory recursive
/// Thanks to Robert Pickering http://strangelights.com/blog/
/// param target: target directory : string
/// param source: source directory : string
let CopyDir target source =
Directory.GetFiles(source, "*.*", SearchOption.AllDirectories)
|> Seq.iter (fun file ->
let newFile = target + file.Remove(0, source.Length)
printf "%s => %s" file newFile
Directory.CreateDirectory(Path.GetDirectoryName(newFile)) |> ignore
File.Copy(file, newFile, true))
If we want to allow users to set custom file filters, we can add a third parameter:
/// Copies a directory recursive
/// and allows to filter the files
/// Thanks to Robert Pickering http://strangelights.com/blog/
/// param target: target directory : string
/// param source: source directory : string
/// param filterFile: FilterFunction: string -> bool
let CopyDirFiltered target source filterFile =
Directory.GetFiles(source, "*.*", SearchOption.AllDirectories)
|> Seq.filter filterFile
|> Seq.iter (fun file ->
let newFile = target + file.Remove(0, source.Length)
printfn "%s => %s" file newFile
Directory.CreateDirectory(Path.GetDirectoryName(newFile)) |> ignore
File.Copy(file, newFile, true))
Now we can define some filter functions:
/// Exclude SVN files (path with .svn)
/// excludeSVNFiles: string -> bool
let excludeSVNFiles (path:string) = not <| path.Contains ".svn"
/// Includes all files
/// allFiles: string -> bool
let allFiles (path:string) = true
Now it is possible to use CopyDirFiltered in the following ways:
/// Copies all files <=> same as CopyDir
CopyDirFiltered "C:\\target" "C:\\source" allFiles
/// Copies all files except SVN files
CopyDirFiltered "C:\\target" "C:\\source" excludeSVNFiles
/// Copies all files only if random number <> 2
let r = new Random()
CopyDirFiltered "C:\\target" "C:\\source" (fun path -> r.Next(5) <> 2)
Extensibility of functions in C#
Of course we can do the same thing in C# 3.0:
/// <summary>
/// Copies a directory recursive
/// and allows to filter the files
/// </summary>
/// <param name="target">The target.</param>
/// <param name="source">The source.</param>
/// <param name="fileFilter">The file filter.</param>
public static void CopyDirFiltered(string target, string source,
Func<string, bool> fileFilter)
{
string[] allFiles = Directory.GetFiles(
source, "*.*", SearchOption.AllDirectories);
foreach (string file in from f in allFiles
where fileFilter(f)
select f)
{
string newFile = target + file.Remove(0, source.Length);
Console.WriteLine("{0} => {1}", file, newFile);
Directory.CreateDirectory(Path.GetDirectoryName(newFile));
File.Copy(file, newFile, true);
}
}
Now it is easy to use the C# function with lambdas:
“A lambda expression is an anonymous function that can contain expressions and statements, and can be used to create delegates or expression tree types.”
[MSDN]
Func<string, bool> filterSVN = x => !x.Contains(".svn");
Func<string, bool> allFiles = x => true;
/// Copies all files <=> same as CopyDir
CopyDirFiltered("C:\\target", "C:\\source", allFiles);
/// Copies all files except SVN files
CopyDirFiltered("C:\\target", "C:\\source", filterSVN);
/// Copies all files only if random number <> 2
var r = new Random();
CopyDirFiltered("C:\\target", "C:\\source", path => r.Next(5) != 2);
Keeping this simple technique in mind allows to create very flexible functions.
Tags:
C#,
F#,
Fake,
Functional Programming