5
$\begingroup$
Manipulate[
  Plot[
    a*x + b, {x, -10, 10},
    PlotRange -> {{-10, 10}, {-10, 10}}, 
    PlotLabel -> Row@{"Linear function", a*x + b}],
 {{a, 1}, -5, 5}, {{b, 0}, -5, 5}]

Works nice. But I'd like to modify the label a little, so that it shows the formula with parameters a and b literaly stated, rather then converted to their values. I've been trying to achieve that this way:

Manipulate[
  holdedf = a*x + b // HoldForm;
  f = holdedf // ReleaseHold; 

  Plot[
    f, {x, -10, 10},
    PlotRange -> {{-10, 10}, {-10, 10}}, 
    PlotLabel -> Row@{"Linear function", holdedf}],

  {{a, 1}, -5, 5}, {{b, 0}, -5, 5},
  TrackedSymbols :> {a, b}]

Yes, I know this code doesn't look very pretty... But the idea was to avoid writing the formula twice: once in the Plot, and once as the PlotLabel.

But this doesn't work as expected. The Plot shows nicely, but the label is distorted. It looks like that: Linear functionFE`a$$156 x + FE`b$$156

Same results if I resing from definining holdedf:

Manipulate[
  Plot[
    a*x + b, {x, -10, 10}, PlotRange -> {{-10, 10}, {-10, 10}}, 
    PlotLabel -> Row@{"Linear function", a*x + b // HoldForm}],
  {{a, 1}, -5, 5}, {{b, 0}, -5, 5}]

And one final example. Evaluate should cancel HoldForm, right? So I can't understand this:

Manipulate[
  Plot[
    a*x + b, {x, -10, 10}, PlotRange -> {{-10, 10}, {-10, 10}}, 
    PlotLabel -> Row@{"Linear function", (Evaluate@a)*x + (Evaluate@b) // HoldForm}],
  {{a, 1}, -5, 5}, {{b, 0}, -5, 5}]

The Evaluate doesn't seem to cancel HoldForm; instead, the label it looks like this: LinearFunctionEvaluate[FE`a$$173] x + Evaluate[FE`b$$173]

Why does this happen? Can it be fixed, or do I have to resort to PlotLabel->"Linear function a x + b"? Is there any better approach to what I'm trying to achieve?

$\endgroup$
7
$\begingroup$

Analysis

Let's get Manipulate and Plot out of the picture so that we do not complicate things. All we need to know and consider is that Manipulate scopes its variables in a manner similar to Module. Now observe:

Module[{a, b},
 holdedf = a*x + b // HoldForm;
 Row @ {"Linear function", holdedf}
]
Linear functiona$7986 x+b$7986

The Symbols a and b have been replaced with localized versions a$7986 and b$7986. The same thing is happening within your Manipulate.

I explained the behavior of Evaluate here:

Work-around

I can't find an older question I was looking for so this is probably a duplication of someone's efforts and method, but here we go:

display[expr_] := expr /. s_Symbol /; Context[s] === "FE`" :> 
     RuleCondition @ 
        StringTrim[
          SymbolName @ Unevaluated @ s,
          "$$" ~~ DigitCharacter ..
        ];

This finds any Symbols in the FE` (front end) context, converts them to strings, and strips the localization renaming. RuleCondition (undocumented) is used for proper replacement inside held expressions:

Applied:

Manipulate[
 holdedf = a*x + b // HoldForm;
 f = holdedf // ReleaseHold;
 Plot[f, {x, -10, 10},
  PlotRange -> {{-10, 10}, {-10, 10}}, 
  PlotLabel -> display @ Row @ {"Linear function ", holdedf}],
 {{a, 1}, -5, 5}, {{b, 0}, -5, 5}, 
 TrackedSymbols :> {a, b}
]

enter image description here

$\endgroup$
  • $\begingroup$ Well... Thanks for explanation! What's the trick with Evaluate, though? Is it because it tries to Evaluate a, and not a$something? Also, if vars get renamed like this, I guess there's now way to do what I want to do but to move the definitions of f and holdedf outside everything? ...which, again, produces weird errors... perhaps i'm doing something wrong again $\endgroup$ – gaazkam Jan 14 '15 at 15:42
  • 1
    $\begingroup$ @gaazkam Regarding Evaluate please read: (46751). As for a workaround I am trying to find a question that I think is related. $\endgroup$ – Mr.Wizard Jan 14 '15 at 15:46
  • $\begingroup$ @gaazkam I provided a workaround. Please let me know if you have any problem with it. $\endgroup$ – Mr.Wizard Jan 14 '15 at 16:05
  • $\begingroup$ Sadly, there are some problems. Sometimes the whole Manipulate grows complex enough that it would be nice if f was SetDelayed rather then simply Setted. If we replace current definitions of holdedf and f with holdedf[a_, b_, x_] := a*x + b // HoldForm; and f[a_, b_, x_] := holdedf[a, b, x] // ReleaseHold; this sadly breaks. However, this is a problem of the whole idea of using f and holdedf; the Manipulate also breaks if your solution isn't used. Sorry, but... er... I'm pretty embarassed to ask you for this, but... how to fix this? $\endgroup$ – gaazkam Jan 14 '15 at 17:12
  • $\begingroup$ @gaazkam Would you append complete code for that example to your question? $\endgroup$ – Mr.Wizard Jan 14 '15 at 17:28
3
$\begingroup$

I would work with nested With:

With[{f := 1-2+a x + b}, 
 With[{stringf = ToString[HoldForm[f], TraditionalForm]},
  Manipulate[
   Plot[f, {x, -10, 10}, PlotRange -> {{-10, 10}, {-10, 10}}, 
    PlotLabel -> "Linear function " <> stringf], {{a, 1}, -5, 
    5}, {{b, 0}, -5, 5}, TrackedSymbols :> {a, b}]]]

which gives (now properly held and scoped, as indicated by Mr. Wizard): enter image description here

$\endgroup$
  • $\begingroup$ This fails to localize a, x, and b so it breaks if these have an external value. There is a one-character fix; make it and earn my vote. :^) $\endgroup$ – Mr.Wizard Jan 16 '15 at 2:03
  • 1
    $\begingroup$ @Mr.Wizard: nice idea to use := instead of = in the first argument of With. This is nowhere documented, or? $\endgroup$ – Rolf Mertig Jan 16 '15 at 7:53
  • $\begingroup$ Nice, thanks! One question: Just out of perfectionism, is there any chance of parametrization of stringf? I mean, something like that: functiontostring[fun_]:=ToString[HoldForm[fun],TraditionalForm]? Unfortunately, this attempt fails... $\endgroup$ – gaazkam Jan 16 '15 at 10:18
  • $\begingroup$ @Rolf Thanks the one. +1 :-) No, it's not documented as far as I know. I'm pretty sure I learned it from Leonid a few years ago. It's really very useful and I have not found any problem using it; it should be part of the documented language IMO. I took it into consideration when writing listWith given the frequency with which I use it. $\endgroup$ – Mr.Wizard Jan 16 '15 at 14:00
  • $\begingroup$ @gaazkam You will need to add SetAttributes[functiontostring, HoldFirst] to keep the argument of the function from being evaluated before the definition is applied. $\endgroup$ – Mr.Wizard Jan 16 '15 at 14:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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