This is related to my earlier question, but is specific to an issue I have encountered with the use of the HoldFirst
First, let's create some fake data for testing purposes.
dateARList =
With[{ar = FoldList[0.9 #1 + #2 &, 0.,
RandomReal[NormalDistribution[0, 1], 100]]},
Transpose[{ Table[DatePlus[{2000, 1, 1}, {n, "Month"}], {n, 0, 100}], ar}] ];
Now define two functions. First, the general one that doesn't assume the size of the matrix in the first argument.
Clear[testHolder, testHolder2]
Attributes[testHolder] = {HoldFirst}
testHolder[m_?MatrixQ, rest : OptionsPattern[{Graphics, Grid}]] :=
Module[{nc, nr, subrules, subargs},
{nr, nc} = Dimensions[m];
subrules = Table[Cases[HoldForm[m[[i, j]]], _Rule], {i, nr}, {j, nc}];
subargs = Table[Cases[HoldForm[m[[i, j]]], Except[_Rule]], {i, nr}, {j, nc}];
Grid[Table[
Head[m[[i, j]]] @@ Join[subargs[[i, j]], subrules[[i, j]],
{PlotLabel -> {i, j}, Joined -> True} ], {i, nr}, {j, nc}],
FilterRules[{rest}, Grid]]
]
testHolder[{{DateListPlot[dateARList, PlotStyle -> Red],
DateListPlot[dateARList, PlotStyle -> Blue]}},
Background -> Yellow, Frame -> True]

As you can see, the options I tried to insert into the sub-plots (Joined and PlotLabel) do not get passed to them, nor do the options for the overall Grid (Frame and Background).
Now, let's try a more specific case where the dimensions of the matrix in the first argument are known.
Attributes[testHolder2] = {HoldFirst}
testHolder2[{{l_[largs__, lopts___Rule], r_[rargs__, ropts___Rule]}},
rest : OptionsPattern[{Graphics, Grid}]] :=
Grid[{{l @@ Join[{largs}, {lopts}, {PlotLabel -> "Left", Joined -> True}],
r @@ Join[{rargs}, {ropts}, {PlotLabel -> "Right", Joined -> True}]}},
FilterRules[{rest}, Grid] ]
Now we have a better outcome - the options for the specific plots are passed to them, but the options for the Grid aren't used.
testHolder2[{{DateListPlot[dateARList, PlotStyle -> Red],
DateListPlot[dateARList, PlotStyle -> Blue]}},
Background -> Yellow, Frame -> True]

I'm probably missing something, but I don't know what it is. Is HoldFirst the right way to ensure that additional options can be inserted into a function before it is evaluated? If not, what do I need to do to the evaluation sequence to get the desired result? Can I get the general (testHolder) case to work, or do I have to set things up with explicit pattern matches for the heads and arguments of the elements in the matrix, as in testHolder2?
Answer
The problem is to keep Mathematica from prematurely evaluating m while at the same time trying to extract its elements. In this approach I solve this by wrapping the elements of m with Hold
testHolder[m_?MatrixQ, rest : OptionsPattern[{Graphics, Grid}]] :=
Module[{nc, nr, mheld, subrules, subargs},
{nr, nc} = Dimensions[m];
mheld = Map[Hold, Unevaluated[m], {2}];
subrules = Table[Cases[mheld[[i, j]], _Rule, {2}], {i, nr}, {j, nc}];
subargs = Table[Cases[mheld[[i, j]], Except[_Rule], {2}], {i, nr}, {j, nc}];
Grid[Table[mheld[[i, j, 1, 0]] @@
Join[subargs[[i, j]], subrules[[i, j]], {PlotLabel -> {i, j}, Joined -> True}],
{i, nr}, {j, nc}], FilterRules[{rest}, Options[Grid]]]]
testHolder[{{DateListPlot[dateARList, PlotStyle -> Red],
DateListPlot[dateARList, PlotStyle -> Blue]}},
Background -> Yellow, Frame -> True]

Comments
Post a Comment