Saturday, July 30, 2016

5 ways to do Decision trees in C#

At Agile2016 we spent the mornings doing Mob Programming working on the Tennis Refactoring Kata from Emily Bache.

Thursday we ran accross this issue:

#1. If Structure

This is fairly easy to code as is, the trouble is it looks horrible:

string result;
result = Win();
if (result != null)
{
return result;
}
result = Advantage();
if (result != null)
{
return result;
}
result = ScoreNormal();
if (result != null)
{
return result;
}
result = ScorePerson1();
if (result != null)
{
return result;
}
result = ScorePerson2();
if (result != null)
{
return result;
}
result = ScoreTie();
return result;
view raw ifstructure.cs hosted with ❤ by GitHub
So we started to play around with alternative structures...


#2. Pass in result

One solution is to call all the methods but abort early if you've already found a solution.
string result;
result = Win();
result = Advantage(result);
result = ScoreNormal(result);
result = ScorePerson1(result);
result = ScorePerson2(result);
result = ScoreTie(result);
return result;
view raw Passin.cs hosted with ❤ by GitHub
This looks a lot nicer, the problem is it leaks into the underlying methods
public string Advantage(string previousResult)
{
if (previousResult != null)
{
return previousResult;
}
view raw leaking.cs hosted with ❤ by GitHub
So we keep looking..

#3. Linq

If you like linq you can solve this with a bit of functional code

return new Func<string>[]
{
Win,
Advantage,
ScoreNormal,
ScorePerson1,
ScorePerson2,
ScoreTie
}.FirstNonNull();
public static T FirstNonNull<T>(this IEnumerable<Func<T>> methods)
{
return methods.Select(x => x()).First(x => x != null);
}
view raw FirstNonNull.cs hosted with ❤ by GitHub

This reads nicely, but is a bit confusing as to why it works and means all your methods must not use any parameters.

but It can also be used with the yield keyword, which is neat and allows parameters but splits logic.
private IEnumerable<string> ProcessScores()
{
yield return Win();
yield return Advantage();
yield return ScoreNormal();
yield return person2.ScoreOpponentHasntScored(person1);
yield return person1.ScoreOpponentHasntScored(person2);
yield return ScoreTie();
throw new Exception("Unreachable code was executed");
}
view raw yeild.cs hosted with ❤ by GitHub
so we kept looking...

#4. Do If Null

We were able to get closer to the 'Pass in result' without leaking with the addition of an extension method.
string result = null;
result = result.DoIfNull(Win);
result = result.DoIfNull(Advantage);
result = result.DoIfNull(ScoreNormal);
result = result.DoIfNull(ScorePerson1);
result = result.DoIfNull(ScorePerson2);
result = result.DoIfNull(ScoreTie);
return result;
public static T DoIfNull<T>(this T t, Func<T> f )
{
if (t == null)
{
return f();
}
return t;
}
view raw doifnull.cs hosted with ❤ by GitHub
at this point someone pointed out the ?? operator...

#5. ?? Operator

The ( a ?? b ) operator says if  a is null do b. The resulting code is rather nice
return Win()
?? Advantage()
?? ScoreNormal()
?? person2.ScoreOpponentHasntScored(person1)
?? person1.ScoreOpponentHasntScored(person2)
?? ScoreTie();
view raw operator.cs hosted with ❤ by GitHub
It's worth noting that if your language has 'truthiness' (like javascript) you can also do this with the or (||) operator


return Win()
|| Advantage()
|| ScoreNormal()
|| person2.ScoreOpponentHasntScored(person1)
|| person1.ScoreOpponentHasntScored(person2)
|| ScoreTie();
view raw or.js hosted with ❤ by GitHub

Which is Best?

Of course that's up to you. For us most liked the ?? operator best, while a few thought the Linq solution was the nicest.

Which one do you like?