0
$\begingroup$

I am making complex 3d graphics, and to clear up functions, I want to use With to remove a repeated action. This action occurs many times through many functions, so I want the With outside all of them, so I only declare it once. Except Mathematica doesn't seem to like having a With outside a function, if it is replacing parameters. A simplified version is shown below:

With[{y = x},
 draw[x_] := Cylinder[{{y,0,0}, {y+1,0,1}}, 0.1]
 ...(*other functions:See Bottom!!!!!!!!*)
]

then:

Graphics3D[draw[3]]

If I do the above, it brings the error:

The Error

So it should go y -> x -> 3. But it only gets half-way. It has changed y to x but I think it is after when the parameters are put in, so x is not changed to 3. Is there a way to keep the with outside, so it is only declared once.

Also, in case anybody says put the With before cylinder. No, I don't want to do that, as there are other functions using the values changed by "With"

-----------------------EDIT-----------------------------

The answers can replace the values for one function, as is shown in the code example, but it doesn't show (although I did say it), that it needs to work through many functions, as shown below. Sorry for the confusion, as causing the first two answers to not fully work.

With[{y = x},
 draw1[x_] := Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.1], 
 draw2[x_] := Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.2]
 ...(*more functions*)
]
$\endgroup$
6
  • $\begingroup$ The way you have draw set up, x is defined within draw as a parameter. Outside of draw, x is just a symbol x, so for example draw[3] will yield the output Cylinder[{{x, 0, 0}, {x, 0, 0}}, 0.1]. In other words, Mathematica doesn't think the formula you've given it for draw involves the input variable at all. $\endgroup$ Commented Jul 12, 2014 at 16:32
  • $\begingroup$ closely related, maybe duplicate: 48521. So you can proceed with Function[ draw[x_] := Cylinder[{{#, 0, 0}, {# + 1, 0, 0}}, 0.1] ][x], notice I've added +1 since your code makes no sense. $\endgroup$
    – Kuba
    Commented Jul 12, 2014 at 16:48
  • $\begingroup$ related tutorial $\endgroup$
    – Kuba
    Commented Jul 12, 2014 at 17:03
  • $\begingroup$ @Kuba,@Jacob Akkerboom. Yes you are both right, my code it incorrect, in both the ways you two stated, I have now changed it. $\endgroup$
    – ptolemy0
    Commented Jul 12, 2014 at 17:10
  • $\begingroup$ Responding to your edit: Function[ draw1[x_] := Cylinder[{{#, 0, 0}, {# + 1, 0, 1}}, 0.1]; draw2[x_] := Cylinder[{{#, 0, 0}, {# + 1, 0, 1}}, 0.2];][x] $\endgroup$
    – Kuba
    Commented Jul 12, 2014 at 17:44

3 Answers 3

2
$\begingroup$

There is a mismatch in your definition, between x and x$

draw // Information

Global`draw

draw[x$_]:=Cylinder[{{x,0,0},{x,0,0}},0.1]...

This is because With is a so called scoping construct. With tries to avoid conflicts between symbols. See this answer for a good explanation by Leonid. If you want to know all cases in which this happens, see this question by me.

If you wish to avoid such behaviour, you can use ReplaceAll on the outside, rather than With. There is another issue with your code too, as the two points in the first argument of Cilinder have to be different. So I have changed this. You could then use the following code

Unevaluated[draw2[x_] := Cylinder[{{y,0,0}, {y,0,1}}, 0.1]]/. Unevaluated[y->x]

Because the syntax with Unevaluated is a bit ugly, you may consider making your own function, like this.

SetAttributes[replaceWith, HoldFirst]
(*Unevaluated[y] is to make sure replaceWith works well when providing 
Unevaluated around its second argument*)
replaceWith[x_,y_]:= Unevaluated[x]/.Unevaluated[y]

The solution is then

replaceWith[
 draw2[x_] := Cylinder[{{y, 0, 0}, {y, 0, 1}}, 0.1]
 ,
 Unevaluated[y -> x]
 ]

In both cases, we have

draw2 // Information

Global`draw2

 draw2[x_]:=Cylinder[{{x,0,0},{x,0,1}},0.1]

Edit

You asked to make it work for multiple definitions. The solution is simply as follows

replaceWith[
 draw1[x_] := Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.1];
 draw2[x_] := Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.2]
 ,
 y -> x
 ]

Note that I have replaced a colon of yours by a semicolon. I prefer it this way, as this corresponds more closely to With (note that syntax in the example you provided was not correct). We then have

Graphics3D[{draw1[1], draw2[1]}]

enter image description here

$\endgroup$
5
$\begingroup$

When you do this, localization kicks in and renames one of the x to avoid conflict:

enter image description here

This is in general beneficial, and designed to prevent trouble. In some situations, however, you don't want this localization-through-renaming to happen. In that case you can use Replace instead of With to replace y by x.

I like to keep using the syntax of With though, so in these situations I tend to use the withRules function defined in the middle of this answer.

enter image description here

The caveat with withRules is precisely that it does not truly localize, so you need to be very careful when using it.


Update:

Version 10-specific template based solution:

template = (draw[x_] := Cylinder[{{#y, 0, 0}, {#y + 1, 0, 1}}, 0.1]) &

template[<| "y" :> x |>]

?draw
draw[x_]:=Cylinder[{{x,0,0},{x+1,0,1}},0.1]
$\endgroup$
3
  • $\begingroup$ I see our answers are quite similar :). I must say I don't like a definition using Internal`InheritedBlock on Rule and RuleDelayed though. That is not necessary in my opinion. Moreover, the Front End also us Rule and RuleDelayed and modifying their behaviour may interfere with dynamic behaviour. But I suppose my own function isn't perfect either :). $\endgroup$ Commented Jul 12, 2014 at 17:25
  • $\begingroup$ @Jacob The modification is temporary (localized), so it shouldn't cause any problems. I guess you are referring to the situation when the main evaluation is interrupted by a pre-emptive one (dynamic) and the modification is still in effect. Yes, that can be a problem with any similar use of Block as well ... I think that's a general problem with the language. The way you defined replaceWith, it's not going to allow evaluation on the RHS of -> (not :>) while also being self-contained. $\endgroup$
    – Szabolcs
    Commented Jul 12, 2014 at 17:47
  • 1
1
$\begingroup$

You could also do this :

With[{y = x},
  SetDelayed @@ {Unevaluated[draw[x_]], 
    Unevaluated[Cylinder[{{y, 0, 0}, {y + 1, 0, 1}}, 0.1]]}
];
$\endgroup$

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