Message Boards Message Boards

Manipulate parser bug => wonderous result ?

Posted 11 years ago
Hi,

I accidentally uncovered what appears to be a parsing bug within Manipulate.  The documented syntax is:
  Manipulate[expr, ...]
where expr can be one or a compound expression, e.g.:
    Manipulate[exprA, ...]
OR
   Manipulate[exprA;exprB, ...]  (* note the semicolon separating the individual expressions *)

But I discovered that things (*seemingly*) work just fine, along with some bonus behavior (more below), for:
   Manipulate[Dynamic[exprA] exprB, ...]
but in the above, the first argument is not a compound expression at all!  I'm surprised it even parsed successfully! 
However, if one uses proper syntax, the bonus behavior is disabled. 

The bonus behavior is that exprA within Dynamic[exprA ] can be a set of clauses (e.g. a compound expression)
that impose constraints on the control variables or do other useful things in response
to changes in TrackedSymbols, *without* triggering the main Manipulate re-eval loop.  Evidently, the parsing bug includes the result of 
Dynamic as part of the displayable output by the Front End. 

Is this a bug or expected behavior?  In either case, I like it :-) 

Here is a code example (2 input cells), and the comments therein describe the bonus behavior:
  genDemo[] := Manipulate[
  
   (* Place controller value constraints here, wrapped in Dynamic[] to preclude re-eval loop that recomputes the display graphic. However,
  to use this idiom, make the last statement within the Dynamic an invisible displayable output such as Spacer[0], NOT followed by ;. 
   This precludes Null showing up in the display output.  ALSO!  Do NOT terminate the Dynamic[] with a semicolon,
   or this mechanism won't operate! Why this works symtactically at all is a mystery!  *)
  
   Dynamic[
     If[Ceiling[cnx] < Ceiling[cty], cty = Ceiling[cnx]];
    If[Ceiling[cnx] < Ceiling[ctx], ctx = Ceiling[cnx]];
    Spacer[0]   (* TRY DELETING THIS STATEMENT. If you do, then Null appears in output display.  *)
    ]   (* TRY PLACING A SEMICOLON HERE.  If you do, then these control constraints won't be operable *)
  
  
   (* The below With[] idiom prevents makeRGBImg from being reinvoked each time the target location changes [which
   should merely update the red dot location in display] *)
   GraphicsGrid[{{With[{img = Table[RandomReal[], {cnx}, {cnx}]},
       Dynamic[Image[makeRGBImg[img, cty, ctx]]]]}}, ImageSize -> 150],
  (* The last executable statement above must terminate with a comma, thus marking the division between
  the "action" and control layout specification sections of the Manipulate[] *)
 
  {{cnx, vnx, Tooltip[lnx, tnx]}, ControlType -> InputField},
  {{ctx, vtx, Tooltip[ltx, ttx]}, 1, cnx, Appearance -> "Labeled"},
  {{cty, vty, Tooltip[lty, tty]}, 1, cnx, Appearance -> "Labeled"},
  LocalizeVariables -> True,
  ControlPlacement -> Left,
  TrackedSymbols :> {cnx, ctx, cty}, ContinuousAction -> False,
  SynchronousUpdating -> False,
  Initialization :> {tnx = "Number of spatial samples (square image)",
    lnx = "Number of Pixels Width",
    vnx = 100,
    ttx = "Target X pixel ",
    ltx = "Target X Pixel",
    vtx = 65.,
    tty = "Target Y pixel",
    lty = "Target Y Pixel",
    vty = 47.9 ,
    makeRGBImg[img_, cty_, ctx_] := Module[{rgbimg},
      rgbimg = Map[{#, #, #} &, img, {2}];
      rgbimg[[Ceiling[cty], Ceiling[ctx]]] = {1., 0, 0};  (* Insert red target marker *)
      Return[rgbimg]]
    } (* end of Initialization *)
  ]

(Text Cell) Try reducing the 1st control's value (# pixels width) to less than the target pixel location index. 
If control constraints wrapped inside Dynamic are operable, then the target pixel location controls
should update without being left "out-of-bounds" red. 

genDemo[]
POSTED BY: Frank Iannarilli
When you don't put a semicolon between two expressions they are treated as if they were symbols multiplied together. The order they appear depends on alphabetical order.

When you run this, you'll the head of the result is Times.
FullForm[Dynamic[If[Ceiling[cnx] < Ceiling[cty], cty = Ceiling[cnx]]
    If[Ceiling[cnx] < Ceiling[ctx],
     ctx = Ceiling[
       cnx]] ]  GraphicsGrid[{{With[{img =
        Table[RandomReal[], {cnx}, {cnx}]},
      Dynamic[Image[makeRGBImg[img, cty, ctx]]]]}}, ImageSize -> 150]]

Each statement gets evaluated, but on the surface they are treated as though they are statements being multiplied together. It's not a parser bug or a feature of manipulate. I'm sure it does a number of fun and exciting things to way the expression evaluates, but reasoning through it is going to be a bad headache. I wouldn't treat this as a feature to be used. It'll result in difficult to dechiper code and confusion when changing variable names ends up changing the order of the multiplication.
POSTED BY: Sean Clarke
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract