2
$\begingroup$

My intention is having a large collection of variables (symbols to be more specific) in a list and substituting all the variables at once with a value using rule instead of going through each one, one at a time. I will explain in more detail bellow if that wasn't clear:

I was trying to substitute the values of a vector/list in mathematica with rules but it didn't work as expected.

So consider a function that takes a list:

fsquared[x_List] := x^2

then consider pre defining (hard coding the list you want to use):

xxx = {xxx1, xxx2}

then consider substituting ALL the values of the list, but instead of doing it one by one, substituting all at once:

fsquared[xxx] /. xxx -> {3, 4}

the intended behaviour/output should be

{9,16}

but mathematica outputs:

{xxx1^2, xxx2^2}

Why is that? I wanted it to substitute each value inside the list with 3 and 4 i.e. the intended behavior is:

fsquared[xxx] /. {xxx1 -> 3, xxx2 -> 4}

the above definitively works, however, when the list of variables I want to substitute gets really long, the above method becomes a little ridiculous.

As in:

fsquared[xxx] /. {xxx1 -> 1, xxx2 -> 2, xxx3 -> 3, xxx4 -> 4, xxx5 -> 5, xxx6 -> 6, xxx7 -> 7}

I will also provide a screen shot to easy readability:

enter image description here

I've tried googling various things like unpacking lists, but so far, I've had no luck searching this. I am assuming something like this must exist, otherwise, it seems rather annoying to have a list of symbols and later decide to change their value and the only want to do it 1 by 1? yikes, that sucks.

I am aware that pattern matching and rule substitution is based on syntactic matching rather than semantic comparison on expressions. But still, is there no way to get this functionality that I am requesting using some built in function?


One caveat that I didn't mention is that I might have nested lists. So something like:

t1 = {a,b}

a = {a1, a2, a3}

b = {b1, b2, b3}

and I'd want:

t1 -> {{1,2,3},{4,5,6}}

or even something a tiny by more complicated like:

t2 = {c,d}

c = {c1, c2, c3}

d = {d1, d2, d3}

then define:

tBig = {t1,t2}

and it would be awesome if the following worked:

tBig -> {{{1,2,3},{4,5,6}},{{7,8,9},{10,11,12}}}

if it would substitute each variable/symbol, one by one.

$\endgroup$
  • 1
    $\begingroup$ Try fsquared[xxx] /. Thread[xxx -> {3, 4}]. When you use fsquared[xxx] /. xxx -> {3, 4} Mathematica tries to replace literal {xxx1, xxx2}, but such thing is not present in your fsquared[xxx] expression. $\endgroup$ – jkuczm Aug 17 '15 at 19:00
  • $\begingroup$ You haven't assigned values to xxx1, or xxx2. If I understand you correctly, what you want is xxx:={xxx1,xxx2}. Then you can do fsquared/@xxx, or add the Listable attribute to fsquared. Because of := anytime you update xxx1, or xxx2 re-evaluating fsquared/@xxx will return the result with the newly defined xxx1, or xxx2. $\endgroup$ – N.J.Evans Aug 17 '15 at 19:02
  • $\begingroup$ @jkuczm yep that seemed to work. After looking up what Thread does that makes sense Thread[xxx -> {3, 4}] = {xxx1 -> 3, xxx2 -> 4}. For your second comment, thanks for clarifying! As I noted in my question, I suspected that mathematica was only doing syntactic replacement, rather than semantic replacement and substituting as I intended it to do. $\endgroup$ – Charlie Parker Aug 17 '15 at 19:48
  • $\begingroup$ @jkuczm there is one small caveat that I didn't tell you about. I might have nested lists. So something like: {{x1, x2},{x3, x4}} -> {{1,2},{3,4}} should also be covered. Let me see what your suggestions does in that (recursive) case... $\endgroup$ – Charlie Parker Aug 17 '15 at 20:18
  • 1
    $\begingroup$ You can Flatten your lists before passing them to Rule: Thread[Flatten@tBig -> Flatten@{{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 11, 12}}}]. You may need to specify a level in second argument of Flatten if you replace symbol with a List. Or to make sure that given nested lists have compatible shapes MapThread Rule over them at appropriate level and then flatten: Flatten@MapThread[ Rule, {tBig, {{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 11, 12}}}}, 3] $\endgroup$ – jkuczm Aug 17 '15 at 21:23
2
$\begingroup$

For the simple case in which you have variable names in flat list and their values in another, you can Thread Rule over those lists:

Thread[{a, b, c} -> {13, 4, 7}]
(* {a -> 13, b -> 4, c -> 7} *)

This gives flat list of rules that can be used in Replace... functions.

It also works for delayed rules:

Thread[{a, b} :> {2+2, 10 - 5}]
(* {a :> 2 + 2, b :> 10 - 5} *)

If you have your variables and values in nested lists you can define your own variants of Rule and RuleDelayed that will thread over all levels and return flattened result.

ClearAll[flatRule]
flatRule[lhs_List, rhs_] := Flatten@MapThread[flatRule, {lhs, rhs}]
flatRule[lhs_, rhs_] := lhs -> rhs

{a, {b, c}, {{d}}}~flatRule~{3, {7, 9}, {{5}}}
(* {a -> 3, b -> 7, c -> 9, d -> 5} *)

You can assign a list to a symbol:

{a, b}~flatRule~{3, {7, 9}}
(* {a -> 3, b -> {7, 9}} *)

If you accidentally use wrong shapes of lists, a warning will be printed:

{a, {b, c}}~flatRule~{2, 5}
(* MapThread::mptd: Object 5 at position {2, 2} in MapThread[flatRule,{{b,c},5}] has only 0 of required 1 dimensions. >> *)
(* {a -> 2, MapThread[flatRule, {{b, c}, 5}]} *)

Usage in replacement:

a + b + c d /. {a, {b, c}, {{d}}}~flatRule~{x1, {x2, x3}, {{x4}}}
(* x1 + x2 + x3 x4 *)

Similar function for delayed rules:

ClearAll[flatRuleDelayed]
SetAttributes[flatRuleDelayed, {HoldRest, SequenceHold}]
flatRuleDelayed[lhs_List, rhs_] := Flatten@MapThread[flatRuleDelayed, Unevaluated@{lhs, rhs}]
flatRuleDelayed[lhs_, rhs_] := lhs :> rhs

{a, {b, c}, {{d}}}~flatRuleDelayed~{3, {2 + 5, 9}, {{Sequence[11, 4]}}}
(* {a :> 3, b :> 2 + 5, c :> 9, d :> Sequence[11, 4]} *)
$\endgroup$
1
$\begingroup$

A couple of observations:

f[x_List] := x^2
f[xxx]
(* f[xxx] *)
xxx = {xxx1, xxx2};
f[xxx]
(* {xxx1^2, xxx2^2} *)
SetAttributes[f, HoldAll]
f[xxx]
(* f[xxx] *)

That's half the problem solved, xxx is held to let us replace it. However:

ReplaceAll[f[xxx], xxx -> {3, 4}]

evaluates to

ReplaceAll[f[xxx], {xxx1, xxx2} -> {3, 4}]

and no match is found. You can instead use

ReplaceAll[f[xxx], HoldPattern[xxx] -> {3, 4}]

The evaluation of f[xxx] is suppressed, as f is designed to act only on lists and the held xxx is not yet a list. Evaluation of xxx in the rule is suppressed by HoldPattern, finally {3, 4} is injected in place of xxx and f can finally evaluate. But now by adding the HoldAll attribute we've broken the evaluation of f[xxx], we won't get a possibly desired {xxx1^2, xxx2^2}.

One alternative approach is not to set HoldAll, but to simply do a Hold/ReleaseHold wrapper:

ReleaseHold@ReplaceAll[Hold[f[xxx]], HoldPattern[xxx] -> {3, 4}]
{9, 16}
$\endgroup$
1
$\begingroup$

The general principal I will illustrate is that code should express intention.

(1) I'm going to suggest another approach. Each approach has its advantages, and due to the complicated way expressions can be evaluated (pattern restrictions, held arguments), each approach probably has drawbacks in certain situations.

The following is straightforward:

fsquared[xxx /. xxx -> {3, 4}]

This gets the order of evaluation right. Of course on such a simple example, it seems a circuitous route to fsquared[{3, 4}]. But with functions with complicated evaluation rules, such as fsquared, it is not clear what the line of code in itself means:

fsquared[xxx] /. xxx -> {3, 4}

It depends on what xxx is at that the time of execution.

(2) A more important problem is the meaning of the code

xxx -> {3, 4}

If the intended meaning is "replace whatever matches the value of xxx (as a pattern) with the list {3, 4}", then there is no reason to complain about the result of

fsquared[xxx] /. xxx -> {3, 4}

when xxx = {xxx1, xxx2}, because fsquared[xxx] evaluates to an expression that contains no instances of {xxx1, xxx2}. But if the intention was to effect the replacements

{xxx1 -> 3, xxx2 -> 4}

then it is a weak representation of the intention. One of the weakness is revealed in the OP's question.

As has been mentioned, Thread[xxx -> {3, 4}] evaluates to the intended replacements {xxx1 -> 3, xxx2 -> 4}. I use it quite a lot in plane graphics, usually in the slightly different form Thread[{x, y} -> point], where point is the list of coordinates of some point.

(3) Another approach, which is not a perfect example of the general principal above, might get closer to what one might intend function fsquared to do. One might be thinking that when fsquared is given a list of numbers, then evaluate; otherwise, wait. Here is one way do that:

ClearAll[fsquared];
fsquared[x_?(VectorQ[#, NumericQ] &)] := x^2

Then fsquared[xxx] /. xxx -> {3, 4} will evaluate to {9, 16}. However, fsquared[{1, 2}] /. {1, 2} -> {3, 4} will fail to alter fsquared[{1, 2}].

(4) One more extra. I forget how useful Trace is on these simple examples. But it's not easy to use, because it can be hard to keep track of all those nested braces. WReach wrote a couple of traceView functions that make it easier to read the results.

We can easily see just what happens in the OP's failed case. I recommend this to anyone who does not understand why something did not evaluate the way they thought it would. It is probably not touted enough. (Beware plotting functions, though: lots of output.)

ClearAll[fsquared];
fsquared[x_List] := x^2;
Block[{xxx},
 xxx = {xxx1, xxx2};
 traceView2[
  fsquared[xxx] /. xxx -> {3, 4}
  ]
 ]

Mathematica graphics

One can see the four major steps: Evaluate the head ReplaceAll (/.), evaluate fsquared[xxx], evaluate the rule xxx -> {3, 4}, and finally apply the rule to the function value. Click on the OpenerView triangles to examine the evaluation of the subexpressions.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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