## How to use local variables with indices in a Mathematica Block environment?

9

I have the following code:

Subscript[f, i_][x_] := Block[{a},
Subscript[a, i] = 3 x;
Subscript[a, 1]
]


If I evaluate for example Subscript[f,1][2], it evaluates to 6. After that asking what Subscript[a,1] is also gives me six. However, I want a to be a local variable.

My question: how can I define a local variable while using indices...?

Note: the original post mentioned that evaluating Subscript[f,1][2] = 6 would lead to the described behaviour, which just isn't true. A reasonable guess is that this was ment to mean that Subscript[f,1][2] was to be evaluated and it would evaluate to 6 (which it does). After that Subscript[a,1] will evaluate to 6 as reported.

Hello Bee, and welcome to math.SE. I think your question is better suited to the mathematica.SE site, so I'm going to migrate it there. There will be a link that appears below the question here that you can follow to the new location of your question. If you need help associating an account on mathematica.SE, you can flag your question for moderator attention, and someone over there will help out. – Zev Chonoles – 2012-04-05T15:29:59.697

I can't seem to reproduce this. Does this behave this way on a fresh Mathematica session? – rcollyer – 2012-04-05T15:39:58.887

@Bee: are you following this and the discussion to one of the answers? I'd suggest you have a look at my edit to your question and see if you think this makes sense. – Albert Retey – 2012-04-05T16:44:44.170

@AlbertRetey Bee is unregistered on [Mathematics.se], and not at all here. So, it is likely that she is not following this. Do as you will, then.

– rcollyer – 2012-04-05T16:51:39.960

8

The problem is that the definition which makes Subscript[a,i] evaluate to 6 is stored in the DownValues of Subscript. The down values of Subscript don't care about the blocking of a and will still exist when Block exits. You can check that by evaluating DownValues[Subscript]. You could use TagSet (shortcut: /:) to store that value in the UpValues of the localized a, which would behave as you expect (i.e. the rule in UpValues of a is removed when Block exits):

ClearAll[f, a, Subscript]
Subscript[f, i_][x_] := Block[{a},
a /: Subscript[a, i] = 3 x;
Subscript[a, 1]
]


on the other hand I wouldn't actually recommend to use such subscripted variables, what you have seen is probably only one of several unexpected things that could happen.

Side Note:It probably should be mentioned that the definition for Subscript[f,i_][x_] is also not associated with f but with Subscript, which can be checked by evaluating SubValues[Subscript]. Unfortunately this can not be changed by using TagSet for f, since Mathematica here will complain about f being too deep in the left hand side expression. One possibility to change that is to reformulate the function definition to e.g.:

f /: Subscript[f, i_] = Function[x,
Block[{a},
a /: Subscript[a, i] = 3 x;
Subscript[a, 1]
]
]


which will behave the same as the original in most practical cases but of course is not exactly the same thing. You can use UpValues[f] to check that this rule is now really associated with f.

Actually, DownValues[Subscript] does not show anything, however SubValues[Subscript] does. – rcollyer – 2012-04-05T16:39:12.400

I think that depends on whether one evaluates Subscript[f,1][2] or Subscript[f,1][2]=6, as discussed elswhere... – Albert Retey – 2012-04-05T16:54:53.673

Ah, I see. Then the answer needs to be clarified slightly, as there are no DownValues of Subscript associated with f (which is what I think you're answer implies) only SubValues, but there DownValues associated with a. – rcollyer – 2012-04-05T17:06:16.530

1@rcollyer: I just added some additional remarks about downvalues, upvalues and subvalues. Hope this is now somewhat clearer. – Albert Retey – 2012-04-06T13:26:52.943

5

I think you want to use Module[] instead of Block[]

Subscript[f, i_][x_] := Module[{a}, Subscript[a, i] = 3 x; Subscript[a, 1]]


Absolutely, but can you get the behavior with Subscript[a,1] that the OP is observing? – rcollyer – 2012-04-05T15:42:45.310

@rcollyer: Yes of course, it's even what I would expect :-). What are you observing and why would you think it should be different from what the OP writes? – Albert Retey – 2012-04-05T15:48:19.983

Yes, if I post the OP's code into a fresh MMA session, I reproduce his issue, Subscript[a,1] returns 6 after an evaluation of Subscript[f,1][2]. However, changing Block[] to Module[], restarting the kernal and running the notebook again yields the desired behavior, namely Subscript[f,1][2] = 6 and Subscript[a,1] = Subscript[a,1] – johntfoster – 2012-04-05T15:48:53.383

@AlbertRetey I get Subscript[a,1] returned on v.8.0.4 with a fresh kernel. – rcollyer – 2012-04-05T15:52:36.843

1@johntfoster: while that in a sense behaves like the OP wants, you would accumulate one rule per call to Subscript[f,_][_] in the downvalues of Subscript (evaluate DownValues[Subscript] to see what I'm talking about). This might not be a problem but could well become one when the function gets called frequently. Also there might be reasons to use Block instead of Module, they serve different purposes... – Albert Retey – 2012-04-05T15:52:52.973

@rcollyer: you are right, I think the problem is that you correctly did evaluate Subscript[f,1][2] = 6, which will set a subvalue for Subscript with argument f, not a downvalue for Subscript with argument a. I didn't copy but retyped and evaluated just Subscript[f,1][2] and that then shows the behaviour that @Bee reported. @Bee: what exactly is it that you want to work? – Albert Retey – 2012-04-05T15:59:23.883

@AlbertRetey retyped everything, and still getting the same behavior. – rcollyer – 2012-04-05T16:05:20.260

@rcollyer: sorry, it probably didn't become clear: the error was with me not with you. If you evaluate Subscript[f,1][2] = 6 as the OP did post, then after that Subscript[a,1] will evaluate to itself, as you observed. If instead you evaluate just Subscript[f,1][2] (not setting it to 6), which is what I did, then after that Subcript[a,1] will evaluate to 6, and this is because there is now an additional rule in the DownValues of Subcript which can be checked by evaluating DownValues[Subscript]. My guess is that the OP actually evaluated what I did and not what he posted... – Albert Retey – 2012-04-05T16:32:05.903

@AlbertRetey thanks. I hadn't noticed the order problem. Likely that's exactly what happened, and the OP did not notice the order issue, either. – rcollyer – 2012-04-05T16:38:32.253

1@rcollyer: I have now edited the question and made a note about that change. – Albert Retey – 2012-04-05T16:43:15.157

4

As Albert Retey says, the issue is that

Subscript[a, i] = 3 x


affects the DownValues of Subscript, not any of the values of a. Subscript is one of the few system-defined symbols where you can modify its values without unprotecting it first (Derivative is another). Thus, in order to localize changes to subscripts of a, you need to use Block on Subscript instead of a. Thus, for your simple use case, you can do:

Subscript[h, i_][x_] := Block[{Subscript},
Subscript[b, i] = 3 x;
Subscript[b, i]]


Now things work as you want:

In[17]:= Subscript[h, 4][3]
Out[17]= 9

In[18]:= Subscript[b, 3]
Out[18]= Subscript[b, 3]


This has the drawback that you lose , because you lose all access to values of Subscript from outside the Block. For a somewhat contrived example of what can go wrong, consider:

Subscript[c, 1] = 17;

Subscript[hh, i_][x_] := Block[{Subscript},
Subscript[b, i] = If[OddQ@Subscript[c, 1], 3 x, 4 x];
Subscript[b, i]]

In[23]:= Subscript[hh, 4][17]
Out[23]= 68

In[24]:= OddQ@Subscript[c, 1]
Out[24]= True


This is perhaps not what you wanted to have happen. Now you can go ahead and manipulate things so that Subscript gets all the DownValues and SubValues from outside the Block, but this is an error-prone and likely inefficient hassle. However, there's an internal function that does exactly what you would want in this case, called InternalInheritedBlock:

Subscript[hhh, i_][x_] := InternalInheritedBlock[{Subscript},
Subscript[b, i] = If[OddQ@Subscript[c, 1], 3 x, 4 x];
Subscript[b, i]]


Now you get the subscripts you want from outside without having to worry about clobbering anything:

In[31]:= Subscript[hhh, 4][17]
Out[31]= 51

In[32]:= Subscript[b, 3]
Out[32]= Subscript[b, 3]