Solve the dual problem

You might have seen an expert programmer in action. You ask him for help in debugging some error, and he comes up with a better way of writing the code. You are amazed at how easy it seemed.

Here’s a secret. He didn’t come up with a better solution. He restated the problem and solved that instead.

The De Morgan dual

You might not know who De Morgan is. He formally stated a set of mathematical logic laws as follows:
not (P and Q) <==> (not P) or (not Q)
not (P or Q) <==> (not P) and (not Q)
not (not P) <==> P

Ok, perhaps that was confusing to you. Let me put it in code form:

bool P = false, Q = true;
if (!(P && Q))
{
   Console.WriteLine("First version");
}
// is equivalent to
if (!P || !Q)
{
   Console.WriteLine("Second version");
}

Does it look more familiar?

The thing with program requirements is that sometimes, they are stated in a convoluted manner, but can be simplified when written in code. Or at least easier to understand in code.

Suppose the business requirement was that if the quantity was not less than 10 and the item was not a normal category item, process with special priority. Well, you could do this

if (!(iQuantity < 10) && (iItem != ItemCategory.Normal))
{
// process with special priority
}
else
{
// process normally
}

That made my mind jump through too many negations. We could transform that into

if ( !( (iQuantity < 10) || (iItem == ItemCategory.Normal) ) )
{
// process with special priority
}
else
{
// process normally
}

I added more space because there were a lot of round brackets.

Well, what's the big deal? Because of the structure of the requirements and the if-else statement, we could rewrite as this

if ( (iQuantity < 10) || (iItem == ItemCategory.Normal) )
{
// process normally
}
else
{
// process with special priority
}

I swapped the contents of the if-else, and thus removed the negation as well. Now, the condition is easier to read.

Just because the requirements were stated in a certain way, doesn't mean you can't rewrite the code to solve the same problem. And my last sentence contained two negations, which made it harder to read.

A practical use would be checking for errors. Usually the error presentation part is shorter than if the condition went through

if ({check for something})
{
// do something, usually many lines of code
}
else
{
// display error message
}

There were times when I was tracing code, through an if condition, and went down line by line, and then hit the else part, and I forgot what the condition was. If it was written this way

if ({error condition})
{
// display error message
}
else
{
// do something, usually many lines of code
}

then it's easier to follow. Add some comments in the else part to state the original condition in case you need to swap back.

You'll have to be sure of what you're doing when swapping. Some requirements cannot be swapped like this. Make sure you know what the contents of the if-else are doing.

Transform the problem into another equivalent

So we've gone through a simple version of changing an if condition to another equivalent if condition. In mathematical terms, it's known as a dual problem. We've stated the if condition one way, and write the code for that. Then we've written the if condition in another equivalent way, and wrote the code for that. And both of them solved the same problem.

What are the uses for this concept? Suppose you're stuck with a problem. You can't write the code to fully express a requirement. Or it could take a lot of effort to solve it. See if you can transform this problem into an equivalent one that's easier to solve.

For example, you could be retrieving a bunch of data from the database. Then you iterate through the data, performing calculations on each row, then inserting everything back into the database. The problem is that it's too slow, or too memory intensive. A lot of data is held in memory while you match and sort and compare and update each row.

Transform the problem from doing calculations and updates in your programming environment to doing calculations and updates in the database environment. The master records are in the database. The detail records are in the database. Why are you retrieving them into memory and do updates? Use the right programming tool; do them in the database!

For another example, say the requirement was to have more white space around images when displayed in a web page. If you didn't know any better, you might have gone through every single image and added a white border around each one. You have understood the problem as creating more white space around images, so you went about solving that.

If you had understood the emphasis as "when displayed in a web page", the problem shifted. You could use CSS to add padding, and the equivalent problem was solved.

This is a powerful concept. Solving an equivalent (dual) problem solves the original (primary) problem.

As programmers, we transform requirements into code. There are many ways to write the code, so there are many equivalents to those requirements. Choose the right equivalent problem to solve.