6
$\begingroup$

Given the following:

Clear[a, b, c, d, e, f, g, h];

{a, b, c, d, e, f, g, h} /. {a -> 12, c -> 12, e -> 12, f -> 12}

(* {12, b, 12, d, 12, 12, g, h} *)  

Is there a more elegant or shorter notation to replace multiple symbols with a similar value?

$\endgroup$
1
  • $\begingroup$ These kind of problems are best solved using the Map function. $\endgroup$ Jan 26, 2015 at 9:37

4 Answers 4

12
$\begingroup$

You can have a single rule using Alternatives (|)...

{a, b, c, d, e, f, g, h} /. x : (a | c | e | f) -> 12

Furthermore you can construct the rule on the fly...

{a, b, c, d, e, f, g, h} /. x : Alternatives@@{a, c, e, f} -> 12

As noted by @Kuba below there is no requirement for the pattern to have a name (x) so...

{a, b, c, d, e, f, g, h} /. (a | c | e | f) -> 12
{a, b, c, d, e, f, g, h} /. Alternatives @@ {a, c, e, f} -> 12

also work.

$\endgroup$
4
  • 2
    $\begingroup$ Now that was kind of alternative (no pun intended) I was hoping for. Thanks Ymareth. $\endgroup$
    – MathLind
    Jan 26, 2015 at 9:44
  • 1
    $\begingroup$ There is no need to name those patterns with x:. $\endgroup$
    – Kuba
    Jan 26, 2015 at 9:47
  • $\begingroup$ The second alternative maybe even more useful since you can use a part of the original list. $\endgroup$
    – MathLind
    Jan 26, 2015 at 9:47
  • $\begingroup$ @MathLind But keep the pattern name if you want to use the symbols as arguments, ie. x : (a | c | e | f) :> 12 x. $\endgroup$
    – BoLe
    Jan 26, 2015 at 20:11
5
$\begingroup$

I like using Thread for this kind of rules

{a, b, c, d, e, f, g, h} /. Thread[{a, c, e, f} -> Table[12, {4}]]

As @kguler suggests there is no need having a Table and keeping track of the length of the list also for a single value.

So the following is better!

{a, b, c, d, e, f, g, h} /. Thread[{a, c, e, f} -> 12]

$\endgroup$
4
  • 3
    $\begingroup$ Simpler Thread[{a, c, e, f} -> 12] also works. $\endgroup$
    – kglr
    Jan 26, 2015 at 11:46
  • $\begingroup$ You are right! I see it's because of head h being a List by default on Thread[{a,c,e,f}->11,h] $\endgroup$
    – tchronis
    Jan 26, 2015 at 12:10
  • 1
    $\begingroup$ @MathLind, i think it might be better if tchronis could update -- if he agrees to - his answer adding the alternative syntax for Thread. $\endgroup$
    – kglr
    Jan 26, 2015 at 16:32
  • 1
    $\begingroup$ Added @kugler 's syntax. $\endgroup$
    – tchronis
    Jan 26, 2015 at 16:37
4
$\begingroup$

You asked for "more elegant or shorter" and Ymareth gave you exactly that. However it is worth noting that with long Alternatives this comes at a performance cost. Please observe:

Needs["GeneralUtilities`"]

set = Range[20000];

fn1[x_] := set /. (Alternatives @@ x -> -1)
fn2[x_] := set /. (Dispatch @ Thread[x -> -1])

BenchmarkPlot[{fn1, fn2}, RandomChoice[set, #] &, 2^Range[14]]

enter image description here

If you do not include the time taken by Dispatch to optimize the table the second method has an even larger advantage, which is applicable if the rules will be used more than once.

As a guideline if it is short enough to type using a | b | c | . . . you're fine, but if using Alternatives @@ at least consider Dispatch.


Ymareth wrote:

It would be interesting to know, if at extreme values of n, the gradients are the same; dispatch just has a much lower constant multiplier or dispatch's cost is growing slower regardless of n.

The complexity of Thread and Dispatch seems pretty consistent:

set = List @@ Range[5*^6]; (* unpacked *)

BenchmarkPlot[{Dispatch@Thread[# -> -1] &}, RandomChoice[set, #] &, 2^Range[22], 
 "IncludeFits" -> True, TimeConstraint -> 60]

enter image description here

The complexity of replacement itself however seems to increase with the number of rules:

set = List @@ Range[5*^6];  (* unpacked *)

BenchmarkPlot[{set /. # &}, Dispatch@Thread[# -> -1] &@RandomChoice[set, #] &, 
 2^Range[22], TimeConstraint -> 60]

enter image description here

Perhaps the constant-time floor is the cost to apply a single rule to the five million element list and it takes ~= 10^5 rules before the cost of checking that many rules rises above the floor. I'll try to think of other ways to probe this behavior.

$\endgroup$
7
  • $\begingroup$ +1 for using Dispatch and the BenchmarkPlot tool. It seems that I have to quit the kernel and re-run when removing Dispatch@ from fn2 (Even when I use ClearAll[fn2] before that. I suppose it has something to do with the internals of BenchmarkPlot ? $\endgroup$
    – tchronis
    Jan 27, 2015 at 6:24
  • $\begingroup$ @tchronis Thanks for the vote. BenchmarkPlot caches its results. I believe using slightly different input values will force a fresh run; for example you could use 2^Range[14] - 1. $\endgroup$
    – Mr.Wizard
    Jan 27, 2015 at 6:56
  • $\begingroup$ Thanks. Changing the name of one function also does the trick. Interesting! But is there a way to clear the cache? Anyway probably there are a lot of questions to be asked in only a comment. $\endgroup$
    – tchronis
    Jan 27, 2015 at 7:00
  • 2
    $\begingroup$ @tchronis I like questions or I wouldn't be on this site. ;-) A bit of spelunking suggests that this should do it: Clear[GeneralUtilities`Benchmarking`PackagePrivate`$TimingCaches] $\endgroup$
    – Mr.Wizard
    Jan 27, 2015 at 7:04
  • $\begingroup$ It would be interesting to know, if at extreme values of n, the gradients are the same; dispatch just has a much lower constant multiplier or dispatch's cost is growing slower regardless of n. $\endgroup$
    – Ymareth
    Jan 27, 2015 at 8:51
0
$\begingroup$

MapAt on alternatives pattern:

With[{
  list = {a, b, c, d, e, f, g, h},
  sub = {a, c, e, f}},
 MapAt[f, list,
  Position[list, Alternatives @@ sub]]]

Replace f with 12& for a constant-12 function.

$\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.