14
$\begingroup$

If you have a list, e.g.

{1, 2, 3}

then you can extract the $k$th part using Part (list[[k]]):

In[1]:= {1, 2, 3}[[2]]
Out[1]= 2

The problem is that if you provide a symbolic expression in the place of the list, Part will try to decompose it:

In[1]:= list[[2]]
Part::partd: Part specification list[[2]] is longer than depth of object.
Out[1]= list[[2]]

That is, the issue occurs when the overall expression is evaluated before the value of list is known. For example:

In[1]:= list[[2]] /. list -> {1, 2, 3}
Part::partd: Part specification list[[2]] is longer than depth of object.
Out[1]= 2

Although the final output is correct, this produces an annoying spurious error message.

It's even worse if list is a compound expression, because then Part will decompose it, unexpectedly changing its value. For example,

In[1]:= SinCos[x][[1]] /. SinCos -> (θ \[Function] {Sin@θ, Cos@θ})
Out[1]= x

Wat.

Example

I commonly run into this when plotting one dimension of nested functions and data structures.

Suppose I have a function that generates a list of functions according to some parameters:

functions[x_Real, a_, b_, c_] := {Sin@x, Cos@x, Sin[a*x], Cos[b*x], Sin[c*x]};

Now I want to plot the first, third, and fourth functions in the list of functions with parameters $1,2,3$; in this case, $y = \sin(x), \sin(1 \cdot x), \cos(2 \cdot x)$. The obvious way is:

Plot[functions[x, 1, 2, 3][[#]]& /@ {1,3,4} // Evaluate, {x, 0, 5}]

(The Evaluate is needed to get Plot to treat the values as separate functions, so that they will be styled differently.) And Mathematica will dutifully plot... $y = x, 2, 3$:

{Plot[functions[x,1,2,3][[#]]&/@{1,3,4}//Unevaluated,{x,0,5}],
 Plot[functions[x,1,2,3][[#]]&/@{1,3,4}//Evaluate,{x,0,5}]}

Wat

Question

How do I prevent Part[] from trying to decompose symbolic expressions when it is evaluated?

I have a workaround below, but I'm interested in whether there's a better way to do it. Is there a standard built-in function that does what I'm looking for?

$\endgroup$
4
  • 2
    $\begingroup$ is this Q/A related/useful? i.e. does Plot[Evaluate[Hold[functions[x, 1, 2, 3][[#]]] & /@ {1, 3, 4}], {x, 0, 1}] work for what you need to do? $\endgroup$
    – kglr
    Nov 15, 2012 at 12:25
  • $\begingroup$ Have you considered changing the order? For example: (list /. list -> {1, 2, 3})[[2]] $\endgroup$
    – xzczd
    Nov 15, 2012 at 13:07
  • $\begingroup$ BTW, notice that deleting the _Real in functions will lead to an opposite result. $\endgroup$
    – xzczd
    Nov 15, 2012 at 13:13
  • $\begingroup$ I'm sorry if the answer to the following question is obvious to you... Are you aware of the fact that a list {a} is as much a "symbolic" expression as is list? Or do you mean by symbolic that AtomQ@list==True? $\endgroup$
    – sebhofer
    Nov 15, 2012 at 14:04

6 Answers 6

7
$\begingroup$

To outline the problem, if you write Part[f[[x,2]],2] with no rules for f, then part will just extract the contents of the "call" to f. Now for Plot called with evaluate, the expression evaluates fully before x is actually set, but in reality you only want it to partially evaluate and stop whenever Part attempts to extract a part of a call to f. To sketch it out, what you want is something like (f[x,1,2,3]/.x->3)[[2]] What you get is f[x,1,2,3][[2]]/.x->3.

If this behavior is something you want for a given Head of your own construction, it can be done with an UpValue definition switching over to a wrapper for part that only evaluates if the head is not the same as the one you wish to stop on:

 f /: Part[a_f, n_] := delayedPart[a, n, f]
 delayedPart[a_, n_, stopHead_] /; (Head[a] =!= stopHead) := Part[a, n]

So now, f[x, 1, 2, 3][[2]] becomes delayedPart[f[x, 1, 2, 3], 2, f] which evaluates as you want it to when x is given. So the difference from any regular function to this one is then if we add the definition for f:

 f[x_?NumericQ, a_, b_, c_] := {Sin@x, Cos@x, Sin[a*x], Cos[b*x], Sin[c*x]}
 g[VarX, 1, 2, 3][[1]] /. VarX -> 42 (* => 42 *)
 f[VarX, 1, 2, 3][[1]] /. VarX -> 42 (* => Sin[42] *)
$\endgroup$
6
$\begingroup$

Question

How do I prevent Part[] from trying to decompose symbolic expressions when it is evaluated?

Mathematica 10 implements something like your listPart (with additional functionality): Indexed:

Indexed can be used to indicate components of symbolic vectors, matrices, tensors, etc.

When expr is a list, Indexed[expr,i] gives expr[[i]].

When expr is a list, Indexed[expr,{i,j,...}] gives Indexed[expr[[i]],{j,...}].

For example:

expr = Indexed[q["x"], 1];

expr /. q["x"] -> {1, 2, 3}
1

Unfortunately it doesn't seem to allow for the extraction of multiple parts e.g. with Span.

$\endgroup$
1
  • $\begingroup$ In version 9, we can use Compile`GetElement. $\endgroup$
    – chyanog
    Jan 26, 2021 at 8:01
4
$\begingroup$

One way is to define a new function that is the same as Part, except will only be evaluated if the parameter is a List:

ListPart[x_List, spec__] := x[[spec]];

Then, instead of using list[[spec]], call ListPart[list, spec]. For example:

In[2]:= list[[2]]
Part::partd: Part specification list[[2]] is longer than depth of object.
Out[2]= list[[2]]

When list is not literally a List, ListPart does not evaluate...

In[3]:= ListPart[list, 2]
Out[3]= ListPart[list, 2]

... until the actual list value is available

In[4]:= ListPart[list, 2] /. list -> {1, 2, 3}
Out[4]= 2

as desired.

Disadvantage: ListPart[list, 2] is much more verbose than list[[2]].

$\endgroup$
1
$\begingroup$

There seems to be two different issues here.

The first is with Plot: the expectation to use a definition for functions[x_Real, a_, b_, c_] := and then get individually styled lines using Part and Evaluate. This does not seem practical, as you specifically prevent symbolic evaluation with _Real (by the way, you may want _?NumericQ). You could perhaps rewrite your function to allow symbolic evaluation which I believe would solve your other problem as well.

The second issue, and the topic of your question, may best be handled by changing the definition of functions (I'll use f for brevity) such that you always provide a part specification. For example, I'll use the syntax f[args][parts]:

f[x_Real, a_, b_, c_][parts___] :=
  {Sin@x, Cos@x, Sin[a*x], Cos[b*x], Sin[c*x]}[[parts]];

f[0.3, 1, 2, 3][]

f[x, 1, 2, 3][{1, 3, 4}]

f[0.3, 1, 2, 3][{1, 3, 4}]
{0.29552, 0.955336, 0.29552, 0.825336, 0.783327}

f[x, 1, 2, 3][{1, 3, 4}]

{0.29552, 0.29552, 0.825336}

This requires no auxiliary functions or definitions, no overloading of Part, and minimal keystrokes in use. The only inconvenience is having to to add [] to each use of f. You could turn the syntax around to f[parts][args] which, due to the order of evaluations in Mathematica, would allow using f[args] plainly, but you would lose the ability to put Attributes on f that would affect args, so I do not recommend it.

Another approach is a variation of jVincent's method:

f2[x_Real, a_, b_, c_] := {Sin@x, Cos@x, Sin[a*x], Cos[b*x], Sin[c*x]};

setPartHold[name_Symbol] :=
 Module[{part},
  name /: x_name[[spec__]] := part[x, spec];
  part[other : Except[_name], spec__] := other[[spec]]
 ]

Then call setPartHold[f2] once to produce this behavior:

f2[0.3, 1, 2, 3]

f2[x, 1, 2, 3][[{1, 3, 4}]]

f2[0.3, 1, 2, 3][[{1, 3, 4}]]
{0.29552, 0.955336, 0.29552, 0.825336, 0.783327}

part$584[f2[x, 1, 2, 3], {1, 3, 4}]

{0.29552, 0.29552, 0.825336}

Note that the generated part$xxx function is active such that:

part$584[f2[x, 1, 2, 3], {1, 3, 4}] /. x -> 0.3
{0.29552, 0.29552, 0.825336}
$\endgroup$
0
$\begingroup$

Here is an alternate solution I just worked out for a problem I am working on. I have a function that calculates a vector of values. In my problem, this function is rather time-consuming so I do not want to evaluate it more than I absolutely have to. The toy version is

zz = Block[{a, b}, a = 3; b = 4; {a, b}]

Elsewhere in the notebook, I want to use these calculated values in a formula

z[[1]]/z[[2]] /. z -> zz

This correctly returns 3/4, but not before carping that the Part specification is longer than the depth of the object. My solution is to write my first function to return a function that accesses elements of my list

zz = Block[{a, b, xx}, a = 3; b = 4; 
  Function[i, xx[[i]]] /. xx -> {a, b}]

where I have to use a little trickery to get {a,b} to evaluate in the Function. zz is now defined as

Function[i, {3, 4}[[i]]]

Now I can write an formula in terms of the array parts

z[1]/z[2] /. z -> zz

without generating an error from Part

$\endgroup$
0
$\begingroup$

I don't have any points to comment on Daniel W's answer.

But here's how you write this

z[[1]]/z[[2]] /. z -> zz

to not generate any part errors

Indexed[HoldForm[z], 1]/Indexed[HoldForm[z], 2] /. HoldForm[z] -> zz
Out[1]=3/4
$\endgroup$

Your Answer

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

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