4
$\begingroup$

Problem

Say I previously defined a variable (maybe through some extensive calculation)

thing = (a+x)y

Then, I want to use that expression elsewhere. In general, I can't just type "thing". For example, the following doesn't work

thingy[a_,y_] := NIntegrate[ thing ,{x,0,10}]

What I want

I want a way to use variables such that it always behaves like I copied and pasted its contents directly e.g. I want to type:

thingy[a_,y_] := NIntegrate[ Paste[thing] ,{x,0,10}]

and have it behave like:

thingy[a_,y_] := NIntegrate[ (a+x)y ,{x,0,10}]

Not just in this particular example, but in all use cases.

What I am not looking for

Making this use case work.

Yes, I know that I can fix it with:

thingy[a1_,y1_] := NIntegrate[ thing /. {a->a1,y->y1} ,{x,0,10}]

I'd like a built-in function, even if it behaves slightly differently from the way I described, rather than a hard-coded user-defined function (unless it's a very short).

$\endgroup$
7
  • 1
    $\begingroup$ If I'm understanding what you're asking for, the answer is "no". Variables don't really hold any contents. The Set (=) construct creates a rewrite rule in the environment. thing doesn't really have a value, it's not a reference to a memory location, for example. It's just something that the evaluator will replace according to the rewrite rule. Having said that, if the evaluator is running and encounters thing, it will immediately replace it, which is effectively "pasting its contents". However... $\endgroup$
    – lericr
    Commented Nov 2, 2022 at 0:19
  • 1
    $\begingroup$ Just in case this might be what you want, there is an Iconize feature. If you select your (a+x) y expression and bring up the context menu, you can choose to iconize it. There is also the Iconize symbol. You can copy/paste this thing around the notebook. $\endgroup$
    – lericr
    Commented Nov 2, 2022 at 0:21
  • 1
    $\begingroup$ So, for examle, execute Iconize[(a + x) y, "thing"]. Now you can copy/paste that icon into your NIntegrate expression. It'll look like a little gray thing labeled "thing", but it will really be the expression (a+x)y. $\endgroup$
    – lericr
    Commented Nov 2, 2022 at 0:32
  • 1
    $\begingroup$ @user293787 yes I meant with underscores. I'll fix that right now. $\endgroup$
    – ions me
    Commented Nov 2, 2022 at 8:14
  • 1
    $\begingroup$ As the question is a common issue it might be helpful for other people searching for this problem to include the use case in the title. For example "define function using SetDelayed with a global variable as though it was pasted" $\endgroup$ Commented Nov 3, 2022 at 13:24

4 Answers 4

4
$\begingroup$

3 possibilities:

  • InputAutoReplacements

  • Inactive/Activate

  • $PreRead

the last I am less sure is safe as I never used that.


1st possibility : InputAutoReplacements

Edit

You can use InputAliases or InputAutoReplacements. I do not see why one would want to use InputAliases here so we can consider InputAutoReplacements.

The code:

thing = a + b y;
SetOptions[EvaluationNotebook[], 
 InputAutoReplacements -> {"pastedthing" -> ToString@thing}]

pasting_code_by_writing


2nd possibility : Inactive/Activate

In this case one could use (note that I used = instead of :=):

thing = (a + x) y;
thingy[a_, y_] = NIntegrate[thing, {x, 0, 10}] // Inactivate

thingy[4, 8]

(* Inactive[NIntegrate][8 (4 + x), {x, 0, 10}] *)

Notice that the integrand has been evaluated which I feel is more pleasant than using HoldForm.

then

thingy //Activate

(* 720. *)

(* Note: see @att's suggestion to use Activate on the DownValues of thingy *)

Check that the result is the same as using NIntegrate directly:

NIntegrate[8 (4 + x), {x, 0, 10}]

(* 720. *)

If writing Inactive[NIntegrate] each time is tedious you could add nintegrate=Inactive[NIntegrate] to your init.m file (some people do not recommend this as you will likely forget what you put there and could cause issues with some code 3 months from now).


3rd possibility: $PreRead

Another possibility which I have never used before, so it might cause issues, is to define a function for $PreRead to set up an alias between the character "thing" and it's evaluated output :

$PreRead = ReplaceAll[#, "thing" -> ToString[thing]] &

Then you can use

thingy[a_, y_] := NIntegrate[thing, {x, 0, 10}];

thingy[4, 3]

One issue with doing that is that you will not be able to clear the variable using:

Clear[thing]

Because Mathematica will see the evaluated form

Clear[(a + x) y];

Probably also a lot of the functions that have Attribute HoldFirst or HoldAll will not work as expected. As such it might be better to use a different name for the alias than for the expression to remember what we are doing when we type.

$\endgroup$
17
  • 1
    $\begingroup$ Worth noting that = is required because := does not evaluate its right hand side (and thus won't perform variable substitutions). It's probably more convenient to append //Inactivate to the definition instead of wrapping heads present there with Inactive, and this step can be entirely skipped if delayed evaluation of the RHS isn't needed. $\endgroup$
    – att
    Commented Nov 2, 2022 at 4:08
  • 1
    $\begingroup$ You can also DownValues[thingy] = Activate@DownValues[thingy] afterwards to remove the need to Activate the function result. $\endgroup$
    – att
    Commented Nov 2, 2022 at 4:11
  • $\begingroup$ @att I agree that Inactive[NIntegrate] is tedious to write but it allows the integrand to be evaluated without the integral being evaluated. I find that a bit pleasant to see when calling the function without Activate . Also using nintegrate=Inactive[NIntegrate] makes it less tedious to write $\endgroup$ Commented Nov 2, 2022 at 4:41
  • $\begingroup$ Inactivateing the whole expression more closely mirrors the semantics of the original := assignment, but I definitely see the argument in favor of only Inactiveating some heads. $\endgroup$
    – att
    Commented Nov 2, 2022 at 4:50
  • 2
    $\begingroup$ @att maybe you might want to post Activate[Inactivate[definition]] idea i am not sure what you have in mind. I updated my answer to also use $PreRead to make an input alias $\endgroup$ Commented Nov 2, 2022 at 5:04
3
$\begingroup$

I'm keying off of the copy/paste idea that you emphasize. You might be interested in Iconize. Let's say you did that complicated calculation and saved it to thing. We'll let this represent that situation:

thing = (a + x) y

Now you can do either of these:

Iconize[(a + x) y, "thing"]
Iconize[thing, "thing"] (* since Iconize doesn't hold its arguments unevaluated *)

As the output you'll see an icon, like a token. It will be labeled thing, and the actual expression will be hidden. You can expand it to see some meta data. The important thing here is that whatever interactions you do with this icon/token will work pretty much as if you were interacting with the full expression directly. In particular, you can copy/paste this thing and the effect is the same as if you had copy/pasted the entire expression. By giving it the label "thing", it kind of looks like the symbol thing.

$\endgroup$
17
  • $\begingroup$ This does make copying and pasting less horrible looking, but I still have to use copy and paste. I.E the following does not work thing = a + b y ; thing = Iconize[thing,"thing"]; thingy[a_,b_,y_] =thing; $\endgroup$
    – ions me
    Commented Nov 2, 2022 at 23:25
  • $\begingroup$ I didn't want to actually paste things because I can't edit previous code, and then rerun the whole document. Is there a way to use this technique but avoid having to actually copy paste? $\endgroup$
    – ions me
    Commented Nov 2, 2022 at 23:30
  • $\begingroup$ Look at userrandrand's updated answer. I think InputAliases or InputAutoReplacements might be what you want. $\endgroup$
    – lericr
    Commented Nov 3, 2022 at 0:22
  • $\begingroup$ I think you'll need to define them for some context (e.g. current notebook or global or whatever) via the options inspector, so it's not as dynamic as, say, the Iconize idea. In other words, it's a front end thing, not a Wolfram Language thing. Is that what you're after? $\endgroup$
    – lericr
    Commented Nov 3, 2022 at 0:27
  • $\begingroup$ I just want to be able to copy and paste complicated expressions from previous calculations, without it (1) being hardcoded and (2) looking ugly. $\endgroup$
    – ions me
    Commented Nov 3, 2022 at 0:53
3
$\begingroup$

You can Inactivate the definition to resolve variables in it (if they are not the head of an expression), then immediately Activate it to evaluate the definition.

thing = (a + x) y;
Activate@Inactivate[thingy[a_, y_] := NIntegrate[thing, {x, 0, 10}]];
DownValues@thingy

{HoldPattern[thingy[a_, y_]] :> NIntegrate[(a + x) y, {x, 0, 10}]}

You can wrap variables you don't wish to expand in Inactive to prevent unwanted substitutions.

Block[{x = 2, test}, 
 Activate@
  Inactivate[test[b_] := Integrate[x, {x, 0, b}]]; 
 DownValues@test]
Block[{x = 2, test}, 
 Activate@
  Inactivate[test[b_] := Integrate[Inactive@x, {Inactive@x, 0, b}]]; 
 DownValues@test]

{HoldPattern[test[b_]] :> \!\(
\*SubsuperscriptBox[\(\[Integral]\), \(0\), \(b\)]\(2 \
\[DifferentialD]2\)\)}
    (* {HoldPattern[test[b_]] :> Integrate[2, {2, 0, b}]} *)

{HoldPattern[test[b_]] :> \!\(
\*SubsuperscriptBox[\(\[Integral]\), \(0\), \(b\)]\(x \
\[DifferentialD]x\)\)}
    (* {HoldPattern[test[b_]] :> Integrate[x, {x, 0, b}]} *)

In the original expression, since := (SetDelayed) does not evaluate its right hand side, variables present there will not be expanded.

Inactivate[...] works by wrapping all heads in an expression with Inactive, inhibiting those heads' further evaluation. Importantly, the now-inactivated head Inactive[SetDelayed] does not prevent evaluation of its right hand side. This allows evaluation to proceed into the expression, identify that thing evaluates to (a + x) y, and perform the substitution accordingly. This yields an Inactivated definition where variable substitutions have been made.

Activate@
  Inactivate[thingy[a_, y_] := NIntegrate[thing, {x, 0, 10}]] // Trace

(* Mathematica formats Inactive heads in output with TemplateBox.
   Here they are shown in bold italics instead. *)
{{Inactivate[thingy[a_, y_] := NIntegrate[thing, {x, 0, 10}]], 
  thingy[a_, y_] := 
   NIntegrate[thing, {x, 0, 10}], {{thing, (a + x) y}, 
   NIntegrate[(a + x) y, {x, 0, 10}]}, 
  thingy[a_, y_] := NIntegrate[(a + x) y, {x, 0, 10}]}, 
 Activate[thingy[a_, y_] := NIntegrate[(a + x) y, {x, 0, 10}]], 
 thingy[a_, y_] := NIntegrate[(a + x) y, {x, 0, 10}], Null}

Finally, we Activate to remove the Inactives, and the definition evaluates.

$\endgroup$
4
  • $\begingroup$ Thanks, this works but I'm a bit confused about why. $\endgroup$
    – ions me
    Commented Nov 2, 2022 at 23:43
  • $\begingroup$ As in why don't the activate and inactivate just cancel out? $\endgroup$
    – ions me
    Commented Nov 2, 2022 at 23:44
  • $\begingroup$ @IonSme Some evaluation occurs between the application of Inactivate and Activate. Let me know if the edit is helpful. $\endgroup$
    – att
    Commented Nov 3, 2022 at 2:43
  • 1
    $\begingroup$ @IonSme if you want to visualize the order of evaluation in mathematica you can use this answer. It might take some time to learn how to navigate but it gives a nice view of what happened. Using traceview2 from that answer you can use traceView2[ Activate@Inactivate[thingy[a_, y_] := NIntegrate[thing, {x, 0, 10}]]] to see the evaluations in a table that you can interactively open and close. $\endgroup$ Commented Nov 3, 2022 at 13:40
2
$\begingroup$

Maybe this will work for you?

thing[a_, y_, x_] := (a + x) y;
thingy[a_, y_] := NIntegrate[thing[a, y, x], {x, 0, 10}];
thingy[3, 2]
(* 160 *)
$\endgroup$
4
  • $\begingroup$ That would be nice if I had the foresight to define thing as a function. However, oftentimes I am dealing with expressions with many variables, which I do not know a priori if they are going to be changing. Afterwards It's a little cumbersome to go back and redefine "thing" everywhere in my code. $\endgroup$
    – ions me
    Commented Nov 2, 2022 at 23:14
  • $\begingroup$ However, oftentimes I am dealing with expressions with many variables, which I do not know a priori if they are going to be changing. Afterwards It's a little cumbersome to go back and redefine "thing" everywhere in my code. $\endgroup$
    – ions me
    Commented Nov 2, 2022 at 23:16
  • $\begingroup$ Alternatively, I could define thing[] such that it takes as arguments every single variable in the expression, however, that's a bit ugly to have everywhere in my code, especially if it turns out that all of them are constants later. $\endgroup$
    – ions me
    Commented Nov 2, 2022 at 23:18
  • $\begingroup$ @IonSme you could in principle define thing then define auxthing[a_,b_,c_]=thing (= not :=) then thingy[a_,b_,c_]:=NIntegrate[auxthing[a,b,c],{s,0,1}] but that would be a bit long to do. $\endgroup$ Commented Nov 3, 2022 at 2:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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