A higher-order function is defined as a function that returns a function as a result. Higher-order functions are common in the world of functional programming, but have only recently started to become more mainstream. As of C# 2.0, they are starting to become part of the language. A perfect example of their use would be with the generic List<T>.FindAll() method, which takes a Predicate<T> as an argument. A Predicate<T> is a delegate, which is a strongly typed method reference. One way to search for all the items in a list is to use anonymous delegates. For example, given a list of Falafel employees:
List<Falafel> falafels = new List<Falafel>();falafels.Add( new Falafel( "Lino", "Tadros" ) );falafels.Add( new Falafel( "John", "Waters" ) );falafels.Add( new Falafel( "Noel", "Rice" ) );falafels.Add( new Falafel( "Adam", "Anderson" ) );falafels.Add( new Falafel( "Xavier", "Pacheco" ) );falafels.Add( new Falafel( "Adam", "Markowitz" ) );falafels.Add( new Falafel( "Mike", "Dugan" ) );falafels.Add( new Falafel( "Bary", "Nusz" ) );falafels.Add( new Falafel( "Rick", "Miller" ) );falafels.Add( new Falafel( "Mike", "Saad" ) );falafels.Add( new Falafel( "Eric", "Titolo" ) );
You could search for all employees whose name starts with "A" like this:
falafels.FindAll( delegate( Falafel item ) { return item.FirstName.StartsWith( "A" ); } )
Output:Adam AndersonAdam Markowitz
However, if you wanted to perform another search for all employees whose name starts with "M", you'd have to write an entirely different anonymous delegate. That's where higher-order functions can help. Let's create a higher-order function that will return a delegate that searches for Falafels whose first name starts with a string we pass as a parameter:
public static class Predicates{ public static Predicate<Falafel> FirstNameStartsWith( string s ) { return delegate( Falafel item ) { return item.FirstName.StartsWith( s ); }; }}
Now we can search for employees whose name starts with any letter we want:
falafels.FindAll( Predicates.FirstNameStartsWith( "A" ) );falafels.FindAll( Predicates.FirstNameStartsWith( "M" ) );
Output:Adam AndersonAdam MarkowitzMike DuganMike Saad
But the fun doesn't stop there! If you define a set of primitive Predicates, you can define more complex Predicates as a combination of the simpler ones. Here is how to define higher-order functions that take a list of Predicates and returns a Predicate that returns the result of evaluating each of them and combining the result with the logical AND or OR operator:
public static Predicate<Falafel> And( params Predicate<Falafel>[] predicates ){ return delegate( Falafel item ) { foreach ( Predicate<Falafel> predicate in predicates ) if ( !predicate( item ) ) return false; return true; };}public static Predicate<Falafel> Or( params Predicate<Falafel>[] predicates ){ return delegate( Falafel item ) { foreach ( Predicate<Falafel> predicate in predicates ) if ( predicate( item ) ) return true; return false; };}
Using these, we can create a single Predicate that searches for all Falafels whose first name starts with "A" or "R" and whose last name starts with "M":
Predicate<Falafel> predicate = Predicates.Or( Predicates.FirstNameStartsWith( "A" ), Predicates.FirstNameStartsWith( "R" ) );predicate = Predicates.And( predicate, Predicates.LastNameStartsWith( "M" ) );falafels.FindAll( predicate );
Output:Adam MarkowitzRick Miller
Higher-order functions are a powerful abstraction, and they're only going to become more common and easier to write with C# 3.0 lambda expressions, so give them a try and see how they can help make your code more concise and expressive.
Remember Me
a@href@title, i, strike, u
Copyright © 2003-2008 Falafel Software Inc.
Subscribe to Falafel Blogs
The opinions expressed herein are Falafel's employees own personal opinions and do not represent Falafel Software's view in any way in case they go bananas!