Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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]

enter image description here

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]

enter image description here

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?

share|improve this question
    
It seems the problem is that Cases[HoldForm[m[[i, j]]], Except[_Rule]] is what evaluates your plots, thus rendering you unable to insert the Joined option. –  J. M. Jun 16 '12 at 13:55
    
I think I was just confused when answering. Do you want the Graphics options inserted at the end of the function to be passed to all individual plots or not? –  Rojo Jun 16 '12 at 15:07
    
I'm not sure of what you need. I posted the method I would use for function insertion. If you will describe the additional functionality you need or how my method fails I will amend it accordingly. –  Mr.Wizard Jun 16 '12 at 15:20
    
Verbeia would you tell me how you find my method lacking? –  Mr.Wizard Jun 19 '12 at 21:28
    
It isn't. Heike's version just was easier to integrate into the rather more complex actual code that I have, so I upvoted all good answers and accepted Heike's as the best one for my needs. –  Verbeia Jun 19 '12 at 21:48
add comment

3 Answers 3

up vote 3 down vote accepted

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]

Mathematica graphics

share|improve this answer
    
I'd tried Hold - isn't it the Unevaluated that does the job? –  Verbeia Jun 16 '12 at 21:15
    
The other crucial difference from my code seems to be Cases[mheld[[i, j]], _Rule, {2}] with the level specification. Not entirely clear to me why that is needed - something to do with getting underneath the Unevaluated? –  Verbeia Jun 16 '12 at 21:26
    
@Verbeia, mheld is a matrix of things like Hold[DateListPlot.... So mheld[[i, j]] is Hold[DateListPlot[..., ru->les]]. There's no Unevaluated there any more. Cases by default works on level 1, but the rules are at level 2, so if you don't specify it they aren't found –  Rojo Jun 18 '12 at 0:09
    
@Rojo you have appended this comment to Heike's answer, where there is an Unevaluated. Your answer also has Unevaluateds in there still. It does seem important. I've accepted Heike's answer because it was the clearest and minimal adjustment to my own code, which now works (hooray!). –  Verbeia Jun 18 '12 at 0:35
    
@Verbeia, yeah, I was referring to this code and your question... If I'm not reading it wrong, the only Unevaluated is in Map[Hold, Unevaluated[m], {2}]. That's an Unevaluated that gets automatically stripped off, and Map doesn't even see it, so it isn't there when it's Cases turn to act. Contrats on your code's working :) –  Rojo Jun 18 '12 at 0:41
show 1 more comment

The problem is the lines Table[Cases[HoldForm[m[[i, j]]], _Rule], {i, nr}, {j, nc}], because m[[i, j]] isn't evaluated before you try to extact the rules, so none is extracted. They could be something like Map[Function[plot, Cases[Unevaluated@plot, _Rule], HoldFirst], Unevaluated@m, {2}]

Other problems:

  • I don't think FilterRules works with something like Grid as a second argument. You need to put the options, such as Options@Grid

  • I haven't tried but probably it's not a good idea to use ?MatrixQ only to test structure because you'll leak evaluation, and plotting twice the same thing isn't something time trivial quite often. You could use Function[mat, MatrixQ@Unevaluated@mat, HoldFirst]. This is something I also use in my answer to map a holding version of append.

  • Head[m[[i, j]] will evaluate the plots, and then extract the heads of the matrix entries (Graphics probably). You want to extract the heads before evaluating. So, do something like (Unevaluated@m)[[i, j, 0]]

Try it with these fixes

Attributes[testHolder] = {HoldFirst};
testHolder[m_?(Function[mat, MatrixQ@Unevaluated@mat, HoldFirst]), rest : OptionsPattern[{Graphics, Grid}]] := 
 Module[{nc, nr, subrules, subargs}, {nr, nc} = Dimensions[m];
  subrules = 
   Map[Function[plot, Cases[Unevaluated@plot, _Rule], HoldFirst], 
    Unevaluated@m, {2}];
  subargs = 
   Map[Function[plot, Cases[Unevaluated@plot, Except[_Rule]], 
     HoldFirst], Unevaluated@m, {2}];
  Grid[Table[(Unevaluated@m)[[i, j, 0]] @@ 
     Join[subargs[[i, j]], 
      subrules[[i, j]], {PlotLabel -> {i, j}, Joined -> True}], {i, 
     nr}, {j, nc}], FilterRules[{rest}, Options@Grid]]
  ]

Anyway, I started answering by doing it myself, and this is how I approached it

Is this the behaviour you want?

SetAttributes[testHolderV3, HoldFirst];
testHolderV3[m : {__List}, rest : OptionsPattern[{Graphics, Grid}]] :=  
 MapIndexed[
   Function[{graph, index}, 
    Append[Unevaluated@graph, 
     Join[FilterRules[{rest}, Options@Graphics],
      {PlotLabel -> index, Joined -> True}]], HoldFirst], 
   Unevaluated@m, {2}] //
  Grid[#, FilterRules[{rest}, Options@Grid] ] &

So

testHolderV3[{{DateListPlot[dateARList, PlotStyle -> Red], 
   DateListPlot[dateARList, PlotStyle -> Blue]}}, 
 Background -> Yellow, Frame -> True]

Mathematica graphics

EDIT

Ok, I'm not sure if you want Graphics options inserted in the list at the end to be passed to all plots or not. Such as a possible ImageSize->Medium. If you want that, add to your testHolder, a FilterRules[subrules, Options@Graphics]. If you don't, remove from testHolderV3 the Join[FilterRules[{rest}, Options@Graphics],... and just leave the options you want

share|improve this answer
add comment

Your code is a bit of a tangle. I'm not sure of all that you're trying to do with it, but here is the approach that I would use to insert a couple of arguments into each plot in a table:

ClearAll[testHolder]

Attributes[testHolder] = {HoldFirst};

testHolder[m_?MatrixQ, rest : OptionsPattern[{Graphics, Grid}]] :=
  Grid[
    MapIndexed[Hold, Unevaluated[m], {2}] /.
      Hold[head_[args__], idx_] :> 
        head[args, Joined -> True, PlotLabel -> idx],
    Sequence @@ FilterRules[{rest}, Options@Grid]
  ]

testHolder[
 {{DateListPlot[dateARList, PlotStyle -> Red], 
   DateListPlot[dateARList, PlotStyle -> Blue]}}, 
 Background -> Yellow, Frame -> True ]

Mathematica graphics

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.