Introducing the probability switch!
I'm going to start this blog talking about a pattern that has appearing in my programming for some time now.
The scenario is as follows. You have a function and want it to, probabilistically, behave differently, every time you call it. One example is a mutation function, in the domain of evolutionary programming, which is expected to perform a different action among a set of possibilities, as illustrated by the following code.
function mutate(somethingToMutate) { let option = Random.randomInt(4); // generate integer in the range [0,3] if (option === 0) { // exchange something exchangeSomething(somethingToMutate); } else if (option === 1) { // add something addSomething(somethingToMutate); } else if (option === 2) { // remove something removeSomething(somethingToMutate); } else { // change something changeSomething(somethingToMutate); } }
In the code above, which could also be a switch statement, each option has a chance of 25%. The problem with the code above is that it is not easy to see the programmer intention right away. Also, it lacks flexibility. For instance, if we want to change the probabilities, we could increase the random range, but it would only increase the chance of the last option. Altough it is possible to change the probabilities by accepting a range for each possibility it is cumbersome, to say the least.
With that scenario in mind, this is my proposed pattern:
function mutateCode(somethingToMutate) { let selectedOption = LanguageConstruct.probabilitySwitch( // exchange something exchangeSomething(somethingToMutate), 0.25, // add something addSomething(somethingToMutate), 0.25, // remove something removeSomething(somethingToMutate), 0.25, // change something changeSomething(somethingToMutate), 0.25 ); }
Much better huh? My thoughts exactly... :)
And the implementation, in case you're wondering, is:
// call format: // LanguageConstruct.probabilitySwitch(func1, prob1, func2, prob2,..., funcN, probN) LanguageConstruct.probabilitySwitch = function(...functionsAndProbabilities) { Assert.assertIsArray(functionsAndProbabilities); Assert.assert(functionsAndProbabilities.length%2===0, 'Expecting an even number of parameters'); let functions = []; let probabilities = []; for (var i=0;iAssert.assertIsFunction(func, 'Expected (func1, prob1, func2, prob2,..., funcN, probN)')); probabilities.forEach(prob => Assert.assertIsNumber(prob, 'Expected (func1, prob1, func2, prob2,..., funcN, probN)')); let probabilitiesSum = probabilities.reduce((sum, value) => sum + value, 0); Assert.assert(probabilitiesSum, 'Probability sum must be 1'); //each one select a number based on its probability and the //one with the bigger number wins. let generatedValues = probabilities.map(prob => Math.random() * prob); let selectedIndex = generatedValues.indexOfGreaterValue(); functions[selectedIndex](); };
The last point is: wouldn't it be nice to have this cool feature interwoven in the language itself? Then you could use it like a first class language construct, like:
function mutateCode(somethingToMutate) { switchProbability(somethingToMutate) { case 0.25: // exchange something exchangeSomething(somethingToMutate) break; case 0.25: // add something addSomething(somethingToMutate), break; case 0.25: // remove something removeSomething(somethingToMutate), break; case 0.25: // change something changeSomething(somethingToMutate), break; } }
Actually, this is possible by "transpiling" the code before execution, with a tool like Sweet.js.
And...That's it for the first post! Did you like it? Leave your comments bellow... Oh no... I'm sorry, this blog doesn't have this functionality yet... Well, you can hit me at @andrers52 and tell me what you think in 140 characters or less! :).