Friday, October 12, 2012

Shades of XY problem

Shades of XY problem. A colleague asked me how to prevent nested substitution of the token when it's inside of an attribute, the following...


foreach(var token in arr) 
{
 sb.Replace( token, string.Format("<li id='{0}' class='meh'>{1}</li>", token, GetDescriptive(token)) );
}

...resulted to this (say token is "bp") nested substitution:

<li id='<li id='bp' class='meh'>BasePay</li>' class='meh'>BasePay</li> 
+ (<li id='<li id='bp' class='meh'>BasePay</li>' class='meh'>BasePay</li> 
* <li id='pi' class='meh'>PercentIncrease</li>)


...then I quickly suggested this:


var d = new Dictionary<Guid,string>();


foreach(var token in arr) 
{
 var g = Guid.NewGuid();
 d[g] = token;
 sb.Replace( token, string.Format("<li id='{0}' class='meh'>{1}</li>", g, GetDescriptive(token)) );
}


foreach(KeyValuePair<Guid,string> kv in d) 
{
        // essentially putting back the original token inside of attribute
 sb.Replace(kv.Key, kv.Value); 
}


That produces correct output:


<li id='bp' class='meh'>BasePay</li> 
+ (<li id='bp' class='meh'>BasePay</li> * <li id='pi' class='meh'>PercentIncrease</li>)



However, I dwell too much on how to prevent values substitution when it's inside of an attribute, in fact I'm thinking of regular expression approach too; sometimes though, when you formulate a regular expression solution you'll have two problems later. And so the proverbial light bulb lit up on my puny brain, why not just use Guid so when the next repeating substitution occur it won't replace those tokens inside of attribute?! Then I devise the Dictionary+Guid combo solution above. It works! Brilliant! Or so I thought I'm brilliant, it works but...


Then it occurred to me while I'm going back home, why did the string replacement occurred two times in the first place? Should doing it once would suffice?


Then upon arriving back to the office the next day, I re-check why the string substitution could occur two times, and then I saw this:


BasePay + (BasePay * PercentIncrease)


Darn, that's why the nested substitution occurred, BasePay occurred two times in the equation.


I even remember my colleague explained the above equation the day before. The colleague explained both the X (equation with repeating token) problem and the Y solution (prevent operands inside of attribute from being nestedly replaced) being attempted first. The colleague cannot be faulted, I focused too much on Y. Admit it or not, to most programmers, challenging problems are what piqued our curiosity the most, and to me it was how to prevent that-goddamn-token-inside-of-attribute-from-being-nestedly-replaced. When we can't connect to a database server, we are most likely to suggest to a colleague to look at the firewall configuration first, or connect directly to IP address, or connect to named pipes instead of TCP/IP, etc, it's rare of us to suggest to look first if the network cable is disconnected, you get my drift.


The most elegant solution is to prevent duplicate tokens from appearing multiple times in the list:


foreach(var token in arr.Distinct()) 
{
 sb.Replace(token, string.Format("<li id='{0}' class='meh'>", token));
}


It works and very simple. And while I'm writing this post, it hit my mind that it is best to tackle the problem from its root cause.


var arr = formula.Split("/+-*()".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Distinct();

foreach(var token in arr) 
{
 sb.Replace(token, string.Format("<li id='{0}' class='meh'>", token));
}


The root cause of any problems in a Y solution comes from X requirement. So when someone is asking you a question, and you think they are just presenting you their Y, demand for X.

No comments:

Post a Comment