Learning from @rcar's answer, and another refactoring later, I've got the following.
As with so many things I code, I've refactored from CFML to CFScript, but the code is basically the same.
I added in his suggestion of a dynamic start point in the array (instead of passing the array by value and changing its value for future recursions), which brought me to the same stats he gets (209 recursions, 571 combination price checks (loop iterations)), and then improved on that by assuming that the array will be sorted by cost -- because it is -- and breaking as soon as we go over the target price. With the break, we're down to 209 recursions and 376 loop iterations.
What other improvements could be made to the algorithm?
function testCombo(minIndex, currentCombo, currentTotal){
var a = 0;
var CC = "";
var CT = 0;
var found = false;
tries += 1;
for (a=arguments.minIndex; a <= arrayLen(apps); a++){
combos += 1;
CC = listAppend(arguments.currentCombo, apps[a].name);
CT = arguments.currentTotal + apps[a].cost;
if (CT eq 15.05){
//print current combo
WriteOutput("#CC# = 15.05
");
return(true);
}else if (CT gt 15.05){
//since we know the array is sorted by cost (asc),
//and we've already gone over the price limit,
//we can ignore anything else in the array
break;
}else{
//less than 15.50, try adding something else
found = testCombo(a, CC, CT);
}
}
return(found);
}
mf = {name="mixed fruit", cost=2.15};
ff = {name="french fries", cost=2.75};
ss = {name="side salad", cost=3.35};
hw = {name="hot wings", cost=3.55};
ms = {name="mozarella sticks", cost=4.20};
sp = {name="sampler plate", cost=5.80};
apps = [ mf, ff, ss, hw, ms, sp ];
tries = 0;
combos = 0;
testCombo(1, "", 0);
WriteOutput("
tries: #tries#
combos: #combos#");