Mathematica: HOWTO Manage test data for functions using rules across multiple functions (and about the Webel '$0' variable suffix convention for substitution targets).

Webel IT Australia promotes the amazing Mathematica tool and the powerful Wolfram Language and offers professional Mathematica services for computational computing and data analysis. Our Mathematica tips, issue tracking, and wishlist is offered here most constructively to help improve the tool and language and support the Mathematica user community.
DISCLAIMER: Wolfram Research does not officially endorse analysis by Webel IT Australia.
Icon class
icon_class
far fa-sticky-note
icon_class_computed
far fa-sticky-note
Note kind
Keywords
Click on the image to view it full size
It can be useful to manage test data across multiple functions with Rules:

testRules1 = {a$0 -> 1.0, b$0 -> 2.0, c$0 -> 3.0, d$0 -> 4.0, n$0 -> 2}

{a$0 -> 1., b$0 -> 2., c$0 -> 3., d$0 -> 4., n$0 -> 2}

testRules2 = {a$0 -> 1.1, b$0 -> 2.2, c$0 -> 3.3, d$0 -> 4.4, n$0 -> 3}

{a$0 -> 1.1, b$0 -> 2.2, c$0 -> 3.3, d$0 -> 4.4, n$0 -> 3}
Note how this employs the Webel convention of suffixing a variable name with '$0' to indicate that it is an "initialisation" target!
Note also how the functions the test data are applied to need not use every variable:

fSimple[a_, b_] := a + b;

fSimple[a$0, b$0] /. testRules1

3.

fSimple[a$0, b$0] /. testRules2

3.3
By having the test data as rule sets they can be used with other functions with a different number of arguments and/or different order (which is far more robust for testing):

fDifferentOrder[b_, a_, c_] := a - b * c;

fDifferentOrder[b$0, a$0, c$0] /. testRules1

-5.

However, one can't always sensibly apply the rules AFTER the function "acts" on its arguments:

fWithLoop[n_Integer, a_, b_] := Module[
   {out = {}},
   For[i = 1, i <= n, i++, AppendTo[out, fSimple[a, b]^i]]; 
   out
];
The following won't loop, it does not yet know 'n$0' before the rules are applied:

fWithLoop[n$0, a$0, b$0] /. testRules1

{}
So use an "arguments helper" to capture the rules first then use Apply (or @@):

args$fWithLoop[rules_] := {n$0, a$0, b$0} /. rules;

fWithLoop @@ args$fWithLoop[testRules1]

{3., 9.}
Consider this looping function wrapper with Options:

Options[fWithLoopAndOption] = {doDouble -> False};

fWithLoopAndOption[n_Integer, a_, b_, OptionsPattern[]] := Module[
   {out = fWithLoop[n, a, b]},
   If[OptionValue[doDouble], 2 # & /@ out, out]
   ];

{fWithLoopAndOption[2, 1.0, 2.0],fWithLoopAndOption[2, 1.0, 2.0, doDouble -> True]}

{{3., 9.},{6., 18.}}
A quite general args handler can be defined. There is no need to define the actual Options for the arguments handler, just accept an OptionsPattern[]:

args$any[argNames_,rules_, opt : OptionsPattern[]] :=
  Join[argNames /. rules, {opt}];

args$any[{n$0, a$0, b$0}, testRules1, doDouble -> False]

{2, 1., 2., doDouble -> False}

fWithLoopAndOption @@ 
 args$any[{n$0, a$0, b$0}, testRules1, doDouble -> False]

{3., 9.}

fWithLoopAndOption @@ 
 args$any[{n$0, a$0, b$0}, testRules1, doDouble -> True]

{6., 18.}
Relates to
Related notes
Related notes (backlinks)
Related snippets (extracts)
Visit also
Visit also (backlinks)