flow.
"Flow is a condition of deep, nearly meditative involvement." - Tom DeMarco

The Predicate Delegate

Monday, March 05, 2007 11:51 AM

.NET has a little known delegate known as the Predicate which can make searching much easier and much cleaner.  Predicates are simply user defined boolean conditions which we can define right in code to help us sort through collections such as arrays.

Let's say, for example, that I had an array of numbers and I wanted to find all of the numbers which were odd.  The obvious way is simply to loop through them checking each number for its "oddness" and adding it to an output array, like so: 

        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
         int[] odds = GetOdds(numbers);
  
        private static int[] GetOdds(int[] numbers)
        {
            int[] odds = new int[numbers.Length];
            int counter = 0;
            for (int i = 0; i < numbers.Length; i++)
            {
                if ((numbers[i] % 2) != 0)
                {
                    // Found an odd!
                    odds[counter++] = numbers[i];
                }
            }
 
            return odds;
        }
 

Sure, this works...but it's a little ugly.  Especially for such a trivial command, plus due to how we initialized our odds array we're going to have some empty blank strings at the end that we'll need to account for.  In fact, when we really look at it the majority of our method seems to be just overhead to support the output array, looping through the input array, etc.  The core logic itself is actually constrained to just one line:

 
if ((numbers[i] % 2) != 0)

It seems like that there should be a cleaner way to do this and, lucky for us, there is!  Using Predicates, we can actually just write this little line of code into a stand alone method and reference it directly from the Array.FindXXX() methods.  Check it out...

        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };        
        int[] odds = Array.FindAll<int>(numbers, IsOdd);

        private static bool IsOdd(int number)
        {
            return ((number % 2) != 0);
        }
 

Now the only thing that we have to worry about is our core logic, .NET takes care of all of the additional overhead for us!

 

We can even reference it inline using anonymous delegates! Check out how we pull just the objects of type Dog from this array using another predicate...


 

            Animal[] animals = { new Dog("Fido"), new Cat("Fluffy"), 
            new Dog("Bruiser"), new Cat("Leroy")};
 
            Dog[] allDogsGoToThisArray =
                Array.FindAll<Animal>(animals,
                    delegate(Animal animal) 
		      {
                        return (animal is Dog);
                    });

Or we can even do it C# 3.0 style courtesy of lambda expressions...

            Dog[] allDogsGoToThisArray =
                Array.FindAll<Animal>(animals,
                    animal => (animal is Dog));

We can use this technique for any of the Find methods off of the Array class (Find, FindAll, FindIndex, FindLast, and FindLastIndex) as well as the Exists method to see if any objects matching our condition exist in the array.  Check it out... 

 

	bool hasACat = Array.Exists<Animal>(animals, IsCat);
        private static bool IsCat(Animal animal)
        {
            return (animal is Cat);
        }
 
The counterpart of this method would be the TrueForAll variation which will tell us if all of our animals our Cats.
	bool allCats =
                Array.TrueForAll<Animal>(animals, IsCat);

 

Hopefully this will help clean up some of those annoying routines you have scattered through your code that are just in place solely to get what you need from an array, check if an array has what you need, or just pull out a certain type of object from your array.  Next time we'll talk about the Predicate's closely related cousin the Action and how he can help you quickly and cleanly perfom actions to an entire list of objects.

kick it on DotNetKicks.com

Feedback

# re: The Predicate object

Yeah, predicates are cool stuff. Whenever i hear about predicates, i think about cats and dogs. What happened to Spot, Lucky and Frisky? Hope they're alright. 3/5/2007 6:17 PM | Joachim

# re: The Predicate object

Haha! Don't worry, they're OK....they're just taking a little breather right now :) 3/5/2007 9:18 PM | Jeremy

 re: The Predicate object

C# and Prolog converge, finally. Thanks for writing your ariticle, I found it useful. 3/5/2007 11:37 PM | Ciz

# re: The Predicate object

Predicate as used inside .NET is also known as The Specification Pattern elsewhere. 3/6/2007 3:22 AM | Michal Talaga

# re: The Predicate object

Cool, I'm really glad that you guys dug the article and that it helped you out!

That's also very cool about the specification pattern, I didn't know that. I'd like to see what I can find on that :o) 3/6/2007 1:25 PM | Jeremy

 re: The Predicate object

.NET Predicate and Action delegates seem to me like applications of the Strategy Pattern, with some methods, such as Array.FindAll, being examples of the Specification Pattern (in the form of a specialised type of Strategy). 3/6/2007 8:53 PM | Ciz

 re: The Predicate object

C# is truly becoming the homer mobile of programming languages. 3/7/2007 10:54 AM | Montressor

# New and Notable 148

Still real tired from my Oklahoma trip , partying with Raymond sure is exhausting-). Agile/Development 3/7/2007 12:11 PM | Sam Gentile

# re: The Predicate Delegate

Nice article, I will be pointing to this in a future post. I use predicates a lot, and often with anonymous methods. I think the most important reason, which you mentioned at the end of the post, is that it separates concerns very nicely. How often do we not iterate through collections in for- or foreach-loops? What if the logic for that iteration changes because the collection is given new requirements (perhaps it should iterate backwards instead)? The code for iterating through a collection belongs - in the collection class itself of course. We just decide what we want to do with each element and let the collection class decide how we get those elements. 3/12/2007 11:28 AM | Chris Hedgate

# re: The Predicate Delegate

Awesome! I'm really glad you liked it! I feel the same way, if another developer is reading your code they can ignore the standard constructs (Find, FindAll, etc) and just focus completely on what we are doing with the predicate. The standard constructs are essentially just context clues. 3/12/2007 9:04 PM | Jeremy

 re: The Predicate Delegate

I'm kinda new to .net and I am working with VB.net and I cannot get your sample to work correctly. Here is what I have:

Module Module1
Sub Main()
Dim numbers As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
Dim odds As Integer() = Array.FindAll(Of Integer)(numbers, IsOdd)

End Sub

Private Function IsOdd(ByVal number As Integer) As Boolean
Return ((number Mod 2) <> 0)
End Function
End Module

The error I get is: Argument not specified for parameter 'number' of 'Private Function IsOdd(number As Integer) As Boolean'. 4/9/2007 6:23 PM | JohnB

# re: The Predicate Delegate

Hey JohnB,

Thanks for the comment. Good question, I hadn't thought about how this would look in VB. I played with it a little bit and it looks like that the gotcha you were running into was that the Array.FindAll() subroutine requires the AddressOf operator in front of the predicate. I would guess that this is due to the fact that delegates are, in effect, just function pointers which means that the subroutine needs the address of the method that it is pointing to. I'm not sure though, perhaps someone who knows VB better than me would like to chime in here...

I've put together some sample code and enclosed it below. I've also added some code at the bottom of the main subroutine to prove, mainly to myself ;), that the routine actually works.

Module Module1

Sub Main()
Dim numbers As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
Dim odds As Integer() = Array.FindAll(numbers, AddressOf IsOdd)

For Each oddNumber In odds
Console.WriteLine(oddNumber)
Next

End Sub

Private Function IsOdd(ByVal number As Integer) As Boolean
Return ((number Mod 2) <> 0)
End Function

End Module

I hope this helps. I'll hold on to the solution for a few days and if you'd like I'd be more than happy to email it to you. You can just drop me a line at Jeremy@JeremyJarrell.com if you're interested.

Thanks again for the comment, that was a really interesting question. I tend to get the C# blinders on when I write some of this stuff, so it's great to have these questions that really make me look at the topics from a different angle :)

Jeremy 4/9/2007 10:55 PM | Jeremy

 re: The Predicate Delegate

Thanks Jeremy! I guess I should have picked up on the keyword "delegate" and remembered about AddressOf. Works like a charm! Thanks again for taking the time to look at it. 4/10/2007 3:06 AM | JohnB

# re: The Predicate Delegate

No problem! Just glad I could help out! 4/10/2007 8:28 AM | Jeremy

 re: The Predicate Delegate

Isn't there a parenthesis missing in the lambda expression example? 4/13/2007 1:06 PM | Eric_Formark

# re: The Predicate Delegate

Oops! You're right! Thanks for catching that. I've corrected the code in the post.

Jeremy 4/13/2007 2:57 PM | Jeremy

# re: The Predicate Delegate

Hey that's cool. This is the first time I've seen this. Thanks! 10/2/2007 1:52 AM | Carlo

 re: The Predicate Delegate

thanks for the example you have made that clear for me now. 11/15/2007 9:40 AM | rob bartley

 re: The Predicate Delegate

I was just wondering if there is a way to call the predicate function in a way that it returns the opposite results?

So in your example with IsOdd, calling it in such a way that it returns the status as if I wrote an IsEven function.

I'd just rather not have to write another function where the results are the negation of another function that is already written. 3/10/2008 4:41 PM | Chris

 Good but low longevity

Nice terse representation of a filter algorithm. Please consider that this is of lower maintainability because many developers either do not know about predicates or takes extra time to understand.

Straightforward and even dumb code is easier in the long run.

The idea is that one should spend time making the higher level, more complex algorithms easier to understand, maintain and work faster instead of using more advanced language features to save 2-5 lines of code. 3/14/2008 12:45 PM | D

 Origins of the C# Predicate construct

The name and conceptual origin of the C# predicate delegate comes from the symbolic logic subject area of the same name.

Predicate Logic (Second Order logic) is these days attributed to the 19th century work of the German mathematician and philosopher, Gottlob Frege.

While the predicate construct bears a passing similarity to the Specification pattern, the underlying idea here is not procedural at all, rather it is set theoretic.

In a nutshell, if a set of objects x all possess the same property P we say 'Px' where x belongs to some domain of objects. The interesting aspect from the CS perspective is the idea that the existence of a decision procedure capable of determining whether 'P' obtains is secondary to the question of whether a given 'x' *is* 'P'.

The .net architects got this exactly right. By modeling predicates as delegates, they elegantly achieve the decoupling of choice algorithm from property assignment. Attributing a predicate to an object effectively becomes a declarative act.

As a result we can now ask the question of whether a given property holds of an object without knowing or caring how the decision as to how that question is answered is actually implemented.

Even better, because the predicate implementation is generic, its use cases extend far beyond the 'for free' implementations already wired into the various .net collection classes. Great work, Jeremy! 7/29/2008 12:59 PM | Martin Haynes

# re: The Predicate Delegate

Wow Martin!

Thanks for the background, that's awesome!!! I've always had a strong interest in the underlying mathematics and logic of CS so I love any chance to learn more about it. You've given me some great food for thought.

Thanks again for sharing the great information!
Jeremy 7/30/2008 8:25 PM | Jeremy

 re: The Predicate Delegate

Thank you very much for this very clean explanation. 8/7/2008 7:05 PM | C.Avci

# re: The Predicate Delegate

My pleasure, glad it could help!

Jeremy 8/7/2008 9:51 PM | Jeremy

Post a comment





 

Please add 7 and 5 and type the answer here: